fix(v7): store WaveformConfig on self; add set_waveform parity; fix magic 32
- Move WaveformConfig() from per-frame local in _run_host_dsp to
self._waveform in __init__, mirroring ReplayWorker pattern.
- Add set_waveform() to RadarDataWorker for injection symmetry with
ReplayWorker.set_waveform() — live path is now configurable.
- Replace hardcoded fallback 32 with self._waveform.n_doppler_bins.
- Update AST contract tests: WaveformConfig() check moves to __init__
parse; attribute chains updated from ("wf", ...) to
("self", "_waveform", ...) to match renamed accessor.
This commit is contained in:
@@ -672,16 +672,19 @@ class TestLiveReplayPhysicalUnitsParity(unittest.TestCase):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def test_live_path_uses_waveform_config(self):
|
def test_live_path_uses_waveform_config(self):
|
||||||
"""_run_host_dsp must call WaveformConfig() and read
|
"""RadarDataWorker.__init__ must instantiate WaveformConfig() into
|
||||||
wf.range_resolution_m / wf.velocity_resolution_mps — not
|
self._waveform; _run_host_dsp must read self._waveform.range_resolution_m
|
||||||
self._settings.range_resolution / velocity_resolution."""
|
/ velocity_resolution_mps — not self._settings equivalents."""
|
||||||
|
init = self._parse_method("RadarDataWorker", "__init__")
|
||||||
|
self.assertTrue(self._has_call_to(init, "WaveformConfig"),
|
||||||
|
"RadarDataWorker.__init__ must instantiate WaveformConfig() into self._waveform.")
|
||||||
method = self._parse_method("RadarDataWorker", "_run_host_dsp")
|
method = self._parse_method("RadarDataWorker", "_run_host_dsp")
|
||||||
self.assertTrue(self._has_call_to(method, "WaveformConfig"),
|
self.assertTrue(
|
||||||
"Live path must instantiate WaveformConfig().")
|
self._has_attribute_chain(method, ("self", "_waveform", "range_resolution_m")),
|
||||||
self.assertTrue(self._has_attribute_chain(method, ("wf", "range_resolution_m")),
|
"Live path must read self._waveform.range_resolution_m.")
|
||||||
"Live path must read wf.range_resolution_m.")
|
self.assertTrue(
|
||||||
self.assertTrue(self._has_attribute_chain(method, ("wf", "velocity_resolution_mps")),
|
self._has_attribute_chain(method, ("self", "_waveform", "velocity_resolution_mps")),
|
||||||
"Live path must read wf.velocity_resolution_mps. "
|
"Live path must read self._waveform.velocity_resolution_mps. "
|
||||||
"RadarSettings.velocity_resolution default 1.0 caused ~5.34x "
|
"RadarSettings.velocity_resolution default 1.0 caused ~5.34x "
|
||||||
"underreport vs replay (test_v7.py:449 pins ~5.343).")
|
"underreport vs replay (test_v7.py:449 pins ~5.343).")
|
||||||
self.assertFalse(self._has_attribute_chain(
|
self.assertFalse(self._has_attribute_chain(
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ class RadarDataWorker(QThread):
|
|||||||
self._recorder = recorder
|
self._recorder = recorder
|
||||||
self._gps = gps_data_ref
|
self._gps = gps_data_ref
|
||||||
self._settings = settings or RadarSettings()
|
self._settings = settings or RadarSettings()
|
||||||
|
self._waveform = WaveformConfig()
|
||||||
self._running = False
|
self._running = False
|
||||||
|
|
||||||
# Frame queue for production RadarAcquisition → this thread
|
# Frame queue for production RadarAcquisition → this thread
|
||||||
@@ -97,6 +98,9 @@ class RadarDataWorker(QThread):
|
|||||||
self._byte_count = 0
|
self._byte_count = 0
|
||||||
self._error_count = 0
|
self._error_count = 0
|
||||||
|
|
||||||
|
def set_waveform(self, wf: "WaveformConfig") -> None:
|
||||||
|
self._waveform = wf
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
self._running = False
|
self._running = False
|
||||||
if self._acquisition:
|
if self._acquisition:
|
||||||
@@ -169,8 +173,8 @@ class RadarDataWorker(QThread):
|
|||||||
The FPGA already does: FFT, MTI, CFAR, DC notch.
|
The FPGA already does: FFT, MTI, CFAR, DC notch.
|
||||||
Host-side DSP adds: clustering, tracking, geo-coordinate mapping.
|
Host-side DSP adds: clustering, tracking, geo-coordinate mapping.
|
||||||
|
|
||||||
Bin-to-physical conversion uses WaveformConfig defaults to keep
|
Bin-to-physical conversion uses self._waveform (WaveformConfig) to keep
|
||||||
live and replay units aligned (same source of truth as ReplayWorker).
|
live and replay units aligned. Override via set_waveform() if needed.
|
||||||
"""
|
"""
|
||||||
targets: list[RadarTarget] = []
|
targets: list[RadarTarget] = []
|
||||||
|
|
||||||
@@ -180,10 +184,9 @@ class RadarDataWorker(QThread):
|
|||||||
|
|
||||||
# Extract detections from FPGA CFAR flags
|
# Extract detections from FPGA CFAR flags
|
||||||
det_indices = np.argwhere(frame.detections > 0)
|
det_indices = np.argwhere(frame.detections > 0)
|
||||||
wf = WaveformConfig()
|
r_res = self._waveform.range_resolution_m
|
||||||
r_res = wf.range_resolution_m
|
v_res = self._waveform.velocity_resolution_mps
|
||||||
v_res = wf.velocity_resolution_mps
|
n_doppler = frame.detections.shape[1] if frame.detections.ndim == 2 else self._waveform.n_doppler_bins
|
||||||
n_doppler = frame.detections.shape[1] if frame.detections.ndim == 2 else 32
|
|
||||||
doppler_center = n_doppler // 2
|
doppler_center = n_doppler // 2
|
||||||
|
|
||||||
for idx in det_indices:
|
for idx in det_indices:
|
||||||
|
|||||||
Reference in New Issue
Block a user