added tec modulation

This commit is contained in:
Ayzen
2026-04-27 17:49:52 +03:00
parent 6b6689fa5f
commit 31fd8ab111
7 changed files with 185 additions and 0 deletions

View File

@ -51,6 +51,8 @@ from laser_control.constants import (
DEFAULT_DS1809_PROFILE_POSITION,
DEFAULT_DS1809_PULSE_MS,
DEFAULT_STM32_DAC_CODE,
DEFAULT_TEC_MODULATION_AMPLITUDE_CODE,
DEFAULT_TEC_MODULATION_FREQUENCY_HZ,
DEFAULT_TEMP1_C,
DEFAULT_TEMP2_C,
DS1809_COUNT_MAX,
@ -64,6 +66,10 @@ from laser_control.constants import (
AD9833_OUTPUT_FREQ_MIN_HZ,
STM32_DAC_CODE_MAX,
STM32_DAC_CODE_MIN,
TEC_MODULATION_AMPLITUDE_CODE_MAX,
TEC_MODULATION_AMPLITUDE_CODE_MIN,
TEC_MODULATION_FREQUENCY_MAX_HZ,
TEC_MODULATION_FREQUENCY_MIN_HZ,
TEMP_MAX_C,
TEMP_MIN_C,
)
@ -151,6 +157,7 @@ def build_device_group(owner) -> QGroupBox:
tabs.addTab(_build_ad9102_tab(owner), "Генератор AD9102")
tabs.addTab(_build_ad9833_tab(owner), "Генератор AD9833")
tabs.addTab(_build_aux_tab(owner), "Выходы и DS1809")
tabs.addTab(_build_tec_modulation_tab(owner), "TEC модуляция")
tabs.addTab(_build_wave_tab(owner), "Своя форма")
layout.addWidget(tabs)
return group
@ -448,6 +455,54 @@ def _build_aux_tab(owner) -> QWidget:
return tab
def _build_tec_modulation_tab(owner) -> QWidget:
tab = QWidget()
layout = QFormLayout(tab)
layout.setHorizontalSpacing(12)
layout.setVerticalSpacing(8)
owner._tec_mod_enable = QCheckBox("Включить модуляцию")
owner._tec_mod_laser = QComboBox()
owner._tec_mod_laser.addItem("Лазер 1", 1)
owner._tec_mod_laser.addItem("Лазер 2", 2)
owner._tec_mod_frequency_hz = _int_spinbox(
TEC_MODULATION_FREQUENCY_MIN_HZ,
TEC_MODULATION_FREQUENCY_MAX_HZ,
DEFAULT_TEC_MODULATION_FREQUENCY_HZ,
suffix=" Гц",
)
owner._tec_mod_frequency_hz.setSingleStep(50)
owner._tec_mod_frequency_hz.setGroupSeparatorShown(True)
owner._tec_mod_amplitude_code = _int_spinbox(
TEC_MODULATION_AMPLITUDE_CODE_MIN,
TEC_MODULATION_AMPLITUDE_CODE_MAX,
DEFAULT_TEC_MODULATION_AMPLITUDE_CODE,
)
owner._tec_mod_amplitude_code.setSingleStep(16)
owner._tec_mod_amplitude_code.setGroupSeparatorShown(True)
owner._apply_tec_modulation_button = _expanding_button("Применить TEC модуляцию", primary=True)
owner._apply_tec_modulation_button.clicked.connect(owner._on_apply_tec_modulation)
layout.addRow(owner._tec_mod_enable)
layout.addRow("Лазер", owner._tec_mod_laser)
layout.addRow(
f"Частота ({TEC_MODULATION_FREQUENCY_MIN_HZ}..{TEC_MODULATION_FREQUENCY_MAX_HZ} Гц)",
owner._tec_mod_frequency_hz,
)
layout.addRow(
f"Амплитуда DAC ({TEC_MODULATION_AMPLITUDE_CODE_MIN}..{TEC_MODULATION_AMPLITUDE_CODE_MAX})",
owner._tec_mod_amplitude_code,
)
layout.addRow(owner._apply_tec_modulation_button)
owner._tec_mod_frequency_hz.setToolTip("Частота синусоидальной добавки к выходу TEC PID.")
owner._tec_mod_amplitude_code.setToolTip(
"Пиковая амплитуда добавки в кодах внешнего TEC DAC. "
"Прошивка ограничивает её по доступному запасу вокруг текущего PID-кода."
)
return tab
def _build_wave_tab(owner) -> QWidget:
tab = QWidget()
layout = QVBoxLayout(tab)

View File

@ -66,6 +66,7 @@ class MainWindow(QMainWindow):
request_apply_ad9833 = pyqtSignal(bool, bool, int)
request_pulse_ds1809 = pyqtSignal(bool, int, int)
request_set_stm32_dac = pyqtSignal(bool, int)
request_apply_tec_modulation = pyqtSignal(bool, int, int, int)
request_upload_wave = pyqtSignal(object)
request_cancel_wave = pyqtSignal()
request_save_profile = pyqtSignal(object)
@ -219,6 +220,7 @@ class MainWindow(QMainWindow):
self.request_apply_ad9833.connect(self._worker.apply_ad9833)
self.request_pulse_ds1809.connect(self._worker.pulse_ds1809)
self.request_set_stm32_dac.connect(self._worker.set_stm32_dac)
self.request_apply_tec_modulation.connect(self._worker.apply_tec_modulation)
self.request_upload_wave.connect(self._worker.upload_ad9102_waveform)
self.request_cancel_wave.connect(self._worker.cancel_ad9102_waveform_upload)
self.request_save_profile.connect(self._worker.save_profile)
@ -320,6 +322,16 @@ class MainWindow(QMainWindow):
)
)
def _on_apply_tec_modulation(self) -> None:
self._dispatch_command(
lambda: self.request_apply_tec_modulation.emit(
self._tec_mod_enable.isChecked(),
int(self._tec_mod_laser.currentData()),
self._tec_mod_frequency_hz.value(),
self._tec_mod_amplitude_code.value(),
)
)
def _on_save_profile(self) -> None:
dialog = ProfileSaveDialog(
custom_waveform_available=self._custom_waveform_is_available(),
@ -408,6 +420,7 @@ class MainWindow(QMainWindow):
self._apply_ad9102_button.setEnabled(connected)
self._apply_ad9833_button.setEnabled(connected)
self._apply_stm32_dac_button.setEnabled(connected)
self._apply_tec_modulation_button.setEnabled(connected)
self._pulse_ds1809_button.setEnabled(connected)
self._upload_wave_button.setEnabled(connected)
self._cancel_wave_button.setEnabled(connected)
@ -724,6 +737,11 @@ class MainWindow(QMainWindow):
f"stm32_dac_enable={1 if self._stm32_dac_enable.isChecked() else 0}",
f"stm32_dac_code={self._stm32_dac_code.value()}",
"",
f"tec_modulation_enable={1 if self._tec_mod_enable.isChecked() else 0}",
f"tec_modulation_laser={int(self._tec_mod_laser.currentData())}",
f"tec_modulation_frequency_hz={self._tec_mod_frequency_hz.value()}",
f"tec_modulation_amplitude_code={self._tec_mod_amplitude_code.value()}",
"",
f"ds1809_apply={'true' if self._ds1809_profile_apply.isChecked() else 'false'}",
f"ds1809_position_from_min={self._ds1809_profile_position.value()}",
]

