added timing

This commit is contained in:
Ayzen
2026-05-26 15:08:56 +03:00
parent 5b480f1b55
commit 83a934f251
42 changed files with 1680 additions and 740 deletions
@@ -89,6 +89,9 @@ class AppWindowLiveProcessingMixin:
gpr_background_mean_count=gpr_background_mean_count,
gpr_remove_sidelobe_objects_enabled=bool(self._gpr_remove_sidelobe_objects_enabled.isChecked()),
gpr_imaging_plane_y_m=float(self._gpr_imaging_plane_y_m.value()),
gpr_min_visible_score=float(self._gpr_min_visible_score.value()),
legacy_gpr_min_visible_pair_count=float(self._legacy_gpr_min_visible_pair_count.value()),
ignore_socket_speed=bool(self._legacy_gpr_ignore_socket_speed_enabled.isChecked()),
reprocess_current_result=bool(reprocess_current_result),
history_command_seq=int(self._history_command_seq),
history_command=str(history_command),
@@ -140,6 +143,14 @@ class AppWindowLiveProcessingMixin:
except Exception as exc: # noqa: BLE001
self._show_exception("Failed to update live processing settings", exc)
def _on_legacy_gpr_ignore_socket_speed_toggled(self, ignore_socket_speed: bool) -> None:
"""Reflect socket/manual speed authority in the GUI and live config."""
try:
self._legacy_gpr_speed_m_s.setEnabled(bool(ignore_socket_speed))
self._write_live_processing_config()
except Exception as exc: # noqa: BLE001
self._show_exception("Failed to update socket-speed mode", exc)
def _on_gpr_visual_settings_changed(self, *_args) -> None:
"""Redraw current GPR result using updated GUI-only render settings."""
if not self._is_gpr_processing_mode(self._processing_mode.currentText()):
@@ -152,22 +163,22 @@ class AppWindowLiveProcessingMixin:
self._show_exception("Failed to update GPR render settings", exc)
def _on_gpr_locator_threshold_changed(self, *_args) -> None:
"""Redraw GPR view and republish locator snapshot after threshold changes."""
"""Redraw GPR view; locator delivery lives in the C++ data_processor."""
self._on_gpr_visual_settings_changed()
if not self._is_gpr_processing_mode(self._processing_mode.currentText()):
return
try:
self._publish_locator_snapshot_from_latest_result()
self._write_live_processing_config()
except Exception as exc: # noqa: BLE001
self._show_exception("Failed to update locator GPR threshold", exc)
def _on_gpr_locator_window_changed(self, *_args) -> None:
"""Redraw GPR view and republish locator snapshot after visible X/Z changes."""
"""Redraw GPR view; locator delivery lives in the C++ data_processor."""
self._on_gpr_visual_settings_changed()
if not self._is_gpr_processing_mode(self._processing_mode.currentText()):
return
try:
self._publish_locator_snapshot_from_latest_result()
self._write_live_processing_config()
except Exception as exc: # noqa: BLE001
self._show_exception("Failed to update locator GPR window", exc)
@@ -205,10 +216,6 @@ class AppWindowLiveProcessingMixin:
self._set_plot_mode(mode)
self._set_processing_mode_page(mode)
self._on_processing_live_settings_changed()
if self._is_gpr_processing_mode(mode):
self._publish_locator_snapshot_from_latest_result()
elif self._is_gpr_processing_mode(previous_mode) and self._locator_service is not None:
self._locator_service.publish_empty()
if mode == "pass_through":
self._log(
"Processing mode selected: pass_through "
@@ -445,9 +445,11 @@ class AppWindowConfigProfileIOMixin:
self._legacy_gpr_start_freq_mhz.setValue(float(gui_state.processing.legacy_gpr.start_freq_mhz))
self._legacy_gpr_stop_freq_mhz.setValue(float(gui_state.processing.legacy_gpr.stop_freq_mhz))
self._legacy_gpr_speed_m_s.setValue(float(gui_state.processing.legacy_gpr.speed_m_s))
self._legacy_gpr_ignore_socket_speed_enabled.setChecked(
bool(gui_state.processing.legacy_gpr.ignore_socket_speed_enabled)
ignore_socket_speed_enabled = bool(
gui_state.processing.legacy_gpr.ignore_socket_speed_enabled
)
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_background_subtract_enabled.setChecked(
bool(gui_state.processing.legacy_gpr.background_subtract_enabled)
@@ -494,7 +496,6 @@ class AppWindowConfigProfileIOMixin:
self._apply_initial_radar_limits()
if self._preprocess_dialog is not None:
self._refresh_sets()
self._reload_locator_service_from_config()
self._on_processing_mode_changed(gui_state.processing.selected_mode)
self._update_history_indicator()
self._remember_active_profile_path(profile_path)
@@ -3,6 +3,7 @@
from __future__ import annotations
from python_app.hardware_full.librevna_service import LibreVnaService
from python_app.hardware_full.matrix_radar_service import create_matrix_radar_service
from python_app.hardware_full.single_radar_service import create_single_radar_service
@@ -22,6 +23,8 @@ class AppWindowRadarLimitsMixin:
return self._apply_radar_limits_to_ui(None)
if config.is_multi_device:
radar_service = LibreVnaService(serial=config.radar.serial or None)
elif config.is_matrix_radar:
radar_service = create_matrix_radar_service(config)
else:
radar_service = create_single_radar_service(config)
@@ -399,8 +399,8 @@ class AppWindowConfigStateBuildersMixin:
config.runtime.settling_ms = int(self._settling_ms.text().strip())
config.runtime.processing_live_config_path = str(self._live_config_writer.path)
if config.is_multi_device:
if len(config.radar.multi_device.slave_serials) != 2:
if config.is_matrix_radar:
if config.is_multi_device and len(config.radar.multi_device.slave_serials) != 2:
raise ValueError("LibreVNA multi-device mode requires exactly two slave serials")
config.apply_device_model_constraints()
else:
@@ -4,7 +4,6 @@ from __future__ import annotations
import time
from PyQt6.QtCore import QSignalBlocker
from python_app.gui.runtime.constraints import validate_processing_mode_constraints
from python_app.gui.runtime.history import build_run_history_signature, record_result_history
@@ -12,7 +11,6 @@ from python_app.hardware_full.kamil_adc_service import apply_kamil_adc_laser_con
from python_app.hardware_full.single_radar_service import create_single_radar_service
from python_app.models.dataset_model import ComboKey, ResultCollection, SweepCollection
from python_app.models.run_config_model import RunConfigModel
from python_app.orchestration.gpr_locator import collection_has_gpr_payloads
from python_app.orchestration.preprocess_assets import (
PREPROCESS_ASSET_SPECS,
REQUIRED_PREPROCESS_ASSET_KEYS,
@@ -42,8 +40,18 @@ class AppWindowPipelineMixin:
)
return
if self._supervisor.is_running():
self._show_error("Pipeline is already running", details=self._process_state_details())
return
# A single-shot capture from a running continuous pipeline must
# restart acquisition with `runtime.continuous=false`; refusing
# here would leave the previous run streaming and the user could
# never reach the single-capture termination state.
if not single_capture:
self._show_error(
"Pipeline is already running",
details=self._process_state_details(),
)
return
self._log("Stopping continuous pipeline before single capture")
self._stop_run()
try:
processor_was_running = self._supervisor.is_processor_running()
@@ -198,6 +206,9 @@ class AppWindowPipelineMixin:
if config.is_multi_device:
self._log("Multi-device raw producer will configure all LibreVNA devices")
return
if config.is_matrix_radar:
self._log("Matrix raw producer will configure the matrix radar")
return
if config.is_kamil_adc:
if apply_kamil_adc_laser_control(config):
self._log("Kamil ADC laser_control applied via Apply Radar")
@@ -280,8 +291,6 @@ class AppWindowPipelineMixin:
self._log_error(report.format())
try:
self._drain_locator_speed_updates()
self._drain_locator_log_updates()
if self._raw_reader is not None:
self._read_all_raw()
self._read_all_preprocessed()
@@ -294,7 +303,14 @@ class AppWindowPipelineMixin:
return
return
self._draw_preferred_collection(result_latest=result_latest)
if result_latest is not None:
render_started_ns = time.monotonic_ns()
self._draw_preferred_collection(result_latest=result_latest)
self._pipeline_metrics.record(
"rendering", time.monotonic_ns() - render_started_ns
)
else:
self._draw_preferred_collection(result_latest=None)
except Exception as exc: # noqa: BLE001
signature = (type(exc).__name__, str(exc))
if self._last_reader_error_signature == signature:
@@ -347,6 +363,11 @@ class AppWindowPipelineMixin:
break
self._raw_history.append(collection)
latest = collection
# `capture_*_ns` are populated by the C++ sweep_orchestrator with
# wallclocks captured around the actual device read. Pre-orchestrator
# producers leave them zero, in which case PipelineMetrics drops it.
acquisition_ns = int(collection.capture_end_ns) - int(collection.capture_start_ns)
self._pipeline_metrics.record("acquisition", acquisition_ns)
if self._single_capture_active and self._single_capture_start_ns is not None:
if collection.monotonic_ns >= self._single_capture_start_ns:
self._single_capture_seen_raw = True
@@ -373,13 +394,9 @@ class AppWindowPipelineMixin:
collection = self._result_reader.pop_result_collection()
if collection is None:
break
self._pipeline_metrics.record("processing", int(collection.processing_duration_ns))
if record_result_history(self._result_history, collection):
latest = collection
if (
self._is_gpr_processing_mode(self._processing_mode.currentText())
and collection_has_gpr_payloads(collection)
):
self._publish_locator_snapshot_from_collection(collection)
return latest
def _drain_rings_once_for_history(self) -> None:
@@ -484,60 +501,3 @@ class AppWindowPipelineMixin:
self._live_processing_config(),
)
def _drain_locator_speed_updates(self) -> None:
"""Apply the newest queued locator speed packet to live processing settings."""
if self._locator_service is None:
return
speed_m_s = self._locator_service.drain_speed_updates()
if speed_m_s is None:
return
if self._processing_mode.currentText() != "legacy_gpr":
return
if self._legacy_gpr_ignore_socket_speed_enabled.isChecked():
return
previous_speed_m_s = float(self._legacy_gpr_speed_m_s.value())
with QSignalBlocker(self._legacy_gpr_speed_m_s):
self._legacy_gpr_speed_m_s.setValue(float(speed_m_s))
current_speed_m_s = float(self._legacy_gpr_speed_m_s.value())
if current_speed_m_s == previous_speed_m_s:
return
self._write_live_processing_config(reprocess_current_result=False)
def _drain_locator_log_updates(self) -> None:
"""Append queued locator socket traffic messages to the runtime log."""
if self._locator_service is None:
return
for message in self._locator_service.drain_log_updates():
self._log(message)
def _publish_locator_snapshot_from_collection(self, collection: ResultCollection) -> None:
"""Publish one locator snapshot from a GPR result collection."""
if self._locator_service is None:
return
self._locator_service.publish_collection(
collection,
self._gpr_locator_threshold(),
visible_bounds=self._gpr_visible_object_bounds(),
object_draw_limits=self._gpr_draw_limits(),
)
def _publish_locator_snapshot_from_latest_result(self) -> None:
"""Publish current locator-visible snapshot from latest cached GPR result."""
if self._locator_service is None:
return
if not self._is_gpr_processing_mode(self._processing_mode.currentText()):
self._locator_service.publish_empty()
return
if not self._result_history:
self._locator_service.publish_empty()
return
latest = self._result_history[-1]
if not collection_has_gpr_payloads(latest):
self._locator_service.publish_empty()
return
self._publish_locator_snapshot_from_collection(latest)
@@ -390,6 +390,12 @@ def build_processing_group(owner) -> QGroupBox:
owner._legacy_gpr_ignore_socket_speed_enabled.setChecked(
bool(legacy_gpr_defaults.ignore_socket_speed_enabled)
)
# When the box is unchecked, an external TCP client controls the speed
# through the C++ locator server; disable the spinner so the GUI value
# cannot silently win against the live socket value.
owner._legacy_gpr_speed_m_s.setEnabled(
bool(legacy_gpr_defaults.ignore_socket_speed_enabled)
)
owner._legacy_gpr_look_angle_deg = QDoubleSpinBox()
owner._legacy_gpr_look_angle_deg.setDecimals(2)
@@ -516,6 +522,9 @@ def build_processing_group(owner) -> QGroupBox:
owner._legacy_gpr_start_freq_mhz.valueChanged.connect(owner._on_processing_live_settings_changed)
owner._legacy_gpr_stop_freq_mhz.valueChanged.connect(owner._on_processing_live_settings_changed)
owner._legacy_gpr_speed_m_s.valueChanged.connect(owner._on_processing_live_settings_changed)
owner._legacy_gpr_ignore_socket_speed_enabled.toggled.connect(
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_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)