Rework radar system

This commit is contained in:
Ayzen
2026-10-06 12:32:26 +03:00
parent 552cdc688a
commit 5bb86b7165
299 changed files with 13537 additions and 27763 deletions
@@ -0,0 +1,94 @@
"""Settings reach the processor atomically and preserve the saved frequency band."""
from copy import deepcopy
import unittest
from unittest import mock
from PyQt6.QtWidgets import QApplication, QComboBox, QDoubleSpinBox, QStackedWidget
from python_app.gui.antenna_geometry_editor import AntennaGeometryEditor
from python_app.gui.controllers.sections.processing_section import build_processing_group
from python_app.models.gui_profile_schema import GuiStateModel
from python_app.models.dataset_model import SweepCollection
from python_app.models.run_config_schema import RunConfigModel
from python_app.tests.gui_test_support import isolated_window, widget
class LiveSettingsRegressionTest(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
cls.app = QApplication.instance() or QApplication([])
def test_initial_processing_band_uses_sweep_limits_before_profile_values(self) -> None:
config, gui = RunConfigModel(), GuiStateModel()
config.radar.sweep.start_hz, config.radar.sweep.stop_hz = 1e6, 9.5e9
for settings in (gui.processing.bscan, gui.processing.gpr,
gui.processing.legacy_gpr, gui.processing.sar3d):
settings.start_freq_mhz, settings.stop_freq_mhz = 20, 9200
section = build_processing_group(config, gui)
self.addCleanup(section.group.deleteLater)
for mode in ("bscan", "gpr", "legacy_gpr", "sar3d"):
for bound, expected in (("start", 20), ("stop", 9200)):
with self.subTest(mode=mode, bound=bound):
editor = widget(section, f"_{mode}_{bound}_freq_mhz", QDoubleSpinBox)
self.assertEqual((editor.minimum(), editor.maximum(), editor.value()), (1, 9500, expected))
def test_failed_live_write_restores_web_batch_and_desktop_changes(self) -> None:
window = isolated_window(self.addCleanup)
window._pipeline.timer.stop()
mode = window._view.processing.mode
gain = widget(window._view.processing, "_bscan_gain", QDoubleSpinBox)
pages = window._view.processing.pages
live = window._live
before = (window._profile.build_gui_state(), window._config.geometry(),
live.active_mode, pages.currentIndex(), window._plots.stack.currentWidget(),
live.revision, deepcopy(live.processing_settings), window._plots.required_revision,
live.writer.path.read_bytes())
for source in ("web", "web_geometry", "desktop_mode", "desktop_value", "desktop_geometry"):
with self.subTest(source=source), \
mock.patch.object(live.writer, "write", side_effect=OSError("disk full")), \
mock.patch.object(window._preprocess, "invalidate_object_calibration_after_geometry_change") as invalidate, \
mock.patch.object(window._log, "show_exception") as error:
if source == "web":
live.apply_web({"processor_mode": "bscan", "bscan_gain": gain.value() + .1})
elif source in {"web_geometry", "desktop_geometry"}:
editor = window._view.processing.geometries[0]
records = editor.records()
records[0]["x_m"] += .125
if source == "web_geometry":
live.apply_web({"tx_geometry": records})
else:
editor.set_records(records)
elif source == "desktop_mode":
mode.setCurrentText("bscan")
else:
widget(window._view.processing, "_gpr_range_comp_power", QDoubleSpinBox).setValue(.4)
error.assert_called_once()
invalidate.assert_not_called()
self.assertEqual((window._profile.build_gui_state(), window._config.geometry(),
live.active_mode, pages.currentIndex(), window._plots.stack.currentWidget(),
live.revision, live.processing_settings, window._plots.required_revision,
live.writer.path.read_bytes()), before)
with mock.patch.object(window._plots, "draw_bscan", side_effect=ValueError("invalid plot")), \
mock.patch.object(window._log, "show_exception") as error:
live.apply_web({"processor_mode": "bscan", "bscan_gain": gain.value() + .1})
error.assert_called_once()
self.assertEqual((mode.currentText(), live.active_mode, pages.currentIndex()), ("bscan", "bscan", 1))
self.assertEqual(live.processing_settings[live.revision]["processor_mode"], "bscan")
self.assertNotEqual(live.writer.path.read_bytes(), before[-1])
def test_mode_publication_selects_replay_from_new_mode_before_changing_the_plot(self) -> None:
window = isolated_window(self.addCleanup)
window._pipeline.timer.stop()
window._history.preprocessed.append(SweepCollection(1, 1, [], capture_start_ns=1, capture_end_ns=2))
with mock.patch.object(window._pipeline.supervisor, "is_processor_running", return_value=True), \
mock.patch.object(window._pipeline, "_result_reader"), \
mock.patch.object(window._pipeline, "drain_results", return_value=None), \
mock.patch.object(window._pipeline, "schedule_replay"), \
mock.patch.object(window._plots, "draw_bscan"):
for mode, reprocess in (("bscan", False), ("pass_through", True)):
with self.subTest(mode=mode):
window._live.apply_web({"processor_mode": mode})
published = window._live.processing_settings[window._live.revision]
self.assertEqual(published["processor_mode"], mode)
self.assertEqual(published["reprocess_current_result"], reprocess)