added tec modulation
This commit is contained in:
@ -34,6 +34,7 @@ PyQt6-приложение для управления лазерной плат
|
|||||||
|
|
||||||
- ручной режим: `T1/T2/I1/I2`
|
- ручной режим: `T1/T2/I1/I2`
|
||||||
- live telemetry: `T1/T2`, внешние термисторы, фотодиоды, `3V3/5V1/5V2/7V0`
|
- live telemetry: `T1/T2`, внешние термисторы, фотодиоды, `3V3/5V1/5V2/7V0`
|
||||||
|
- TEC drive modulation: синусоидальная добавка к выходу TEC PID для выбранного лазера
|
||||||
- AD9102: saw/SRAM режимы и загрузка custom waveform
|
- AD9102: saw/SRAM режимы и загрузка custom waveform
|
||||||
- AD9833, DS1809 и STM32 DAC через отдельные firmware-команды
|
- AD9833, DS1809 и STM32 DAC через отдельные firmware-команды
|
||||||
- сохранение профиля на SD-карту устройства
|
- сохранение профиля на SD-карту устройства
|
||||||
|
|||||||
@ -34,6 +34,7 @@ CMD_STM32_DAC_CONTROL = 0xBBBB
|
|||||||
CMD_AD9102_WAVE_CONTROL = 0xCCCC
|
CMD_AD9102_WAVE_CONTROL = 0xCCCC
|
||||||
CMD_AD9102_WAVE_DATA = 0xDDDD
|
CMD_AD9102_WAVE_DATA = 0xDDDD
|
||||||
CMD_PROFILE_SAVE_DATA = 0xEEEE
|
CMD_PROFILE_SAVE_DATA = 0xEEEE
|
||||||
|
CMD_TEC_MODULATION_CONTROL = 0xF0F0
|
||||||
|
|
||||||
# ---- Setup-word bit layout from firmware app_decode_work_packet()
|
# ---- Setup-word bit layout from firmware app_decode_work_packet()
|
||||||
|
|
||||||
@ -102,6 +103,9 @@ DS1809_FLAG_DECREMENT = 0x0002
|
|||||||
|
|
||||||
STM32_DAC_FLAG_ENABLE = 0x0001
|
STM32_DAC_FLAG_ENABLE = 0x0001
|
||||||
|
|
||||||
|
TEC_MODULATION_FLAG_ENABLE = 0x0001
|
||||||
|
TEC_MODULATION_FLAG_CHANNEL_2 = 0x0002
|
||||||
|
|
||||||
AD9102_WAVE_OPCODE_BEGIN = 0x0001
|
AD9102_WAVE_OPCODE_BEGIN = 0x0001
|
||||||
AD9102_WAVE_OPCODE_COMMIT = 0x0002
|
AD9102_WAVE_OPCODE_COMMIT = 0x0002
|
||||||
AD9102_WAVE_OPCODE_CANCEL = 0x0003
|
AD9102_WAVE_OPCODE_CANCEL = 0x0003
|
||||||
@ -184,6 +188,11 @@ DS1809_PROFILE_POSITION_MAX = 63
|
|||||||
STM32_DAC_CODE_MIN = 0
|
STM32_DAC_CODE_MIN = 0
|
||||||
STM32_DAC_CODE_MAX = 4095
|
STM32_DAC_CODE_MAX = 4095
|
||||||
|
|
||||||
|
TEC_MODULATION_FREQUENCY_MIN_HZ = 50
|
||||||
|
TEC_MODULATION_FREQUENCY_MAX_HZ = 2_000
|
||||||
|
TEC_MODULATION_AMPLITUDE_CODE_MIN = 0
|
||||||
|
TEC_MODULATION_AMPLITUDE_CODE_MAX = 4_096
|
||||||
|
|
||||||
# ---- Rail tolerances
|
# ---- Rail tolerances
|
||||||
|
|
||||||
VOLT_3V3_MIN = 3.1
|
VOLT_3V3_MIN = 3.1
|
||||||
@ -218,6 +227,8 @@ DEFAULT_STM32_DAC_VREF = 2.5
|
|||||||
DEFAULT_STM32_DAC_CODE = round(
|
DEFAULT_STM32_DAC_CODE = round(
|
||||||
DEFAULT_STM32_DAC_VOLT / DEFAULT_STM32_DAC_VREF * STM32_DAC_CODE_MAX
|
DEFAULT_STM32_DAC_VOLT / DEFAULT_STM32_DAC_VREF * STM32_DAC_CODE_MAX
|
||||||
)
|
)
|
||||||
|
DEFAULT_TEC_MODULATION_FREQUENCY_HZ = 1_000
|
||||||
|
DEFAULT_TEC_MODULATION_AMPLITUDE_CODE = 256
|
||||||
|
|
||||||
DEFAULT_PI_P = 2560
|
DEFAULT_PI_P = 2560
|
||||||
DEFAULT_PI_I = 128
|
DEFAULT_PI_I = 128
|
||||||
|
|||||||
@ -50,6 +50,10 @@ from .constants import (
|
|||||||
STM32_DAC_CODE_MIN,
|
STM32_DAC_CODE_MIN,
|
||||||
STATUS_RESPONSE_LENGTH,
|
STATUS_RESPONSE_LENGTH,
|
||||||
WAIT_AFTER_SEND_SEC,
|
WAIT_AFTER_SEND_SEC,
|
||||||
|
TEC_MODULATION_AMPLITUDE_CODE_MAX,
|
||||||
|
TEC_MODULATION_AMPLITUDE_CODE_MIN,
|
||||||
|
TEC_MODULATION_FREQUENCY_MAX_HZ,
|
||||||
|
TEC_MODULATION_FREQUENCY_MIN_HZ,
|
||||||
)
|
)
|
||||||
from .exceptions import (
|
from .exceptions import (
|
||||||
CommunicationError,
|
CommunicationError,
|
||||||
@ -429,6 +433,44 @@ class LaserController:
|
|||||||
)
|
)
|
||||||
logger.info("STM32 DAC configured: enabled=%s code=%d", enabled, dac_code)
|
logger.info("STM32 DAC configured: enabled=%s code=%d", enabled, dac_code)
|
||||||
|
|
||||||
|
def configure_tec_modulation(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
enabled: bool,
|
||||||
|
laser: int,
|
||||||
|
frequency_hz: int,
|
||||||
|
amplitude_code: int,
|
||||||
|
) -> None:
|
||||||
|
"""Configure fast zero-mean TEC drive modulation around the PID output."""
|
||||||
|
laser = self._validate_int_range(laser, "laser", 1, 2)
|
||||||
|
frequency_hz = self._validate_int_range(
|
||||||
|
frequency_hz,
|
||||||
|
"frequency_hz",
|
||||||
|
TEC_MODULATION_FREQUENCY_MIN_HZ,
|
||||||
|
TEC_MODULATION_FREQUENCY_MAX_HZ,
|
||||||
|
)
|
||||||
|
amplitude_code = self._validate_int_range(
|
||||||
|
amplitude_code,
|
||||||
|
"amplitude_code",
|
||||||
|
TEC_MODULATION_AMPLITUDE_CODE_MIN,
|
||||||
|
TEC_MODULATION_AMPLITUDE_CODE_MAX,
|
||||||
|
)
|
||||||
|
self._send_and_expect_ok(
|
||||||
|
Protocol.encode_tec_modulation_control(
|
||||||
|
enabled=enabled,
|
||||||
|
laser=laser,
|
||||||
|
frequency_hz=frequency_hz,
|
||||||
|
amplitude_code=amplitude_code,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
logger.info(
|
||||||
|
"TEC modulation configured: enabled=%s laser=%d frequency_hz=%d amplitude_code=%d",
|
||||||
|
enabled,
|
||||||
|
laser,
|
||||||
|
frequency_hz,
|
||||||
|
amplitude_code,
|
||||||
|
)
|
||||||
|
|
||||||
def save_profile_to_sd(self, request: ProfileSaveRequest) -> None:
|
def save_profile_to_sd(self, request: ProfileSaveRequest) -> None:
|
||||||
"""Stream a rendered profile INI and optional waveform CSV to the device SD card."""
|
"""Stream a rendered profile INI and optional waveform CSV to the device SD card."""
|
||||||
if not isinstance(request, ProfileSaveRequest):
|
if not isinstance(request, ProfileSaveRequest):
|
||||||
|
|||||||
@ -51,6 +51,8 @@ from laser_control.constants import (
|
|||||||
DEFAULT_DS1809_PROFILE_POSITION,
|
DEFAULT_DS1809_PROFILE_POSITION,
|
||||||
DEFAULT_DS1809_PULSE_MS,
|
DEFAULT_DS1809_PULSE_MS,
|
||||||
DEFAULT_STM32_DAC_CODE,
|
DEFAULT_STM32_DAC_CODE,
|
||||||
|
DEFAULT_TEC_MODULATION_AMPLITUDE_CODE,
|
||||||
|
DEFAULT_TEC_MODULATION_FREQUENCY_HZ,
|
||||||
DEFAULT_TEMP1_C,
|
DEFAULT_TEMP1_C,
|
||||||
DEFAULT_TEMP2_C,
|
DEFAULT_TEMP2_C,
|
||||||
DS1809_COUNT_MAX,
|
DS1809_COUNT_MAX,
|
||||||
@ -64,6 +66,10 @@ from laser_control.constants import (
|
|||||||
AD9833_OUTPUT_FREQ_MIN_HZ,
|
AD9833_OUTPUT_FREQ_MIN_HZ,
|
||||||
STM32_DAC_CODE_MAX,
|
STM32_DAC_CODE_MAX,
|
||||||
STM32_DAC_CODE_MIN,
|
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_MAX_C,
|
||||||
TEMP_MIN_C,
|
TEMP_MIN_C,
|
||||||
)
|
)
|
||||||
@ -151,6 +157,7 @@ def build_device_group(owner) -> QGroupBox:
|
|||||||
tabs.addTab(_build_ad9102_tab(owner), "Генератор AD9102")
|
tabs.addTab(_build_ad9102_tab(owner), "Генератор AD9102")
|
||||||
tabs.addTab(_build_ad9833_tab(owner), "Генератор AD9833")
|
tabs.addTab(_build_ad9833_tab(owner), "Генератор AD9833")
|
||||||
tabs.addTab(_build_aux_tab(owner), "Выходы и DS1809")
|
tabs.addTab(_build_aux_tab(owner), "Выходы и DS1809")
|
||||||
|
tabs.addTab(_build_tec_modulation_tab(owner), "TEC модуляция")
|
||||||
tabs.addTab(_build_wave_tab(owner), "Своя форма")
|
tabs.addTab(_build_wave_tab(owner), "Своя форма")
|
||||||
layout.addWidget(tabs)
|
layout.addWidget(tabs)
|
||||||
return group
|
return group
|
||||||
@ -448,6 +455,54 @@ def _build_aux_tab(owner) -> QWidget:
|
|||||||
return tab
|
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:
|
def _build_wave_tab(owner) -> QWidget:
|
||||||
tab = QWidget()
|
tab = QWidget()
|
||||||
layout = QVBoxLayout(tab)
|
layout = QVBoxLayout(tab)
|
||||||
|
|||||||
@ -66,6 +66,7 @@ class MainWindow(QMainWindow):
|
|||||||
request_apply_ad9833 = pyqtSignal(bool, bool, int)
|
request_apply_ad9833 = pyqtSignal(bool, bool, int)
|
||||||
request_pulse_ds1809 = pyqtSignal(bool, int, int)
|
request_pulse_ds1809 = pyqtSignal(bool, int, int)
|
||||||
request_set_stm32_dac = pyqtSignal(bool, int)
|
request_set_stm32_dac = pyqtSignal(bool, int)
|
||||||
|
request_apply_tec_modulation = pyqtSignal(bool, int, int, int)
|
||||||
request_upload_wave = pyqtSignal(object)
|
request_upload_wave = pyqtSignal(object)
|
||||||
request_cancel_wave = pyqtSignal()
|
request_cancel_wave = pyqtSignal()
|
||||||
request_save_profile = pyqtSignal(object)
|
request_save_profile = pyqtSignal(object)
|
||||||
@ -219,6 +220,7 @@ class MainWindow(QMainWindow):
|
|||||||
self.request_apply_ad9833.connect(self._worker.apply_ad9833)
|
self.request_apply_ad9833.connect(self._worker.apply_ad9833)
|
||||||
self.request_pulse_ds1809.connect(self._worker.pulse_ds1809)
|
self.request_pulse_ds1809.connect(self._worker.pulse_ds1809)
|
||||||
self.request_set_stm32_dac.connect(self._worker.set_stm32_dac)
|
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_upload_wave.connect(self._worker.upload_ad9102_waveform)
|
||||||
self.request_cancel_wave.connect(self._worker.cancel_ad9102_waveform_upload)
|
self.request_cancel_wave.connect(self._worker.cancel_ad9102_waveform_upload)
|
||||||
self.request_save_profile.connect(self._worker.save_profile)
|
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:
|
def _on_save_profile(self) -> None:
|
||||||
dialog = ProfileSaveDialog(
|
dialog = ProfileSaveDialog(
|
||||||
custom_waveform_available=self._custom_waveform_is_available(),
|
custom_waveform_available=self._custom_waveform_is_available(),
|
||||||
@ -408,6 +420,7 @@ class MainWindow(QMainWindow):
|
|||||||
self._apply_ad9102_button.setEnabled(connected)
|
self._apply_ad9102_button.setEnabled(connected)
|
||||||
self._apply_ad9833_button.setEnabled(connected)
|
self._apply_ad9833_button.setEnabled(connected)
|
||||||
self._apply_stm32_dac_button.setEnabled(connected)
|
self._apply_stm32_dac_button.setEnabled(connected)
|
||||||
|
self._apply_tec_modulation_button.setEnabled(connected)
|
||||||
self._pulse_ds1809_button.setEnabled(connected)
|
self._pulse_ds1809_button.setEnabled(connected)
|
||||||
self._upload_wave_button.setEnabled(connected)
|
self._upload_wave_button.setEnabled(connected)
|
||||||
self._cancel_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_enable={1 if self._stm32_dac_enable.isChecked() else 0}",
|
||||||
f"stm32_dac_code={self._stm32_dac_code.value()}",
|
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_apply={'true' if self._ds1809_profile_apply.isChecked() else 'false'}",
|
||||||
f"ds1809_position_from_min={self._ds1809_profile_position.value()}",
|
f"ds1809_position_from_min={self._ds1809_profile_position.value()}",
|
||||||
]
|
]
|
||||||
|
|||||||
@ -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)
|
@pyqtSlot(object)
|
||||||
def save_profile(self, request: object) -> None:
|
def save_profile(self, request: object) -> None:
|
||||||
"""Save the current GUI configuration to the device SD card."""
|
"""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.log_message.emit("INFO", f"STM32 DAC set to code {dac_code}")
|
||||||
self._emit_status()
|
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:
|
def _save_profile_impl(self, request: object) -> None:
|
||||||
self._controller.save_profile_to_sd(request)
|
self._controller.save_profile_to_sd(request)
|
||||||
profile_name = getattr(request, "profile_name", "<unnamed>")
|
profile_name = getattr(request, "profile_name", "<unnamed>")
|
||||||
|
|||||||
@ -29,6 +29,7 @@ from .constants import (
|
|||||||
CMD_DS1809_CONTROL,
|
CMD_DS1809_CONTROL,
|
||||||
CMD_STATE,
|
CMD_STATE,
|
||||||
CMD_STM32_DAC_CONTROL,
|
CMD_STM32_DAC_CONTROL,
|
||||||
|
CMD_TEC_MODULATION_CONTROL,
|
||||||
CMD_TRANS_ENABLE,
|
CMD_TRANS_ENABLE,
|
||||||
DEFAULT_SETUP_WORD,
|
DEFAULT_SETUP_WORD,
|
||||||
DS1809_FLAG_DECREMENT,
|
DS1809_FLAG_DECREMENT,
|
||||||
@ -46,6 +47,8 @@ from .constants import (
|
|||||||
SEND_PARAMS_TOTAL_LENGTH,
|
SEND_PARAMS_TOTAL_LENGTH,
|
||||||
SHORT_CONTROL_TOTAL_LENGTH,
|
SHORT_CONTROL_TOTAL_LENGTH,
|
||||||
STM32_DAC_FLAG_ENABLE,
|
STM32_DAC_FLAG_ENABLE,
|
||||||
|
TEC_MODULATION_FLAG_CHANNEL_2,
|
||||||
|
TEC_MODULATION_FLAG_ENABLE,
|
||||||
STATUS_DESCRIPTIONS,
|
STATUS_DESCRIPTIONS,
|
||||||
STATUS_RESPONSE_LENGTH,
|
STATUS_RESPONSE_LENGTH,
|
||||||
WAVE_DATA_TOTAL_LENGTH,
|
WAVE_DATA_TOTAL_LENGTH,
|
||||||
@ -270,6 +273,25 @@ class Protocol:
|
|||||||
0,
|
0,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def encode_tec_modulation_control(
|
||||||
|
*,
|
||||||
|
enabled: bool,
|
||||||
|
laser: int,
|
||||||
|
frequency_hz: int,
|
||||||
|
amplitude_code: int,
|
||||||
|
) -> bytes:
|
||||||
|
"""Build a TEC drive-modulation control packet."""
|
||||||
|
flags = TEC_MODULATION_FLAG_ENABLE if enabled else 0
|
||||||
|
if laser == 2:
|
||||||
|
flags |= TEC_MODULATION_FLAG_CHANNEL_2
|
||||||
|
return Protocol._encode_short_control(
|
||||||
|
CMD_TEC_MODULATION_CONTROL,
|
||||||
|
flags,
|
||||||
|
_ensure_uint(frequency_hz, "frequency_hz", 0, 0xFFFF),
|
||||||
|
_ensure_uint(amplitude_code, "amplitude_code", 0, 0xFFFF),
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def encode_ad9102_wave_begin(sample_count: int) -> bytes:
|
def encode_ad9102_wave_begin(sample_count: int) -> bytes:
|
||||||
"""Build an AD9102 custom-wave upload BEGIN packet."""
|
"""Build an AD9102 custom-wave upload BEGIN packet."""
|
||||||
|
|||||||
Reference in New Issue
Block a user