new kamil adc

This commit is contained in:
Ayzen
2026-06-11 13:02:02 +03:00
parent 21f76d7cd2
commit 9661504e51
46 changed files with 12097 additions and 1063 deletions
@@ -10,35 +10,26 @@ from python_app.models.run_config_model import RunConfigModel
from python_app.workflows.kamil_adc_neutral_preprocess import build_kamil_adc_neutral_s21_sets
def _kamil_config(*, points: int = 5) -> RunConfigModel:
return RunConfigModel.from_dict(
{
"radar": {
"model": "kamil_adc",
"kamil_adc": {
"band": {"start_hz": 2_100_000_000.0, "stop_hz": 5_500_000_000.0, "points": points},
},
},
"switches": {"port1": {"positions": 1}, "port2": {"positions": 2}},
"run": {"combos": [{"input": 0, "output": 0}, {"input": 1, "output": 0}]},
}
)
class KamilAdcNeutralPreprocessTest(unittest.TestCase):
def test_builds_passthrough_s21_sets_for_current_sweep(self) -> None:
config = RunConfigModel.from_dict(
{
"radar": {
"model": "kamil_adc",
"sweep": {
"start_hz": 1_000_000.0,
"stop_hz": 4_000_000.0,
"if_bandwidth_hz": 1.0,
"stimulus_power_dbm": -10.0,
},
},
"switches": {
"port1": {"positions": 1},
"port2": {"positions": 2},
},
"run": {
"combos": [
{"input": 0, "output": 0},
{"input": 1, "output": 0},
],
},
}
)
def test_builds_passthrough_s21_sets_on_band_grid(self) -> None:
calibration, reference = build_kamil_adc_neutral_s21_sets(_kamil_config(points=5))
calibration, reference = build_kamil_adc_neutral_s21_sets(config, point_count=4)
expected_frequency = np.linspace(1_000_000.0, 4_000_000.0, 4, dtype=np.float32)
expected_frequency = np.linspace(2_100_000_000.0, 5_500_000_000.0, 5).astype(np.float32)
self.assertEqual(len(calibration.traces), 2)
self.assertEqual(len(reference.traces), 2)
self.assertEqual(
@@ -47,47 +38,35 @@ class KamilAdcNeutralPreprocessTest(unittest.TestCase):
)
for trace in calibration.traces:
np.testing.assert_array_equal(trace.frequency_hz, expected_frequency)
np.testing.assert_array_equal(trace.s11, np.zeros(4, dtype=np.complex64))
np.testing.assert_array_equal(trace.s21, np.ones(4, dtype=np.complex64))
np.testing.assert_array_equal(trace.s11, np.zeros(5, dtype=np.complex64))
np.testing.assert_array_equal(trace.s21, np.ones(5, dtype=np.complex64))
for trace in reference.traces:
np.testing.assert_array_equal(trace.frequency_hz, expected_frequency)
np.testing.assert_array_equal(trace.s11, np.zeros(4, dtype=np.complex64))
np.testing.assert_array_equal(trace.s21, np.zeros(4, dtype=np.complex64))
np.testing.assert_array_equal(trace.s21, np.zeros(5, dtype=np.complex64))
def test_grid_matches_processor_grid(self) -> None:
"""The neutral axis must be byte-identical to the acquisition grid."""
from python_app.hardware_full.kamil_adc import (
KamilAdcProcessingParams,
KamilAdcSweepProcessor,
)
config = _kamil_config(points=17)
processor = KamilAdcSweepProcessor(KamilAdcProcessingParams.from_kamil_model(config.radar.kamil_adc))
calibration, _reference = build_kamil_adc_neutral_s21_sets(config)
np.testing.assert_array_equal(calibration.traces[0].frequency_hz, processor.grid_hz)
def test_rejects_non_kamil_config(self) -> None:
config = RunConfigModel.from_dict({"radar": {"model": "librevna"}})
with self.assertRaisesRegex(ValueError, "kamil_adc"):
build_kamil_adc_neutral_s21_sets(config, point_count=4)
build_kamil_adc_neutral_s21_sets(config)
@staticmethod
def _kamil_config() -> RunConfigModel:
return RunConfigModel.from_dict(
{
"radar": {
"model": "kamil_adc",
"sweep": {"start_hz": 1_000_000.0, "stop_hz": 4_000_000.0,
"if_bandwidth_hz": 1.0, "stimulus_power_dbm": -10.0},
},
"switches": {"port1": {"positions": 1}, "port2": {"positions": 2}},
"run": {"combos": [{"input": 0, "output": 0}, {"input": 1, "output": 0}]},
}
)
def test_point_count_zero_or_negative_raises(self) -> None:
config = self._kamil_config()
for bad in (0, -1):
with self.subTest(point_count=bad), self.assertRaisesRegex(ValueError, "point count"):
build_kamil_adc_neutral_s21_sets(config, point_count=bad)
def test_single_point_sweep(self) -> None:
calibration, _reference = build_kamil_adc_neutral_s21_sets(self._kamil_config(), point_count=1)
for trace in calibration.traces:
self.assertEqual(trace.frequency_hz.tolist(), [1_000_000.0])
self.assertEqual(trace.s21.shape, (1,))
def test_rejects_degenerate_band(self) -> None:
with self.assertRaisesRegex(ValueError, "points must be >= 2"):
build_kamil_adc_neutral_s21_sets(_kamil_config(points=1))
def test_dtypes_are_float32_and_complex64(self) -> None:
calibration, _reference = build_kamil_adc_neutral_s21_sets(self._kamil_config(), point_count=4)
calibration, _reference = build_kamil_adc_neutral_s21_sets(_kamil_config())
trace = calibration.traces[0]
self.assertEqual(trace.frequency_hz.dtype, np.float32)
self.assertEqual(trace.s21.dtype, np.complex64)
@@ -96,7 +75,7 @@ class KamilAdcNeutralPreprocessTest(unittest.TestCase):
def test_calibration_s21_is_a_nonzero_divisor(self) -> None:
# The C++ through-calibrator divides measured/calibration, so calibration S21
# must never be zero — that is the whole point of the '1+0j neutral' contract.
calibration, _reference = build_kamil_adc_neutral_s21_sets(self._kamil_config(), point_count=4)
calibration, _reference = build_kamil_adc_neutral_s21_sets(_kamil_config())
for trace in calibration.traces:
self.assertTrue(bool(np.all(trace.s21 != 0)))