Rework radar system
This commit is contained in:
@@ -9,6 +9,8 @@ import logging
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from python_app.file_io import sanitize_path_component
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
MAX_COMBOS = 4096
|
||||
|
||||
@@ -186,14 +188,12 @@ class SwitchModel:
|
||||
|
||||
name: str
|
||||
driver_mode: str = "mock"
|
||||
driver: str = ""
|
||||
radar_port: int = 0
|
||||
positions: int = 1
|
||||
default_position: int = 0
|
||||
gpio_chip: str = ""
|
||||
pin_a: int = -1
|
||||
pin_b: int = -1
|
||||
invert_logic: bool = False
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
@@ -278,23 +278,6 @@ class S21PreprocessModel:
|
||||
object_calibration: PreprocessAssetModel = field(default_factory=PreprocessAssetModel)
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class S11CalibrationModel:
|
||||
"""One-port S11 OSL calibration assets."""
|
||||
|
||||
open: PreprocessAssetModel = field(default_factory=PreprocessAssetModel)
|
||||
short: PreprocessAssetModel = field(default_factory=PreprocessAssetModel)
|
||||
load: PreprocessAssetModel = field(default_factory=PreprocessAssetModel)
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class S11PreprocessModel:
|
||||
"""One-port S11 preprocessing assets."""
|
||||
|
||||
calibration: S11CalibrationModel = field(default_factory=S11CalibrationModel)
|
||||
reference: PreprocessAssetModel = field(default_factory=PreprocessAssetModel)
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class PreprocessNotchModel:
|
||||
"""Optional frequency-domain notch filter applied after calibration and reference subtraction."""
|
||||
@@ -310,7 +293,6 @@ class PreprocessModel:
|
||||
"""Selected preprocessing artifacts for live acquisition."""
|
||||
|
||||
s21: S21PreprocessModel = field(default_factory=S21PreprocessModel)
|
||||
s11: S11PreprocessModel = field(default_factory=S11PreprocessModel)
|
||||
notch: PreprocessNotchModel = field(default_factory=PreprocessNotchModel)
|
||||
|
||||
|
||||
@@ -402,11 +384,6 @@ class RunConfigModel:
|
||||
"""Return whether this config targets synchronized multi-device acquisition."""
|
||||
return self.radar.model == self.LIBREVNA_MULTI_MODEL
|
||||
|
||||
@property
|
||||
def is_matrix_radar(self) -> bool:
|
||||
"""Return whether this config acquires the full virtual switch matrix per sweep."""
|
||||
return self.is_multi_device
|
||||
|
||||
@property
|
||||
def matrix_output_switch_positions(self) -> int:
|
||||
"""Physical positions of the real switch on the master stimulus path."""
|
||||
@@ -449,7 +426,21 @@ class RunConfigModel:
|
||||
"""Return whether this config targets the external Kamil ADC acquisition path."""
|
||||
return self.radar.model == self.KAMIL_ADC_MODEL
|
||||
|
||||
def radar_key_extra_parts(self) -> list[str]:
|
||||
def radar_key(self) -> str:
|
||||
"""Stable hardware/sweep identity used to find saved preprocess sets."""
|
||||
radar, sweep = self.radar, self.radar.sweep
|
||||
serials = [radar.serial or "no_serial"]
|
||||
serials.extend(value.strip() or "no_serial" for value in self._radar_key_extra_parts())
|
||||
serial = "_".join(sanitize_path_component(value) for value in serials)
|
||||
points = "adc" if self.is_kamil_adc else str(int(sweep.points))
|
||||
return (
|
||||
f"{radar.model}_{serial}"
|
||||
f"_st{_format_float_for_key(sweep.start_hz)}_sp{_format_float_for_key(sweep.stop_hz)}"
|
||||
f"_p{points}_if{_format_float_for_key(sweep.if_bandwidth_hz)}"
|
||||
f"_pw{_format_float_for_key(sweep.power_dbm)}"
|
||||
)
|
||||
|
||||
def _radar_key_extra_parts(self) -> list[str]:
|
||||
"""Return model-specific identity parts that affect captured data."""
|
||||
if self.is_multi_device:
|
||||
return list(self.radar.multi_device.slave_serials)
|
||||
@@ -497,7 +488,7 @@ class RunConfigModel:
|
||||
|
||||
def apply_device_model_constraints(self) -> None:
|
||||
"""Apply only required wire-format constraints for the selected device model."""
|
||||
if not self.is_matrix_radar:
|
||||
if not self.is_multi_device:
|
||||
return
|
||||
self._apply_matrix_virtual_switches()
|
||||
self.combos = self.build_runtime_combos()
|
||||
@@ -509,21 +500,19 @@ class RunConfigModel:
|
||||
|
||||
self.output_switch.name = self.output_switch.name or "virtual_output"
|
||||
self.output_switch.driver_mode = "mock"
|
||||
self.output_switch.driver = self.output_switch.driver or "h7992"
|
||||
self.output_switch.radar_port = 1
|
||||
self.output_switch.positions = out_physical * self.matrix_virtual_output_positions
|
||||
self.output_switch.default_position = 0
|
||||
|
||||
self.input_switch.name = self.input_switch.name or "virtual_input"
|
||||
self.input_switch.driver_mode = "mock"
|
||||
self.input_switch.driver = self.input_switch.driver or "h7992"
|
||||
self.input_switch.radar_port = 2
|
||||
self.input_switch.positions = in_physical * self.MULTI_DEVICE_INPUT_POSITIONS
|
||||
self.input_switch.default_position = 0
|
||||
|
||||
def ensure_combos(self) -> None:
|
||||
"""Populate combos with full matrix when no explicit run combos are set."""
|
||||
if self.is_matrix_radar:
|
||||
if self.is_multi_device:
|
||||
self.apply_device_model_constraints()
|
||||
return
|
||||
if self.combos:
|
||||
@@ -531,7 +520,7 @@ class RunConfigModel:
|
||||
self.combos = self.build_full_combos(self.input_switch.positions, self.output_switch.positions)
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, payload: dict[str, Any]) -> RunConfigModel:
|
||||
def from_dict(cls, payload: object) -> RunConfigModel:
|
||||
"""Build model from JSON-like payload using codec layer."""
|
||||
from python_app.models.run_config_codec import run_config_from_dict
|
||||
|
||||
@@ -558,3 +547,11 @@ class RunConfigModel:
|
||||
from python_app.models.run_config_codec import run_config_to_dict
|
||||
|
||||
return run_config_to_dict(self)
|
||||
|
||||
|
||||
def _format_float_for_key(value: float) -> str:
|
||||
"""Format float compactly for deterministic key serialization."""
|
||||
integer = int(round(value))
|
||||
if abs(value - float(integer)) < 1e-6:
|
||||
return str(integer)
|
||||
return f"{value:.6f}".rstrip("0").rstrip(".")
|
||||
|
||||
Reference in New Issue
Block a user