some changes and log fix
This commit is contained in:
@@ -1,4 +1,15 @@
|
||||
"""Neutral preprocessing-set helpers for Kamil ADC acquisition."""
|
||||
"""Neutral preprocessing-set helpers — the "run without calibration" path.
|
||||
|
||||
A neutral pair is a calibration set carrying unit S21 (1+0j) and a reference set
|
||||
carrying zero S21. The C++ through-calibrator divides measured/calibration and the
|
||||
reference is subtracted, so applying both leaves the measured S21 untouched. That
|
||||
lets an operator start the pipeline before any real calibration exists, which the
|
||||
required-asset check in `_start_run` would otherwise refuse.
|
||||
|
||||
Supported models: Kamil ADC (axis from the ADC processing grid) and every
|
||||
VNA-style model, including synchronized multi-device LibreVNA (axis from the
|
||||
configured linear sweep grid).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -17,31 +28,65 @@ from python_app.models.run_config_model import ComboModel, RunConfigModel
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def build_kamil_adc_neutral_s21_sets(
|
||||
def supports_neutral_preprocess_sets(config: RunConfigModel) -> bool:
|
||||
"""Return whether neutral S21 sets can be generated for this radar model.
|
||||
|
||||
Enabled for the Kamil ADC and for synchronized multi-device LibreVNA, the two
|
||||
models whose emitted frequency axis is fully derivable from the config alone.
|
||||
Other models still work through `build_neutral_s21_sets`, but are kept out of the
|
||||
UI shortcut until their axis has been verified against real hardware.
|
||||
"""
|
||||
return bool(config.is_kamil_adc or config.is_multi_device)
|
||||
|
||||
|
||||
def neutral_frequency_grid_hz(config: RunConfigModel) -> np.ndarray:
|
||||
"""Return the exact per-trace frequency axis the configured radar emits.
|
||||
|
||||
Neutral sets must line up sample-for-sample with live sweeps, so the axis comes
|
||||
from the same source the acquisition path uses: the ADC processing grid for Kamil
|
||||
ADC, and the configured linear sweep grid for every VNA-style model (LibreVNA
|
||||
single and multi-device, SN9000, Compact-M). The C++ preprocessor re-checks this
|
||||
axis against the measured one within a tolerance, so a mismatch fails loudly
|
||||
instead of silently corrupting the correction.
|
||||
"""
|
||||
if config.is_kamil_adc:
|
||||
# Single source of truth for the axis: the same grid the processor emits.
|
||||
processor = KamilAdcSweepProcessor(
|
||||
KamilAdcProcessingParams.from_kamil_model(config.radar.kamil_adc)
|
||||
)
|
||||
return processor.grid_hz
|
||||
|
||||
points = int(config.radar.sweep.points)
|
||||
if points < 1:
|
||||
raise ValueError("Neutral sets require radar.sweep.points >= 1")
|
||||
if points == 1:
|
||||
return np.array([float(config.radar.sweep.start_hz)], dtype=np.float32)
|
||||
# Mirrors both acquisition paths: the native collector seeds this same linspace
|
||||
# and the mock backend generates it outright.
|
||||
return np.linspace(
|
||||
float(config.radar.sweep.start_hz),
|
||||
float(config.radar.sweep.stop_hz),
|
||||
points,
|
||||
dtype=np.float32,
|
||||
)
|
||||
|
||||
|
||||
def build_neutral_s21_sets(
|
||||
config: RunConfigModel,
|
||||
) -> tuple[SweepCollection, SweepCollection]:
|
||||
"""Build neutral S21 calibration/reference collections for the Kamil ADC radar.
|
||||
"""Build neutral S21 calibration/reference collections for the active radar.
|
||||
|
||||
The calibration uses unit S21 (1+0j) and the reference uses zero S21 across
|
||||
every configured combo, so applying them in the preprocessing pipeline leaves
|
||||
the input S21 unchanged. The frequency axis is the exact acquisition grid
|
||||
(``radar.kamil_adc.band``), so neutral sets line up sample-for-sample with
|
||||
live sweeps. Returns the ``(calibration, reference)`` collections.
|
||||
Covers every combo in the effective matrix, so a matrix radar widened by real
|
||||
switches gets a neutral pair for all of its positions and the preprocessor's
|
||||
``validate_combos()`` is satisfied. Returns ``(calibration, reference)``.
|
||||
"""
|
||||
if not config.is_kamil_adc:
|
||||
raise ValueError("Neutral Kamil ADC sets require radar.model='kamil_adc'")
|
||||
|
||||
combos = list(config.combos)
|
||||
if not combos:
|
||||
combos = RunConfigModel.build_full_combos(
|
||||
config.input_switch.positions, config.output_switch.positions
|
||||
)
|
||||
combos = config.build_runtime_combos()
|
||||
if not combos:
|
||||
raise ValueError("Kamil ADC neutral sets require at least one switch combo")
|
||||
raise ValueError("Neutral sets require at least one switch combo")
|
||||
|
||||
# Single source of truth for the axis: the same grid the processor emits.
|
||||
processor = KamilAdcSweepProcessor(KamilAdcProcessingParams.from_kamil_model(config.radar.kamil_adc))
|
||||
frequency_hz = processor.grid_hz
|
||||
frequency_hz = neutral_frequency_grid_hz(config)
|
||||
|
||||
now_ns = time.monotonic_ns()
|
||||
calibration = _neutral_collection(
|
||||
@@ -57,11 +102,27 @@ def build_kamil_adc_neutral_s21_sets(
|
||||
monotonic_ns=now_ns,
|
||||
)
|
||||
logger.info(
|
||||
"Built neutral Kamil ADC S21 sets: combos=%d points=%d", len(combos), int(frequency_hz.size)
|
||||
"Built neutral S21 sets: model=%s combos=%d points=%d",
|
||||
config.radar.model,
|
||||
len(combos),
|
||||
int(frequency_hz.size),
|
||||
)
|
||||
return calibration, reference
|
||||
|
||||
|
||||
def build_kamil_adc_neutral_s21_sets(
|
||||
config: RunConfigModel,
|
||||
) -> tuple[SweepCollection, SweepCollection]:
|
||||
"""Build neutral S21 sets, rejecting anything but the Kamil ADC radar.
|
||||
|
||||
Kept as the model-checked entry point for the ADC path; new callers that must
|
||||
work for several radar models should use `build_neutral_s21_sets` instead.
|
||||
"""
|
||||
if not config.is_kamil_adc:
|
||||
raise ValueError("Neutral Kamil ADC sets require radar.model='kamil_adc'")
|
||||
return build_neutral_s21_sets(config)
|
||||
|
||||
|
||||
def _neutral_collection(
|
||||
*,
|
||||
combos: list[ComboModel],
|
||||
|
||||
Reference in New Issue
Block a user