some fixes and improvements

This commit is contained in:
Ayzen
2026-05-28 14:33:12 +03:00
parent 83a934f251
commit eacea436a4
29 changed files with 2114 additions and 424 deletions
+12 -14
View File
@@ -169,14 +169,15 @@ class AppWindow(
def _init_history_state(self) -> None:
"""Initialize runtime history buffers and render-cache state."""
history_limit = self._history_limit_from_config()
self._raw_history: deque[SweepCollection] = deque(maxlen=history_limit)
self._pre_history: deque[SweepCollection] = deque(maxlen=history_limit)
self._result_history: deque[ResultCollection] = deque(maxlen=history_limit)
bscan_history_limit = self._history_limit_from_config()
save_history_limit = self._save_history_limit_from_config()
self._raw_history: deque[SweepCollection] = deque(maxlen=save_history_limit)
self._pre_history: deque[SweepCollection] = deque(maxlen=save_history_limit)
self._result_history: deque[ResultCollection] = deque(maxlen=save_history_limit)
# Sequence id must survive GUI restarts so history commands stay monotonic.
self._history_command_seq = self._load_history_command_seq(self._live_config_writer.path)
self._bscan_history_limit = history_limit
self._bscan_history_limit = bscan_history_limit
self._bscan_history_by_combo = {}
self._bscan_depth_axis_by_combo = {}
self._bscan_history_floor_collection_id = 0
@@ -200,15 +201,12 @@ class AppWindow(
self._radar_limits: dict[str, float | int] | None = None
def _history_limit_from_config(self) -> int:
"""Return unified GUI history limit derived from configured ring capacities."""
return max(
1,
min(
int(self._defaults_config.rings.raw_tap.capacity),
int(self._defaults_config.rings.preprocessed_tap.capacity),
int(self._defaults_config.rings.results.capacity),
),
)
"""Return B-scan render history limit derived from configured ring capacities."""
return self._history_limit_for_config(self._defaults_config)
def _save_history_limit_from_config(self) -> int:
"""Return maxlen for GUI snapshot-save deques (independent of ring capacities)."""
return self._save_history_limit_for_config(self._defaults_config)
def _init_runtime_limits(self) -> None:
"""Initialize read/drain loop limits used by polling and snapshot code."""
@@ -81,6 +81,10 @@ class AppWindowLiveProcessingMixin:
gpr_draw_top_m_objects=int(self._gpr_draw_top_m_objects.value()),
gpr_speed_m_s=float(self._legacy_gpr_speed_m_s.value()),
gpr_look_angle_deg=float(self._legacy_gpr_look_angle_deg.value()),
gpr_apply_freq_phase_correction=bool(
self._legacy_gpr_apply_freq_phase_correction.isChecked()
),
gpr_reference_mode=self._legacy_gpr_reference_mode.currentText(),
gpr_snr_thresh=float(self._legacy_gpr_snr_thresh.value()),
gpr_snr_comp_max=float(self._legacy_gpr_snr_comp_max.value()),
gpr_start_freq_mhz=gpr_start_freq_mhz,
@@ -117,12 +117,19 @@ class AppWindowConfigProfileIOMixin:
self._optical_variation_panel.setVisible(mode == "variation")
def _apply_history_limit_from_config(self, config) -> None:
"""Resize in-memory history buffers to match the loaded config."""
history_limit = self._history_limit_for_config(config)
self._raw_history = deque(self._raw_history, maxlen=history_limit)
self._pre_history = deque(self._pre_history, maxlen=history_limit)
self._result_history = deque(self._result_history, maxlen=history_limit)
self._bscan_history_limit = history_limit
"""Resize in-memory history buffers to match the loaded config.
Save-side deques use a config-independent limit so that processing-side
ring capacities can stay small without truncating the save buffer. The
B-scan render limit still follows ring capacities to keep plot updates
responsive.
"""
save_history_limit = self._save_history_limit_for_config(config)
bscan_history_limit = self._history_limit_for_config(config)
self._raw_history = deque(self._raw_history, maxlen=save_history_limit)
self._pre_history = deque(self._pre_history, maxlen=save_history_limit)
self._result_history = deque(self._result_history, maxlen=save_history_limit)
self._bscan_history_limit = bscan_history_limit
self._clear_bscan_plot_history()
def _save_current_config(self) -> None:
@@ -283,6 +290,8 @@ class AppWindowConfigProfileIOMixin:
self._legacy_gpr_speed_m_s,
self._legacy_gpr_ignore_socket_speed_enabled,
self._legacy_gpr_look_angle_deg,
self._legacy_gpr_apply_freq_phase_correction,
self._legacy_gpr_reference_mode,
self._legacy_gpr_background_subtract_enabled,
self._legacy_gpr_background_mean_count,
self._legacy_gpr_render_mode,
@@ -451,6 +460,13 @@ class AppWindowConfigProfileIOMixin:
self._legacy_gpr_ignore_socket_speed_enabled.setChecked(ignore_socket_speed_enabled)
self._legacy_gpr_speed_m_s.setEnabled(ignore_socket_speed_enabled)
self._legacy_gpr_look_angle_deg.setValue(float(gui_state.processing.legacy_gpr.look_angle_deg))
self._legacy_gpr_apply_freq_phase_correction.setChecked(
bool(gui_state.processing.legacy_gpr.apply_freq_phase_correction)
)
self._set_combo_current_text(
self._legacy_gpr_reference_mode,
gui_state.processing.legacy_gpr.reference_mode,
)
self._legacy_gpr_background_subtract_enabled.setChecked(
bool(gui_state.processing.legacy_gpr.background_subtract_enabled)
)
@@ -27,6 +27,14 @@ from python_app.orchestration.preprocess_assets import (
from python_app.storage.npz_store import radar_key_from_config
# GUI snapshot-save deques (`_raw_history`, `_pre_history`, `_result_history`)
# are intentionally decoupled from SHM ring capacities: rings are sized for the
# C++ processing pipeline, while save buffers only retain what the GUI poll
# loop already read out. Growing this number lets the user save more recent
# history without touching the processing-side ring sizes.
GUI_SAVE_HISTORY_LIMIT: int = 1000
class AppWindowConfigStateBuildersMixin:
"""Build stable and GUI-only config models from current widget state."""
@@ -156,7 +164,7 @@ class AppWindowConfigStateBuildersMixin:
@staticmethod
def _history_limit_for_config(config: RunConfigModel) -> int:
"""Return unified GUI history limit derived from config ring capacities."""
"""Return B-scan render history limit derived from config ring capacities."""
return max(
1,
min(
@@ -166,6 +174,18 @@ class AppWindowConfigStateBuildersMixin:
),
)
@staticmethod
def _save_history_limit_for_config(config: RunConfigModel) -> int:
"""Return maxlen for GUI snapshot-save deques.
Independent of ring capacities see :data:`GUI_SAVE_HISTORY_LIMIT`.
The `config` argument is kept for symmetry with
:meth:`_history_limit_for_config` and possible future per-profile
overrides.
"""
del config
return GUI_SAVE_HISTORY_LIMIT
def _default_gui_state_for_config(self, config: RunConfigModel) -> GuiStateModel:
"""Build fallback GUI-only defaults for a stable run config."""
default_combo = config.combos[0] if config.combos else ComboModel(input=0, output=0)
@@ -352,6 +372,10 @@ class AppWindowConfigStateBuildersMixin:
speed_m_s=float(self._legacy_gpr_speed_m_s.value()),
ignore_socket_speed_enabled=bool(self._legacy_gpr_ignore_socket_speed_enabled.isChecked()),
look_angle_deg=float(self._legacy_gpr_look_angle_deg.value()),
apply_freq_phase_correction=bool(
self._legacy_gpr_apply_freq_phase_correction.isChecked()
),
reference_mode=self._legacy_gpr_reference_mode.currentText(),
snr_thresh=float(self._legacy_gpr_snr_thresh.value()),
snr_comp_max=float(self._legacy_gpr_snr_comp_max.value()),
background_subtract_enabled=bool(self._legacy_gpr_background_subtract_enabled.isChecked()),
@@ -355,7 +355,8 @@ class AppWindowPipelineMixin:
def _read_all_raw(self) -> SweepCollection | None:
"""Read available raw collections from raw ring."""
assert self._raw_reader is not None
if self._raw_reader is None:
raise RuntimeError("Raw ring reader is not initialised")
latest: SweepCollection | None = None
for _ in range(self._max_pop_per_poll):
collection = self._raw_reader.pop_raw_collection()
@@ -388,7 +389,8 @@ class AppWindowPipelineMixin:
def _read_all_results(self) -> ResultCollection | None:
"""Read available result collections from results ring."""
assert self._result_reader is not None
if self._result_reader is None:
raise RuntimeError("Result ring reader is not initialised")
latest: ResultCollection | None = None
for _ in range(self._max_pop_per_poll):
collection = self._result_reader.pop_result_collection()
@@ -403,6 +403,22 @@ def build_processing_group(owner) -> QGroupBox:
owner._legacy_gpr_look_angle_deg.setSingleStep(0.1)
owner._legacy_gpr_look_angle_deg.setValue(float(legacy_gpr_defaults.look_angle_deg))
# Intra-sweep phase compensation toggle. When checked, S21 phase is
# corrected per frequency point before the IFFT so the radar's motion
# during one sweep is removed at the spectrum stage. Only matters when
# speed != 0; otherwise has no effect by construction.
owner._legacy_gpr_apply_freq_phase_correction = QCheckBox("Apply intra-sweep phase correction")
owner._legacy_gpr_apply_freq_phase_correction.setChecked(
bool(legacy_gpr_defaults.apply_freq_phase_correction)
)
# Anchor point for the motion model's per-event `dt_ref`. `frame_center`
# spreads motion symmetrically around the frame midpoint; `first_tx_event`
# accumulates it forward from the first event center.
owner._legacy_gpr_reference_mode = QComboBox()
owner._legacy_gpr_reference_mode.addItems(["frame_center", "first_tx_event"])
owner._set_combo_current_text(owner._legacy_gpr_reference_mode, legacy_gpr_defaults.reference_mode)
owner._legacy_gpr_background_subtract_enabled = QCheckBox("Subtract mean of previous collections")
owner._legacy_gpr_background_subtract_enabled.setChecked(bool(legacy_gpr_defaults.background_subtract_enabled))
@@ -460,6 +476,8 @@ def build_processing_group(owner) -> QGroupBox:
("Start MHz", owner._legacy_gpr_start_freq_mhz),
("Stop MHz", owner._legacy_gpr_stop_freq_mhz),
("Look angle deg", owner._legacy_gpr_look_angle_deg),
("Reference mode", owner._legacy_gpr_reference_mode),
owner._legacy_gpr_apply_freq_phase_correction,
("Visible X min m", owner._legacy_gpr_visible_x_min_m),
("Visible X max m", owner._legacy_gpr_visible_x_max_m),
("Visible Z min m", owner._legacy_gpr_visible_z_min_m),
@@ -526,6 +544,8 @@ def build_processing_group(owner) -> QGroupBox:
owner._on_legacy_gpr_ignore_socket_speed_toggled
)
owner._legacy_gpr_look_angle_deg.valueChanged.connect(owner._on_processing_live_settings_changed)
owner._legacy_gpr_apply_freq_phase_correction.toggled.connect(owner._on_processing_live_settings_changed)
owner._legacy_gpr_reference_mode.currentTextChanged.connect(owner._on_processing_live_settings_changed)
owner._legacy_gpr_background_subtract_enabled.toggled.connect(owner._on_processing_live_settings_changed)
owner._legacy_gpr_background_mean_count.valueChanged.connect(owner._on_processing_live_settings_changed)
owner._legacy_gpr_render_mode.currentTextChanged.connect(owner._on_gpr_visual_settings_changed)