View File

@ -101,6 +101,22 @@ class ControllerWorker(QObject):
)
)
@pyqtSlot(bool, int, int, int)
def apply_tec_modulation(
self,
enabled: bool,
laser: int,
frequency_hz: int,
amplitude_code: int,
) -> None:
"""Configure fast TEC drive modulation."""
self._run_command(
lambda: (
self._ensure_connected(),
self._apply_tec_modulation_impl(enabled, laser, frequency_hz, amplitude_code),
)
)
@pyqtSlot(object)
def save_profile(self, request: object) -> None:
"""Save the current GUI configuration to the device SD card."""
@ -256,6 +272,26 @@ class ControllerWorker(QObject):
self.log_message.emit("INFO", f"STM32 DAC set to code {dac_code}")
self._emit_status()
def _apply_tec_modulation_impl(
self,
enabled: bool,
laser: int,
frequency_hz: int,
amplitude_code: int,
) -> None:
self._controller.configure_tec_modulation(
enabled=enabled,
laser=laser,
frequency_hz=frequency_hz,
amplitude_code=amplitude_code,
)
state = "enabled" if enabled else "disabled"
self.log_message.emit(
"INFO",
f"TEC modulation {state}: laser={laser}, frequency={frequency_hz} Hz, amplitude={amplitude_code}",
)
self._emit_status()
def _save_profile_impl(self, request: object) -> None:
self._controller.save_profile_to_sd(request)
profile_name = getattr(request, "profile_name", "<unnamed>")