Rework radar system
This commit is contained in:
@@ -2,19 +2,19 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
from contextlib import ExitStack
|
||||
|
||||
import unittest
|
||||
from unittest import mock
|
||||
|
||||
import numpy as np
|
||||
|
||||
from python_app.hardware_full.matrix_radar_service import create_acquisition_service
|
||||
from python_app.hardware_full.matrix_radar_service import create_matrix_radar_service
|
||||
from python_app.hardware_full.point_target_mock_service import PointTargetMockService
|
||||
from python_app.hardware_full.single_radar_service import create_single_radar_service
|
||||
from python_app.models.dataset_model import ComboKey
|
||||
from python_app.models.run_config_model import RunConfigModel
|
||||
from python_app.models.run_config_schema import RunConfigModel
|
||||
from python_app.models.run_config_schema import GprRxGeometryModel, GprTxGeometryModel
|
||||
from python_app.workflows.multi_radar_capture_workflow import MultiRadarSequentialCaptureSession
|
||||
from python_app.workflows.radar_config_variants import RadarConfigVariant
|
||||
from python_app.workflows.sequential_capture_workflow import SequentialCaptureSession
|
||||
|
||||
@@ -41,13 +41,15 @@ def _config() -> RunConfigModel:
|
||||
|
||||
class PointTargetMockTest(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
self.clock = self.enterContext(mock.patch(_CLOCK, return_value=_EPOCH_NS))
|
||||
self.sleep = self.enterContext(mock.patch(_SLEEP))
|
||||
cleanup = ExitStack()
|
||||
self.addCleanup(cleanup.close)
|
||||
self.clock = cleanup.enter_context(mock.patch(_CLOCK, return_value=_EPOCH_NS))
|
||||
self.sleep = cleanup.enter_context(mock.patch(_SLEEP))
|
||||
self.config = _config()
|
||||
self.service = PointTargetMockService(self.config)
|
||||
self.service.configure(self.config.radar.sweep)
|
||||
|
||||
def test_all_channels_follow_bistatic_and_monostatic_distance(self) -> None:
|
||||
def test_all_channels_follow_bistatic_distance(self) -> None:
|
||||
collection = self.service.acquire_collection(collection_id=43)
|
||||
target = np.array([0, .25, 3.75])
|
||||
self.assertEqual(collection.collection_id, 43)
|
||||
@@ -59,7 +61,6 @@ class PointTargetMockTest(unittest.TestCase):
|
||||
r_rx = np.linalg.norm(target - [rx.x_m, rx.y_m, rx.z_m])
|
||||
phase_per_m = -2j * np.pi * trace.frequency_hz.astype(np.float64) / 3e8
|
||||
np.testing.assert_allclose(trace.s21, .5 / (r_tx * r_rx) * np.exp(phase_per_m * (r_tx + r_rx)), rtol=1e-6)
|
||||
np.testing.assert_allclose(trace.s11, .5 / r_tx**2 * np.exp(phase_per_m * 2 * r_tx), rtol=1e-6)
|
||||
self.assertEqual((trace.capture_start_ns, trace.capture_end_ns), (_EPOCH_NS, _EPOCH_NS))
|
||||
self.assertEqual(trace.s21.dtype, np.dtype(np.complex64))
|
||||
self.assertEqual((collection.capture_start_ns, collection.capture_end_ns, collection.monotonic_ns), (_EPOCH_NS,) * 3)
|
||||
@@ -70,14 +71,12 @@ class PointTargetMockTest(unittest.TestCase):
|
||||
traces = self.service.acquire_collection().traces
|
||||
for trace in traces[1:]:
|
||||
np.testing.assert_array_equal(trace.s21, traces[0].s21)
|
||||
np.testing.assert_array_equal(trace.s11, traces[0].s11)
|
||||
|
||||
def test_geometry_changes_receiver_path_without_changing_transmitter_echo(self) -> None:
|
||||
def test_geometry_changes_receiver_path(self) -> None:
|
||||
before = self.service.acquire_collection().traces[0]
|
||||
self.config.gpr.rx_geometry[0].z_m = 1
|
||||
after = self.service.acquire_collection().traces[0]
|
||||
self.assertFalse(np.array_equal(before.s21, after.s21))
|
||||
np.testing.assert_array_equal(before.s11, after.s11)
|
||||
|
||||
def test_motion_uses_elapsed_time_and_returns_after_eight_seconds(self) -> None:
|
||||
tx = {0: (-1., 0., 0.), 1: (1., 0., 0.)}
|
||||
@@ -132,13 +131,16 @@ class PointTargetMockTest(unittest.TestCase):
|
||||
self.assertLess(max(ranges), unambiguous_range_m)
|
||||
|
||||
def test_single_manual_capture_selects_actual_antenna_and_keeps_capture_time(self) -> None:
|
||||
session = SequentialCaptureSession(self.config, "s21_calibration", "mock_geometry")
|
||||
radar = mock.Mock(wraps=self.service)
|
||||
with mock.patch("python_app.workflows.sequential_capture_workflow.create_single_radar_service", return_value=radar):
|
||||
session = SequentialCaptureSession(self.config, "s21_calibration", "mock_geometry")
|
||||
session.open()
|
||||
try:
|
||||
first = session.capture_current_combo()
|
||||
second = session.capture_current_combo()
|
||||
first = session.capture_current_combo().display_trace
|
||||
second = session.capture_current_combo().display_trace
|
||||
finally:
|
||||
session.close()
|
||||
radar.configure.assert_called_once_with(self.config.radar.sweep)
|
||||
self.assertEqual((first.combo, second.combo), (ComboKey(0, 0), ComboKey(1, 0)))
|
||||
self.assertFalse(np.array_equal(first.s21, second.s21))
|
||||
self.assertEqual((second.capture_start_ns, second.capture_end_ns), (_EPOCH_NS, _EPOCH_NS))
|
||||
@@ -147,13 +149,13 @@ class PointTargetMockTest(unittest.TestCase):
|
||||
single = create_single_radar_service(self.config)
|
||||
self.assertIsInstance(single, PointTargetMockService)
|
||||
single.configure(self.config.radar.sweep)
|
||||
self.assertIsInstance(create_acquisition_service(self.config), PointTargetMockService)
|
||||
self.assertIsInstance(create_matrix_radar_service(self.config), PointTargetMockService)
|
||||
self.config.radar.model = RunConfigModel.LIBREVNA_MULTI_MODEL
|
||||
self.config.radar.multi_device.stimulus_switch_positions = 2
|
||||
self.config.radar.multi_device.input_switch_positions = 2
|
||||
self.config.radar.multi_device.output_switch_positions = 2
|
||||
self.config.apply_device_model_constraints()
|
||||
matrix = create_acquisition_service(self.config)
|
||||
matrix = create_matrix_radar_service(self.config)
|
||||
self.assertIsInstance(matrix, PointTargetMockService)
|
||||
matrix.open()
|
||||
matrix.configure(self.config.radar.sweep)
|
||||
@@ -166,15 +168,15 @@ class PointTargetMockTest(unittest.TestCase):
|
||||
def test_multi_variant_single_capture_drains_each_variant_before_sampling(self) -> None:
|
||||
radar = mock.Mock(wraps=self.service)
|
||||
events = []
|
||||
radar.drain_after_switch = mock.Mock(side_effect=lambda: events.append("drain"))
|
||||
radar.prepare_combo.side_effect = lambda combo: (events.append("prepare"), self.service.prepare_combo(combo))
|
||||
radar.acquire.side_effect = lambda: (events.append("acquire"), self.service.acquire())[1]
|
||||
variants = [RadarConfigVariant(Path(name), name, self.config, name) for name in ("a", "b")]
|
||||
variants = [RadarConfigVariant(name, self.config, name) for name in ("a", "b")]
|
||||
with mock.patch(
|
||||
"python_app.workflows.multi_radar_capture_workflow.create_single_radar_service",
|
||||
"python_app.workflows.sequential_capture_workflow.create_single_radar_service",
|
||||
return_value=radar,
|
||||
):
|
||||
session = MultiRadarSequentialCaptureSession(
|
||||
base_config=self.config, kind="s21_calibration", set_name="variants", radar_variants=variants,
|
||||
session = SequentialCaptureSession(
|
||||
config=self.config, kind="s21_calibration", set_name="variants", radar_variants=variants,
|
||||
)
|
||||
session.open()
|
||||
try:
|
||||
@@ -182,14 +184,14 @@ class PointTargetMockTest(unittest.TestCase):
|
||||
batch = session.capture_current_combo()
|
||||
finally:
|
||||
session.close()
|
||||
self.assertEqual(events, ["drain", "acquire"] * 4)
|
||||
self.assertEqual(events, ["prepare", "acquire"] * 4)
|
||||
self.assertEqual([trace.combo for trace in batch.traces], [ComboKey(1, 0)] * 2)
|
||||
for trace in batch.traces:
|
||||
self.assertEqual((trace.capture_start_ns, trace.capture_end_ns), (_EPOCH_NS, _EPOCH_NS))
|
||||
|
||||
def test_invalid_or_unconfigured_mock_fails_clearly(self) -> None:
|
||||
with self.assertRaisesRegex(ValueError, "outside the antenna matrix"):
|
||||
self.service.acquire_combo_collection(input_pos=2, output_pos=0)
|
||||
self.service.acquire_collection(combo=ComboKey(2, 0))
|
||||
with self.assertRaisesRegex(RuntimeError, "not configured"):
|
||||
PointTargetMockService(self.config).acquire_collection()
|
||||
self.config.input_switch.positions = 0
|
||||
|
||||
Reference in New Issue
Block a user