Rework radar system

This commit is contained in:
Ayzen
2026-10-06 12:32:26 +03:00
parent 552cdc688a
commit 5bb86b7165
299 changed files with 13537 additions and 27763 deletions
+25 -16
View File
@@ -2,6 +2,8 @@
from __future__ import annotations
from contextlib import ExitStack
from PyQt6.QtCore import QSignalBlocker, pyqtSignal
from PyQt6.QtWidgets import (
QCheckBox,
@@ -24,12 +26,11 @@ from PyQt6.QtWidgets import (
import numpy as np
import pyqtgraph as pg
from python_app.models.gui_profile_schema import GuiPreprocessDialogStateModel
from python_app.models.dataset_model import TraceData
from python_app.orchestration.preprocess_assets import (
CAPTURABLE_PREPROCESS_ASSET_KEYS,
PREPROCESS_ASSET_SPECS,
PREPROCESS_ASSET_NAMES,
REQUIRED_PREPROCESS_ASSET_KEYS,
VISIBLE_PREPROCESS_ASSET_KEYS,
preprocess_asset_display_name,
)
@@ -49,7 +50,6 @@ class PreprocessDialog(QDialog):
finalize_sequence_requested = pyqtSignal()
abort_sequence_requested = pyqtSignal()
create_neutral_sets_requested = pyqtSignal()
import_object_calibration_requested = pyqtSignal()
create_object_calibration_requested = pyqtSignal()
def __init__(self, parent=None) -> None:
@@ -113,7 +113,7 @@ class PreprocessDialog(QDialog):
layout.addLayout(self._build_median_sweep_row(group))
layout.addWidget(self._build_radar_config_group(group))
layout.addWidget(self._build_selector_group("S21", VISIBLE_PREPROCESS_ASSET_KEYS, group))
layout.addWidget(self._build_selector_group("S21", tuple(PREPROCESS_ASSET_NAMES), group))
return group
def _build_median_sweep_row(self, parent: QGroupBox) -> QHBoxLayout:
@@ -179,13 +179,9 @@ class PreprocessDialog(QDialog):
row = QHBoxLayout(field)
row.setContentsMargins(0, 0, 0, 0)
row.addWidget(combo, stretch=1)
self._import_object_calibration_button = QPushButton("Import…", field)
self._import_object_calibration_button.setToolTip("Import an existing cal_*.npy MIMO calibration folder.")
self._import_object_calibration_button.clicked.connect(self.import_object_calibration_requested.emit)
self._create_object_calibration_button = QPushButton("Create…", field)
self._create_object_calibration_button.setToolTip("Calculate a MIMO calibration from reflector measurements.")
self._create_object_calibration_button.clicked.connect(self.create_object_calibration_requested.emit)
row.addWidget(self._import_object_calibration_button)
row.addWidget(self._create_object_calibration_button)
form.addRow(self._asset_row_label(key), field)
return group
@@ -218,7 +214,7 @@ class PreprocessDialog(QDialog):
def _build_sequence_button_grid(self, parent: QGroupBox) -> QGridLayout:
"""Build per-asset capture start buttons."""
layout = QGridLayout()
for index, key in enumerate(CAPTURABLE_PREPROCESS_ASSET_KEYS):
for index, key in enumerate(REQUIRED_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)
@@ -285,6 +281,19 @@ class PreprocessDialog(QDialog):
"""Return requested target set name."""
return self._set_name_input.text().strip()
def apply_settings(self, state: GuiPreprocessDialogStateModel, selected: dict[str, str], *, neutral_visible: bool) -> None:
"""Apply profile values atomically, without emitting selection/edit signals."""
with ExitStack() as blockers:
for widget in (self._set_name_input, self._radar_config_dir_input,
self._use_all_radar_configs_checkbox, *self._set_combos.values()):
blockers.enter_context(QSignalBlocker(widget))
self.set_set_name(state.set_name)
self.set_radar_config_dir(state.radar_config_dir)
self.set_use_all_radar_configs(state.use_all_radar_configs)
self.set_median_sweep_count(state.median_sweep_count)
self.set_selected_sets(selected, emit_signal=False)
self.set_neutral_sets_visible(neutral_visible)
def set_set_name(self, value: str) -> None:
"""Replace requested target set name."""
self._set_name_input.setText(value)
@@ -335,7 +344,7 @@ class PreprocessDialog(QDialog):
def selection_snapshot(self) -> dict[str, str]:
"""Return currently selected set names keyed by preprocess asset key."""
return {key: self._set_combos[key].currentData() or "" for key in VISIBLE_PREPROCESS_ASSET_KEYS}
return {key: self._set_combos[key].currentData() or "" for key in PREPROCESS_ASSET_NAMES}
def clear_capture_log(self) -> None:
"""Clear capture history text box."""
@@ -394,7 +403,7 @@ class PreprocessDialog(QDialog):
return
actions_enabled = bool(actions_enabled)
active_label = preprocess_asset_display_name(kind) if kind in PREPROCESS_ASSET_SPECS else kind
active_label = preprocess_asset_display_name(kind) if kind in PREPROCESS_ASSET_NAMES else kind
self._active_kind_label.setText(active_label)
self._progress_label.setText(f"{captured_count} / {total_count}")
self._undo_last_button.setEnabled(bool(can_undo) and actions_enabled)
@@ -414,14 +423,14 @@ class PreprocessDialog(QDialog):
def set_available_sets(self, available_sets: dict[str, list[str]]) -> None:
"""Replace combo-box choices for all preprocess assets."""
for key in VISIBLE_PREPROCESS_ASSET_KEYS:
for key in PREPROCESS_ASSET_NAMES:
combo = self._set_combos[key]
with QSignalBlocker(combo):
self._set_combo_items(combo, available_sets.get(key, []), combo.currentData() or "")
def set_selected_sets(self, selected_sets: dict[str, str], *, emit_signal: bool = True) -> None:
"""Apply selected set names to all comboboxes and optionally emit update."""
for key in VISIBLE_PREPROCESS_ASSET_KEYS:
for key in PREPROCESS_ASSET_NAMES:
selected_value = selected_sets.get(key, "")
combo = self._set_combos[key]
with QSignalBlocker(combo):
@@ -451,9 +460,9 @@ class PreprocessDialog(QDialog):
if self._preview_placeholder is not None:
self._preview_placeholder.setText("Preview will appear after the first successful capture.")
def draw_last_trace(self, trace: TraceData, title: str, *, channel: str) -> None:
def draw_last_trace(self, trace: TraceData, title: str) -> None:
"""Draw amplitude (dB) and wrapped phase (deg, ±180) panes for the latest capture."""
samples = trace.s11 if channel == "s11" else trace.s21
samples = trace.s21
magnitude_db = 20.0 * np.log10(np.maximum(np.abs(samples), 1e-12))
phase_deg = np.degrees(np.angle(samples))