sweep feature added

This commit is contained in:
Ayzen
2026-10-09 19:45:30 +03:00
parent 62e0430668
commit a1386e2588
8 changed files with 241 additions and 6 deletions
+120 -1
View File
@@ -2,6 +2,8 @@
from __future__ import annotations
from collections.abc import Callable
from PyQt6.QtWidgets import (
QCheckBox,
QComboBox,
@@ -52,6 +54,12 @@ from laser_control.constants import (
DEFAULT_DS1809_PROFILE_POSITION,
DEFAULT_DS1809_PULSE_MS,
DEFAULT_STM32_DAC_CODE,
DEFAULT_SWEEP_CURRENT_MAX_MA,
DEFAULT_SWEEP_CURRENT_MIN_MA,
DEFAULT_SWEEP_CURRENT_STEP_MA,
DEFAULT_SWEEP_DAC_STEP_CODE,
DEFAULT_SWEEP_PAUSE_MS,
DEFAULT_SWEEP_STEP_PERIOD_US,
DEFAULT_TEC_MODULATION_AMPLITUDE_CODE,
DEFAULT_TEC_MODULATION_FREQUENCY_HZ,
DEFAULT_TEMP1_C,
@@ -67,6 +75,11 @@ from laser_control.constants import (
AD9833_OUTPUT_FREQ_MIN_HZ,
STM32_DAC_CODE_MAX,
STM32_DAC_CODE_MIN,
SWEEP_CURRENT_STEP_MIN_MA,
SWEEP_PAUSE_MAX_MS,
SWEEP_STEP_PERIOD_MAX_US,
SWEEP_STEP_PERIOD_MIN_US,
SWEEP_TICK_US,
TEC_MODULATION_AMPLITUDE_CODE_MAX,
TEC_MODULATION_AMPLITUDE_CODE_MIN,
TEC_MODULATION_FREQUENCY_MAX_HZ,
@@ -74,6 +87,8 @@ from laser_control.constants import (
TEMP_MAX_C,
TEMP_MIN_C,
)
from laser_control.conversions import current_ma_to_n
from laser_control.models import SweepSettings, SweepTarget
def _double_spinbox(
@@ -112,6 +127,64 @@ def _expanding_button(label: str, *, primary: bool = False) -> QPushButton:
return button
class SweepControls(QGroupBox):
"""Checkbox-enabled firmware staircase sweep settings for one output."""
def __init__(
self,
title: str,
target: SweepTarget,
*,
minimum: QSpinBox | QDoubleSpinBox,
maximum: QSpinBox | QDoubleSpinBox,
step: QSpinBox | QDoubleSpinBox,
to_code: Callable[[float], int],
on_apply: Callable[[SweepSettings], None],
) -> None:
super().__init__(title)
self._target = target
self._to_code = to_code
self._enable = QCheckBox("Включить свипирование")
self._minimum = minimum
self._maximum = maximum
self._step = step
self._step_period_us = _int_spinbox(
SWEEP_STEP_PERIOD_MIN_US,
SWEEP_STEP_PERIOD_MAX_US,
DEFAULT_SWEEP_STEP_PERIOD_US,
suffix=" мкс",
)
self._step_period_us.setSingleStep(SWEEP_TICK_US)
self._step_period_us.setToolTip(f"Разрешение {SWEEP_TICK_US} мкс, значение округляется вниз.")
self._pause_ms = _int_spinbox(0, SWEEP_PAUSE_MAX_MS, DEFAULT_SWEEP_PAUSE_MS, suffix=" мс")
self._pause_ms.setToolTip("Выход удерживается на нижней границе перед каждым новым свипом.")
self.apply_button = _expanding_button("Применить", primary=True)
self.apply_button.clicked.connect(lambda: on_apply(self.settings()))
layout = QFormLayout(self)
layout.setHorizontalSpacing(12)
layout.setVerticalSpacing(8)
layout.addRow(self._enable)
layout.addRow("Нижняя граница", minimum)
layout.addRow("Верхняя граница", maximum)
layout.addRow("Шаг", step)
layout.addRow("Время шага", self._step_period_us)
layout.addRow("Пауза между свипами", self._pause_ms)
layout.addRow(self.apply_button)
def settings(self) -> SweepSettings:
"""Return the current inputs converted to firmware codes."""
return SweepSettings(
target=self._target,
enabled=self._enable.isChecked(),
min_code=self._to_code(self._minimum.value()),
max_code=self._to_code(self._maximum.value()),
step_code=self._to_code(self._step.value()),
step_period_us=self._step_period_us.value(),
pause_ms=self._pause_ms.value(),
)
def build_manual_group(owner) -> QGroupBox:
"""Create manual control inputs."""
group = QGroupBox("Ручной режим")
@@ -159,6 +232,7 @@ def build_device_group(owner) -> QGroupBox:
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_sweep_tab(owner), "Свипирование")
tabs.addTab(_build_wave_tab(owner), "Своя форма")
layout.addWidget(tabs)
return group
@@ -479,7 +553,7 @@ def _build_tec_modulation_tab(owner) -> QWidget:
TEC_MODULATION_AMPLITUDE_CODE_MAX,
DEFAULT_TEC_MODULATION_AMPLITUDE_CODE,
)
owner._tec_mod_amplitude_code.setSingleStep(16)
owner._tec_mod_amplitude_code.setSingleStep(256)
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)
@@ -504,6 +578,51 @@ def _build_tec_modulation_tab(owner) -> QWidget:
return tab
def _build_sweep_tab(owner) -> QWidget:
tab = QWidget()
layout = QHBoxLayout(tab)
layout.setSpacing(10)
owner._laser2_sweep = SweepControls(
"Ток лазера 2",
SweepTarget.LASER2_CURRENT,
minimum=_double_spinbox(
CURRENT_MIN_MA, CURRENT_MAX_MA, DEFAULT_SWEEP_CURRENT_MIN_MA, decimals=3, step=0.05, suffix=" мА"
),
maximum=_double_spinbox(
CURRENT_MIN_MA, CURRENT_MAX_MA, DEFAULT_SWEEP_CURRENT_MAX_MA, decimals=3, step=0.05, suffix=" мА"
),
step=_double_spinbox(
SWEEP_CURRENT_STEP_MIN_MA,
CURRENT_MAX_MA - CURRENT_MIN_MA,
DEFAULT_SWEEP_CURRENT_STEP_MA,
decimals=3,
step=0.01,
suffix=" мА",
),
to_code=current_ma_to_n,
on_apply=owner._on_apply_sweep,
)
owner._laser2_sweep.setToolTip(
"Работает только в рабочем режиме (после «Применить» в ручном режиме); "
"после остановки возвращается ручной ток лазера 2."
)
owner._stm32_dac_sweep = SweepControls(
"STM32 DAC (PA4)",
SweepTarget.STM32_DAC,
minimum=_int_spinbox(STM32_DAC_CODE_MIN, STM32_DAC_CODE_MAX, STM32_DAC_CODE_MIN),
maximum=_int_spinbox(STM32_DAC_CODE_MIN, STM32_DAC_CODE_MAX, STM32_DAC_CODE_MAX),
step=_int_spinbox(1, STM32_DAC_CODE_MAX, DEFAULT_SWEEP_DAC_STEP_CODE),
to_code=int,
on_apply=owner._on_apply_sweep,
)
owner._stm32_dac_sweep.setToolTip("После остановки возвращается ручной уровень выхода STM32 DAC.")
layout.addWidget(owner._laser2_sweep)
layout.addWidget(owner._stm32_dac_sweep)
return tab
def _build_wave_tab(owner) -> QWidget:
tab = QWidget()
layout = QVBoxLayout(tab)