added s11!

This commit is contained in:
Ayzen
2026-03-26 18:29:42 +03:00
parent 9ddbde22bd
commit 077542cbd0
43 changed files with 713 additions and 347 deletions
+82 -74
View File
@@ -1,4 +1,4 @@
"""Dialog for calibration/reference set selection and sequential capture."""
"""Dialog for preprocess set selection and sequential capture."""
from __future__ import annotations
@@ -6,6 +6,7 @@ from PyQt6.QtCore import pyqtSignal
from PyQt6.QtWidgets import (
QComboBox,
QDialog,
QFormLayout,
QGridLayout,
QGroupBox,
QHBoxLayout,
@@ -15,23 +16,24 @@ from PyQt6.QtWidgets import (
QPushButton,
QVBoxLayout,
)
import pyqtgraph as pg
import numpy as np
import pyqtgraph as pg
from python_app.models.dataset_model import TraceData
from python_app.orchestration.preprocess_assets import (
PREPROCESS_ASSET_KEYS,
PREPROCESS_ASSET_SPECS,
S11_PREPROCESS_ASSET_KEYS,
S21_PREPROCESS_ASSET_KEYS,
preprocess_asset_display_name,
)
class PreprocessDialog(QDialog):
"""Standalone dialog for preprocessing capture workflows.
The dialog combines three concerns:
1. Set selection (calibration/reference).
2. Sequential capture controls for filling all N*M combinations.
3. Quick preview of the last captured trace.
"""
"""Standalone dialog for preprocess set selection and capture workflows."""
refresh_requested = pyqtSignal()
selection_changed = pyqtSignal(str, str)
selection_changed = pyqtSignal()
start_sequence_requested = pyqtSignal(str)
capture_next_requested = pyqtSignal()
abort_sequence_requested = pyqtSignal()
@@ -39,13 +41,14 @@ class PreprocessDialog(QDialog):
def __init__(self, parent=None) -> None:
"""Initialize window metadata and compose dialog UI."""
super().__init__(parent)
self._set_combos: dict[str, QComboBox] = {}
self._init_window()
self._build_ui()
def _init_window(self) -> None:
"""Set static window properties."""
self.setWindowTitle("Preprocessing Setup")
self.resize(1040, 760)
self.resize(1120, 820)
def _build_ui(self) -> None:
"""Build root dialog layout and all sections."""
@@ -57,28 +60,33 @@ class PreprocessDialog(QDialog):
def _build_sets_group(self) -> QGroupBox:
"""Build set-management controls used for preprocessing snapshots."""
group = QGroupBox("Calibration / Reference Sets", self)
layout = QGridLayout(group)
group = QGroupBox("Preprocess Sets", self)
layout = QVBoxLayout(group)
header_row = QHBoxLayout()
self._set_name_input = QLineEdit("set_001", group)
self._calibration_combo = QComboBox(group)
self._reference_combo = QComboBox(group)
refresh_button = QPushButton("Refresh Sets", group)
refresh_button.clicked.connect(self.refresh_requested.emit)
header_row.addWidget(QLabel("Set name"))
header_row.addWidget(self._set_name_input, stretch=1)
header_row.addWidget(refresh_button)
layout.addLayout(header_row)
self._calibration_combo.currentTextChanged.connect(self._emit_selection_changed)
self._reference_combo.currentTextChanged.connect(self._emit_selection_changed)
layout.addWidget(self._build_selector_group("S21", S21_PREPROCESS_ASSET_KEYS, group))
layout.addWidget(self._build_selector_group("S11", S11_PREPROCESS_ASSET_KEYS, group))
return group
layout.addWidget(QLabel("Set name"), 0, 0)
layout.addWidget(self._set_name_input, 0, 1)
layout.addWidget(refresh_button, 0, 2)
def _build_selector_group(self, title: str, keys: tuple[str, ...], parent: QGroupBox) -> QGroupBox:
"""Build one selector subgroup for a channel family."""
group = QGroupBox(title, parent)
form = QFormLayout(group)
form.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow)
layout.addWidget(QLabel("Calibration set"), 1, 0)
layout.addWidget(self._calibration_combo, 1, 1, 1, 2)
layout.addWidget(QLabel("Reference set"), 2, 0)
layout.addWidget(self._reference_combo, 2, 1, 1, 2)
for key in keys:
combo = QComboBox(group)
combo.currentTextChanged.connect(self._emit_selection_changed)
self._set_combos[key] = combo
form.addRow(self._asset_row_label(key), combo)
return group
def _build_sequence_group(self) -> QGroupBox:
@@ -95,8 +103,6 @@ class PreprocessDialog(QDialog):
self._tx_antenna_label_input.setPlaceholderText("e.g. TX_A")
self._rx_antenna_label_input.setPlaceholderText("e.g. RX_B")
button_row = self._build_sequence_button_row(group)
layout.addWidget(QLabel("Active type"), 0, 0)
layout.addWidget(self._active_kind_label, 0, 1)
layout.addWidget(QLabel("Progress"), 1, 0)
@@ -107,22 +113,26 @@ class PreprocessDialog(QDialog):
layout.addWidget(self._tx_antenna_label_input, 3, 1)
layout.addWidget(QLabel("RX antenna label"), 4, 0)
layout.addWidget(self._rx_antenna_label_input, 4, 1)
layout.addLayout(button_row, 5, 0, 1, 2)
layout.addLayout(self._build_sequence_button_grid(group), 5, 0, 1, 2)
layout.addLayout(self._build_sequence_action_row(group), 6, 0, 1, 2)
self._capture_log = QPlainTextEdit(group)
self._capture_log.setReadOnly(True)
self._capture_log.setPlaceholderText("Capture history per combo")
layout.addWidget(self._capture_log, 6, 0, 1, 2)
layout.addWidget(self._capture_log, 7, 0, 1, 2)
return group
def _build_sequence_button_row(self, parent: QGroupBox) -> QHBoxLayout:
"""Build action buttons for sequence flow control."""
start_calibration_button = QPushButton("Start Calibration Sequence", parent)
start_calibration_button.clicked.connect(lambda: self.start_sequence_requested.emit("calibration"))
start_reference_button = QPushButton("Start Reference Sequence", parent)
start_reference_button.clicked.connect(lambda: self.start_sequence_requested.emit("reference"))
def _build_sequence_button_grid(self, parent: QGroupBox) -> QGridLayout:
"""Build per-asset capture start buttons."""
layout = QGridLayout()
for index, key in enumerate(PREPROCESS_ASSET_KEYS):
button = QPushButton(f"Start {preprocess_asset_display_name(key)}", parent)
button.clicked.connect(lambda _checked=False, asset_key=key: self.start_sequence_requested.emit(asset_key))
layout.addWidget(button, index // 2, index % 2)
return layout
def _build_sequence_action_row(self, parent: QGroupBox) -> QHBoxLayout:
"""Build capture/abort row for active sequence control."""
self._capture_next_button = QPushButton("Capture Current Combo", parent)
self._capture_next_button.clicked.connect(self.capture_next_requested.emit)
self._capture_next_button.setEnabled(False)
@@ -131,12 +141,11 @@ class PreprocessDialog(QDialog):
self._abort_button.clicked.connect(self.abort_sequence_requested.emit)
self._abort_button.setEnabled(False)
button_row = QHBoxLayout()
button_row.addWidget(start_calibration_button)
button_row.addWidget(start_reference_button)
button_row.addWidget(self._capture_next_button)
button_row.addWidget(self._abort_button)
return button_row
layout = QHBoxLayout()
layout.addWidget(self._capture_next_button)
layout.addWidget(self._abort_button)
layout.addStretch(1)
return layout
def _build_status_line(self, root_layout: QVBoxLayout) -> None:
"""Build one-line status output for dialog operations."""
@@ -155,13 +164,9 @@ class PreprocessDialog(QDialog):
"""Return requested target set name."""
return self._set_name_input.text().strip()
def calibration_set(self) -> str:
"""Return currently selected calibration set."""
return self._calibration_combo.currentText().strip()
def reference_set(self) -> str:
"""Return currently selected reference set."""
return self._reference_combo.currentText().strip()
def selection_snapshot(self) -> dict[str, str]:
"""Return currently selected set names keyed by preprocess asset key."""
return {key: self._set_combos[key].currentText().strip() for key in PREPROCESS_ASSET_KEYS}
def antenna_labels(self) -> tuple[str, str]:
"""Return optional TX/RX user labels used in capture logs."""
@@ -209,7 +214,8 @@ class PreprocessDialog(QDialog):
self._abort_button.setEnabled(False)
return
self._active_kind_label.setText(kind)
active_label = preprocess_asset_display_name(kind) if kind in PREPROCESS_ASSET_SPECS else kind
self._active_kind_label.setText(active_label)
self._progress_label.setText(f"{captured_count} / {total_count}")
if next_input is None or next_output is None:
self._combo_label.setText("<complete>")
@@ -220,35 +226,32 @@ class PreprocessDialog(QDialog):
self._capture_next_button.setEnabled(True)
self._abort_button.setEnabled(True)
def set_calibration_sets(self, names: list[str]) -> None:
"""Replace calibration set choices while preserving current selection when possible."""
self._set_combo_items(self._calibration_combo, names, self.calibration_set())
def set_available_sets(self, available_sets: dict[str, list[str]]) -> None:
"""Replace combo-box choices for all preprocess assets."""
for key in PREPROCESS_ASSET_KEYS:
combo = self._set_combos[key]
self._set_combo_items(combo, available_sets.get(key, []), combo.currentText().strip())
def set_reference_sets(self, names: list[str]) -> None:
"""Replace reference set choices while preserving current selection when possible."""
self._set_combo_items(self._reference_combo, names, self.reference_set())
def set_selected_sets(self, calibration_set: str, reference_set: str) -> None:
"""Apply selected set names to both comboboxes and emit selection update."""
if calibration_set:
index = self._calibration_combo.findText(calibration_set)
def set_selected_sets(self, selected_sets: dict[str, str]) -> None:
"""Apply selected set names to all comboboxes and emit selection update."""
for key in PREPROCESS_ASSET_KEYS:
selected_value = selected_sets.get(key, "")
if not selected_value:
continue
combo = self._set_combos[key]
index = combo.findText(selected_value)
if index >= 0:
self._calibration_combo.setCurrentIndex(index)
if reference_set:
index = self._reference_combo.findText(reference_set)
if index >= 0:
self._reference_combo.setCurrentIndex(index)
combo.setCurrentIndex(index)
self._emit_selection_changed()
def set_status(self, message: str) -> None:
"""Set short human-readable status line."""
self._status_label.setText(message)
def draw_last_trace(self, trace: TraceData, title: str) -> None:
"""Draw the latest captured sweep trace in dB scale."""
magnitude_db = 20.0 * np.log10(np.maximum(np.abs(trace.s21), 1e-12))
def draw_last_trace(self, trace: TraceData, title: str, *, channel: str) -> None:
"""Draw the latest captured sweep trace for the requested channel in dB scale."""
samples = trace.s11 if channel == "s11" else trace.s21
magnitude_db = 20.0 * np.log10(np.maximum(np.abs(samples), 1e-12))
self._preview_plot.clear()
self._preview_plot.plot(
trace.frequency_hz,
@@ -261,8 +264,13 @@ class PreprocessDialog(QDialog):
)
def _emit_selection_changed(self) -> None:
"""Emit current calibration/reference selection."""
self.selection_changed.emit(self.calibration_set(), self.reference_set())
"""Emit current selection snapshot change."""
self.selection_changed.emit()
@staticmethod
def _asset_row_label(key: str) -> str:
"""Return short selector label for one preprocess asset."""
return preprocess_asset_display_name(key)
@staticmethod
def _set_combo_items(combo: QComboBox, names: list[str], current_text: str) -> None: