From ebd96c90ce9bcc40ddc29afe377698c97808496f Mon Sep 17 00:00:00 2001 From: Jason <83615043+JJassonn69@users.noreply.github.com> Date: Tue, 21 Apr 2026 02:35:53 +0545 Subject: [PATCH] fix(v7): store WaveformConfig on self; add set_waveform parity; fix magic 32 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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. --- 9_Firmware/9_3_GUI/test_v7.py | 21 ++++++++++++--------- 9_Firmware/9_3_GUI/v7/workers.py | 15 +++++++++------ 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/9_Firmware/9_3_GUI/test_v7.py b/9_Firmware/9_3_GUI/test_v7.py index 0d93862..7000d1b 100644 --- a/9_Firmware/9_3_GUI/test_v7.py +++ b/9_Firmware/9_3_GUI/test_v7.py @@ -672,16 +672,19 @@ class TestLiveReplayPhysicalUnitsParity(unittest.TestCase): return False def test_live_path_uses_waveform_config(self): - """_run_host_dsp must call WaveformConfig() and read - wf.range_resolution_m / wf.velocity_resolution_mps — not - self._settings.range_resolution / velocity_resolution.""" + """RadarDataWorker.__init__ must instantiate WaveformConfig() into + self._waveform; _run_host_dsp must read self._waveform.range_resolution_m + / 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") - self.assertTrue(self._has_call_to(method, "WaveformConfig"), - "Live path must instantiate WaveformConfig().") - self.assertTrue(self._has_attribute_chain(method, ("wf", "range_resolution_m")), - "Live path must read wf.range_resolution_m.") - self.assertTrue(self._has_attribute_chain(method, ("wf", "velocity_resolution_mps")), - "Live path must read wf.velocity_resolution_mps. " + self.assertTrue( + self._has_attribute_chain(method, ("self", "_waveform", "range_resolution_m")), + "Live path must read self._waveform.range_resolution_m.") + self.assertTrue( + self._has_attribute_chain(method, ("self", "_waveform", "velocity_resolution_mps")), + "Live path must read self._waveform.velocity_resolution_mps. " "RadarSettings.velocity_resolution default 1.0 caused ~5.34x " "underreport vs replay (test_v7.py:449 pins ~5.343).") self.assertFalse(self._has_attribute_chain( diff --git a/9_Firmware/9_3_GUI/v7/workers.py b/9_Firmware/9_3_GUI/v7/workers.py index 727c72b..6d80615 100644 --- a/9_Firmware/9_3_GUI/v7/workers.py +++ b/9_Firmware/9_3_GUI/v7/workers.py @@ -84,6 +84,7 @@ class RadarDataWorker(QThread): self._recorder = recorder self._gps = gps_data_ref self._settings = settings or RadarSettings() + self._waveform = WaveformConfig() self._running = False # Frame queue for production RadarAcquisition → this thread @@ -97,6 +98,9 @@ class RadarDataWorker(QThread): self._byte_count = 0 self._error_count = 0 + def set_waveform(self, wf: "WaveformConfig") -> None: + self._waveform = wf + def stop(self): self._running = False if self._acquisition: @@ -169,8 +173,8 @@ class RadarDataWorker(QThread): The FPGA already does: FFT, MTI, CFAR, DC notch. Host-side DSP adds: clustering, tracking, geo-coordinate mapping. - Bin-to-physical conversion uses WaveformConfig defaults to keep - live and replay units aligned (same source of truth as ReplayWorker). + Bin-to-physical conversion uses self._waveform (WaveformConfig) to keep + live and replay units aligned. Override via set_waveform() if needed. """ targets: list[RadarTarget] = [] @@ -180,10 +184,9 @@ class RadarDataWorker(QThread): # Extract detections from FPGA CFAR flags det_indices = np.argwhere(frame.detections > 0) - wf = WaveformConfig() - r_res = wf.range_resolution_m - v_res = wf.velocity_resolution_mps - n_doppler = frame.detections.shape[1] if frame.detections.ndim == 2 else 32 + r_res = self._waveform.range_resolution_m + v_res = self._waveform.velocity_resolution_mps + n_doppler = frame.detections.shape[1] if frame.detections.ndim == 2 else self._waveform.n_doppler_bins doppler_center = n_doppler // 2 for idx in det_indices: