new GPR parameters and some UI fixes
This commit is contained in:
@@ -11,6 +11,7 @@ from PyQt6.QtCore import QSignalBlocker
|
||||
from PyQt6.QtWidgets import QFileDialog
|
||||
|
||||
from python_app.hardware_full.librevna_service import LibreVnaService
|
||||
from python_app.gui.runtime.constraints import validate_processing_mode_constraints
|
||||
from python_app.models.gui_profile_model import (
|
||||
GuiBscanStateModel,
|
||||
GuiDataActionsStateModel,
|
||||
@@ -37,6 +38,14 @@ from python_app.storage.npz_store import radar_key_from_config
|
||||
class AppWindowConfigMixin:
|
||||
"""Builds runtime config models from current UI state."""
|
||||
|
||||
def _validate_processing_mode_selection(self, mode: str) -> None:
|
||||
"""Validate requested processing mode against current stable/live GUI state."""
|
||||
validate_processing_mode_constraints(
|
||||
mode,
|
||||
self._build_config(),
|
||||
self._live_processing_config(),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _parse_csv_int_list(text: str) -> list[int]:
|
||||
"""Parse comma-separated integer selection list."""
|
||||
@@ -117,6 +126,16 @@ class AppWindowConfigMixin:
|
||||
values = sorted(geometry_values & combo_values) or sorted(geometry_values)
|
||||
return ",".join(str(value) for value in values)
|
||||
|
||||
@staticmethod
|
||||
def _default_gpr_visible_x_bounds_from_config(config: RunConfigModel) -> tuple[float, float]:
|
||||
"""Build default visible X-range for GPR object-only rendering."""
|
||||
x_values = [float(entry.x_m) for entry in config.gpr.tx_geometry]
|
||||
x_values.extend(float(entry.x_m) for entry in config.gpr.rx_geometry)
|
||||
if not x_values:
|
||||
return (-2.0, 2.0)
|
||||
margin_m = 2.0
|
||||
return (min(x_values) - margin_m, max(x_values) + margin_m)
|
||||
|
||||
@staticmethod
|
||||
def _history_limit_for_config(config: RunConfigModel) -> int:
|
||||
"""Return unified GUI history limit derived from config ring capacities."""
|
||||
@@ -133,6 +152,7 @@ class AppWindowConfigMixin:
|
||||
"""Build fallback GUI-only defaults for a stable run config."""
|
||||
default_combo = config.combos[0] if config.combos else ComboModel(input=0, output=0)
|
||||
default_mode = "single" if len(config.combos) == 1 else "text"
|
||||
default_gpr_x_min_m, default_gpr_x_max_m = self._default_gpr_visible_x_bounds_from_config(config)
|
||||
return GuiStateModel(
|
||||
switches=GuiSwitchStateModel(
|
||||
combo_mode=default_mode,
|
||||
@@ -167,8 +187,16 @@ class AppWindowConfigMixin:
|
||||
stop_freq_mhz=6000.0,
|
||||
speed_m_s=0.0,
|
||||
look_angle_deg=0.0,
|
||||
snr_thresh=4.5,
|
||||
snr_comp_max=25.0,
|
||||
background_subtract_enabled=True,
|
||||
background_mean_count=10,
|
||||
render_mode="heatmap",
|
||||
min_visible_pair_count=1,
|
||||
visible_x_min_m=default_gpr_x_min_m,
|
||||
visible_x_max_m=default_gpr_x_max_m,
|
||||
visible_z_min_m=0.0,
|
||||
visible_z_max_m=14.0,
|
||||
),
|
||||
),
|
||||
data_actions=GuiDataActionsStateModel(
|
||||
@@ -221,8 +249,16 @@ class AppWindowConfigMixin:
|
||||
stop_freq_mhz=float(self._gpr_stop_freq_mhz.value()),
|
||||
speed_m_s=float(self._gpr_speed_m_s.value()),
|
||||
look_angle_deg=float(self._gpr_look_angle_deg.value()),
|
||||
snr_thresh=float(self._gpr_snr_thresh.value()),
|
||||
snr_comp_max=float(self._gpr_snr_comp_max.value()),
|
||||
background_subtract_enabled=bool(self._gpr_background_subtract_enabled.isChecked()),
|
||||
background_mean_count=int(self._gpr_background_mean_count.value()),
|
||||
render_mode=self._gpr_render_mode.currentText(),
|
||||
min_visible_pair_count=int(self._gpr_min_visible_pair_count.value()),
|
||||
visible_x_min_m=float(self._gpr_visible_x_min_m.value()),
|
||||
visible_x_max_m=float(self._gpr_visible_x_max_m.value()),
|
||||
visible_z_min_m=float(self._gpr_visible_z_min_m.value()),
|
||||
visible_z_max_m=float(self._gpr_visible_z_max_m.value()),
|
||||
),
|
||||
),
|
||||
data_actions=GuiDataActionsStateModel(
|
||||
@@ -404,8 +440,16 @@ class AppWindowConfigMixin:
|
||||
self._gpr_stop_freq_mhz,
|
||||
self._gpr_speed_m_s,
|
||||
self._gpr_look_angle_deg,
|
||||
self._gpr_snr_thresh,
|
||||
self._gpr_snr_comp_max,
|
||||
self._gpr_background_subtract_enabled,
|
||||
self._gpr_background_mean_count,
|
||||
self._gpr_render_mode,
|
||||
self._gpr_min_visible_pair_count,
|
||||
self._gpr_visible_x_min_m,
|
||||
self._gpr_visible_x_max_m,
|
||||
self._gpr_visible_z_min_m,
|
||||
self._gpr_visible_z_max_m,
|
||||
self._save_count,
|
||||
self._save_path_input,
|
||||
self._save_name_input,
|
||||
@@ -466,10 +510,18 @@ class AppWindowConfigMixin:
|
||||
self._gpr_stop_freq_mhz.setValue(float(gui_state.processing.gpr.stop_freq_mhz))
|
||||
self._gpr_speed_m_s.setValue(float(gui_state.processing.gpr.speed_m_s))
|
||||
self._gpr_look_angle_deg.setValue(float(gui_state.processing.gpr.look_angle_deg))
|
||||
self._gpr_snr_thresh.setValue(float(gui_state.processing.gpr.snr_thresh))
|
||||
self._gpr_snr_comp_max.setValue(float(gui_state.processing.gpr.snr_comp_max))
|
||||
self._gpr_background_subtract_enabled.setChecked(
|
||||
bool(gui_state.processing.gpr.background_subtract_enabled)
|
||||
)
|
||||
self._gpr_background_mean_count.setValue(int(gui_state.processing.gpr.background_mean_count))
|
||||
self._set_combo_current_text(self._gpr_render_mode, gui_state.processing.gpr.render_mode)
|
||||
self._gpr_min_visible_pair_count.setValue(int(gui_state.processing.gpr.min_visible_pair_count))
|
||||
self._gpr_visible_x_min_m.setValue(float(gui_state.processing.gpr.visible_x_min_m))
|
||||
self._gpr_visible_x_max_m.setValue(float(gui_state.processing.gpr.visible_x_max_m))
|
||||
self._gpr_visible_z_min_m.setValue(float(gui_state.processing.gpr.visible_z_min_m))
|
||||
self._gpr_visible_z_max_m.setValue(float(gui_state.processing.gpr.visible_z_max_m))
|
||||
|
||||
self._save_count.setValue(int(gui_state.data_actions.save_count))
|
||||
self._save_path_input.setText(str(gui_state.data_actions.save_path))
|
||||
@@ -478,6 +530,7 @@ class AppWindowConfigMixin:
|
||||
self._defaults_config = config
|
||||
self._gui_defaults = gui_state
|
||||
self._selected_preprocess_sets = selected_preprocess_sets
|
||||
self._selected_preprocess_radar_key = self._radar_key(config)
|
||||
self._preprocess_set_name = str(gui_state.preprocess_dialog.set_name)
|
||||
self._apply_history_limit_from_config(config)
|
||||
self._gpr_geometry_signature = None
|
||||
@@ -552,6 +605,18 @@ class AppWindowConfigMixin:
|
||||
power_dbm=config.radar.sweep.power_dbm,
|
||||
)
|
||||
|
||||
def _radar_key_from_ui(self) -> str:
|
||||
"""Build current radar key directly from radar widgets only."""
|
||||
return radar_key_from_config(
|
||||
model_name=self._defaults_config.radar.model,
|
||||
serial=self._serial_input.text().strip(),
|
||||
sweep_start_hz=float(self._start_hz_input.text().strip()),
|
||||
sweep_stop_hz=float(self._stop_hz_input.text().strip()),
|
||||
sweep_points=int(self._points_input.text().strip()),
|
||||
ifbw_hz=float(self._ifbw_input.text().strip()),
|
||||
power_dbm=float(self._power_input.text().strip()),
|
||||
)
|
||||
|
||||
def _live_processing_config(self, *, history_command: str = "none") -> ProcessingLiveConfig:
|
||||
"""Build live processing config from current processing widgets."""
|
||||
self._sync_bscan_frequency_limits_with_radar()
|
||||
@@ -580,6 +645,8 @@ class AppWindowConfigMixin:
|
||||
gpr_stop_freq_mhz=float(self._gpr_stop_freq_mhz.value()),
|
||||
gpr_speed_m_s=float(self._gpr_speed_m_s.value()),
|
||||
gpr_look_angle_deg=float(self._gpr_look_angle_deg.value()),
|
||||
gpr_snr_thresh=float(self._gpr_snr_thresh.value()),
|
||||
gpr_snr_comp_max=float(self._gpr_snr_comp_max.value()),
|
||||
gpr_background_subtract_enabled=bool(self._gpr_background_subtract_enabled.isChecked()),
|
||||
gpr_background_mean_count=int(self._gpr_background_mean_count.value()),
|
||||
history_command_seq=int(self._history_command_seq),
|
||||
@@ -615,8 +682,33 @@ class AppWindowConfigMixin:
|
||||
except Exception as exc: # noqa: BLE001
|
||||
self._show_exception("Failed to update live processing settings", exc)
|
||||
|
||||
def _on_gpr_visual_settings_changed(self, *_args) -> None:
|
||||
"""Redraw current GPR result using updated GUI-only render settings."""
|
||||
if self._processing_mode.currentText() != "gpr":
|
||||
return
|
||||
try:
|
||||
if self._result_history and self._draw_results(self._result_history[-1]):
|
||||
return
|
||||
self._clear_gpr_plot()
|
||||
except Exception as exc: # noqa: BLE001
|
||||
self._show_exception("Failed to update GPR render settings", exc)
|
||||
|
||||
def _on_processing_mode_changed(self, mode: str) -> None:
|
||||
"""Switch processing parameter page and refresh corresponding visualization."""
|
||||
previous_mode = getattr(self, "_active_processing_mode", "pass_through")
|
||||
if mode != previous_mode:
|
||||
try:
|
||||
self._validate_processing_mode_selection(mode)
|
||||
except Exception as exc: # noqa: BLE001
|
||||
with QSignalBlocker(self._processing_mode):
|
||||
self._set_combo_current_text(self._processing_mode, previous_mode)
|
||||
self._show_error(
|
||||
f"Cannot switch processing mode to {mode}",
|
||||
details=str(exc) or type(exc).__name__,
|
||||
)
|
||||
return
|
||||
|
||||
self._active_processing_mode = mode
|
||||
mode_to_page = {
|
||||
"pass_through": 0,
|
||||
"bscan": 1,
|
||||
@@ -655,8 +747,12 @@ class AppWindowConfigMixin:
|
||||
f"freq={self._gpr_start_freq_mhz.value():g}..{self._gpr_stop_freq_mhz.value():g} MHz, "
|
||||
f"speed={self._gpr_speed_m_s.value():g} m/s, "
|
||||
f"look_angle={self._gpr_look_angle_deg.value():g} deg, "
|
||||
f"snr_thresh={self._gpr_snr_thresh.value():g}, "
|
||||
f"snr_comp_max={self._gpr_snr_comp_max.value():g}, "
|
||||
f"background_subtract={self._gpr_background_subtract_enabled.isChecked()}, "
|
||||
f"mean_count={self._gpr_background_mean_count.value()})"
|
||||
f"mean_count={self._gpr_background_mean_count.value()}, "
|
||||
f"render_mode={self._gpr_render_mode.currentText()}, "
|
||||
f"min_pairs={self._gpr_min_visible_pair_count.value()})"
|
||||
)
|
||||
|
||||
def _clear_history_mode_caches(self) -> None:
|
||||
@@ -665,6 +761,7 @@ class AppWindowConfigMixin:
|
||||
self._clear_bscan_plot_history()
|
||||
if hasattr(self, "_bscan_plot"):
|
||||
self._bscan_plot.clear()
|
||||
self._configure_bscan_plot_axes()
|
||||
self._clear_trace_plots()
|
||||
if hasattr(self, "_gpr_plot"):
|
||||
self._clear_gpr_plot()
|
||||
@@ -676,6 +773,7 @@ class AppWindowConfigMixin:
|
||||
self._sync_bscan_history_from_results()
|
||||
if not self._draw_bscan_heatmap_from_history():
|
||||
self._bscan_plot.clear()
|
||||
self._configure_bscan_plot_axes()
|
||||
return
|
||||
if self._processing_mode.currentText() == "gpr":
|
||||
if self._result_history and self._draw_results(self._result_history[-1]):
|
||||
@@ -686,10 +784,12 @@ class AppWindowConfigMixin:
|
||||
self._draw_results(self._result_history[-1])
|
||||
return
|
||||
self._bscan_plot.clear()
|
||||
self._configure_bscan_plot_axes()
|
||||
self._clear_trace_plots()
|
||||
|
||||
def _on_radar_identity_changed(self, *_args) -> None:
|
||||
"""Refresh device limits when radar identity/mode changes."""
|
||||
self._reset_preprocess_selection_after_radar_key_change()
|
||||
if self._radar_mode.currentText() != "native":
|
||||
self._apply_radar_limits_to_ui(None)
|
||||
return
|
||||
@@ -699,6 +799,7 @@ class AppWindowConfigMixin:
|
||||
|
||||
def _on_radar_sweep_limits_changed(self) -> None:
|
||||
"""Clamp processing frequency bounds after sweep start/stop edits."""
|
||||
self._reset_preprocess_selection_after_radar_key_change()
|
||||
if self._sync_processing_frequency_limits_with_radar():
|
||||
self._on_processing_live_settings_changed()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user