kamil_adc support added
This commit is contained in:
@@ -24,7 +24,12 @@ class AppWindowLiveProcessingMixin:
|
||||
self._live_processing_config(),
|
||||
)
|
||||
|
||||
def _live_processing_config(self, *, history_command: str = "none") -> ProcessingLiveConfig:
|
||||
def _live_processing_config(
|
||||
self,
|
||||
*,
|
||||
history_command: str = "none",
|
||||
reprocess_current_result: bool = True,
|
||||
) -> ProcessingLiveConfig:
|
||||
"""Build live processing config from current processing widgets."""
|
||||
self._sync_bscan_frequency_limits_with_radar()
|
||||
self._sync_gpr_frequency_limits_with_radar()
|
||||
@@ -71,6 +76,9 @@ class AppWindowLiveProcessingMixin:
|
||||
gpr_range_comp_power=float(self._gpr_range_comp_power.value()),
|
||||
gpr_angle_comp_power=float(self._gpr_angle_comp_power.value()),
|
||||
gpr_comp_power=float(self._legacy_gpr_comp_power.value()),
|
||||
gpr_score_mode=self._gpr_score_mode.currentText(),
|
||||
gpr_max_detected_objects_to_draw=int(self._gpr_max_detected_objects_to_draw.value()),
|
||||
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_snr_thresh=float(self._legacy_gpr_snr_thresh.value()),
|
||||
@@ -80,15 +88,27 @@ class AppWindowLiveProcessingMixin:
|
||||
gpr_background_subtract_enabled=gpr_background_enabled,
|
||||
gpr_background_mean_count=gpr_background_mean_count,
|
||||
gpr_remove_sidelobe_objects_enabled=bool(self._gpr_remove_sidelobe_objects_enabled.isChecked()),
|
||||
reprocess_current_result=bool(reprocess_current_result),
|
||||
history_command_seq=int(self._history_command_seq),
|
||||
history_command=str(history_command),
|
||||
)
|
||||
|
||||
def _write_live_processing_config(self, *, history_command: str = "none", bump_history_seq: bool = False) -> None:
|
||||
def _write_live_processing_config(
|
||||
self,
|
||||
*,
|
||||
history_command: str = "none",
|
||||
bump_history_seq: bool = False,
|
||||
reprocess_current_result: bool = True,
|
||||
) -> None:
|
||||
"""Persist current live processing config to runtime JSON file."""
|
||||
if bump_history_seq:
|
||||
self._history_command_seq += 1
|
||||
self._live_config_writer.write(self._live_processing_config(history_command=history_command))
|
||||
self._live_config_writer.write(
|
||||
self._live_processing_config(
|
||||
history_command=history_command,
|
||||
reprocess_current_result=reprocess_current_result,
|
||||
)
|
||||
)
|
||||
|
||||
def _on_processing_live_settings_changed(self, *_args) -> None:
|
||||
"""Handle live-processing setting changes and trigger redraw when needed."""
|
||||
@@ -215,11 +235,14 @@ class AppWindowLiveProcessingMixin:
|
||||
f"freq={self._gpr_start_freq_mhz.value():g}..{self._gpr_stop_freq_mhz.value():g} MHz, "
|
||||
f"range_comp={self._gpr_range_comp_power.value():g}, "
|
||||
f"angle_comp={self._gpr_angle_comp_power.value():g}, "
|
||||
f"score_mode={self._gpr_score_mode.currentText()}, "
|
||||
f"background_subtract={self._gpr_background_subtract_enabled.isChecked()}, "
|
||||
f"mean_count={self._gpr_background_mean_count.value()}, "
|
||||
f"remove_sidelobes={self._gpr_remove_sidelobe_objects_enabled.isChecked()}, "
|
||||
f"render_mode={self._gpr_render_mode.currentText()}, "
|
||||
f"min_score={self._gpr_min_visible_score.value():g})"
|
||||
f"min_score={self._gpr_min_visible_score.value():g}, "
|
||||
f"max_draw={self._gpr_max_detected_objects_to_draw.value()}, "
|
||||
f"draw_top={self._gpr_draw_top_m_objects.value()})"
|
||||
)
|
||||
elif mode == "legacy_gpr":
|
||||
self._log(
|
||||
|
||||
@@ -70,6 +70,52 @@ class AppWindowConfigProfileIOMixin:
|
||||
self._pass_through_y_min_db.setEnabled(enabled)
|
||||
self._pass_through_y_max_db.setEnabled(enabled)
|
||||
|
||||
def _set_radar_settings_mode(self, adc_mode: bool) -> None:
|
||||
"""Show the radar settings panel appropriate for the loaded radar model."""
|
||||
self._vna_radar_settings_panel.setVisible(not adc_mode)
|
||||
self._adc_radar_settings_panel.setVisible(adc_mode)
|
||||
|
||||
def _sync_adc_settings_controls(self) -> None:
|
||||
"""Enable optical-board controls according to enabled flag and mode."""
|
||||
if not hasattr(self, "_optical_enabled_checkbox"):
|
||||
return
|
||||
enabled = bool(self._optical_enabled_checkbox.isChecked())
|
||||
mode = self._optical_mode_combo.currentText()
|
||||
common_widgets = [
|
||||
self._optical_port_input,
|
||||
self._optical_mode_combo,
|
||||
self._optical_pi_coeff1_p_input,
|
||||
self._optical_pi_coeff1_i_input,
|
||||
self._optical_pi_coeff2_p_input,
|
||||
self._optical_pi_coeff2_i_input,
|
||||
]
|
||||
manual_widgets = [
|
||||
self._optical_manual_temp1_input,
|
||||
self._optical_manual_temp2_input,
|
||||
self._optical_manual_current1_input,
|
||||
self._optical_manual_current2_input,
|
||||
]
|
||||
variation_widgets = [
|
||||
self._optical_variation_type_combo,
|
||||
self._optical_static_temp1_input,
|
||||
self._optical_static_temp2_input,
|
||||
self._optical_static_current1_input,
|
||||
self._optical_static_current2_input,
|
||||
self._optical_min_value_input,
|
||||
self._optical_max_value_input,
|
||||
self._optical_step_input,
|
||||
self._optical_time_step_input,
|
||||
self._optical_delay_time_input,
|
||||
]
|
||||
for widget in common_widgets:
|
||||
widget.setEnabled(enabled)
|
||||
for widget in manual_widgets:
|
||||
widget.setEnabled(enabled and mode == "manual")
|
||||
for widget in variation_widgets:
|
||||
widget.setEnabled(enabled and mode == "variation")
|
||||
self._optical_manual_panel.setVisible(mode == "manual")
|
||||
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)
|
||||
@@ -101,12 +147,17 @@ class AppWindowConfigProfileIOMixin:
|
||||
self._defaults_config = profile.run_config.clone()
|
||||
self._gui_defaults = profile.gui
|
||||
self._remember_active_profile_path(output_path)
|
||||
points_text = (
|
||||
"points=from Kamil ADC stream"
|
||||
if profile.run_config.is_kamil_adc
|
||||
else f"points={profile.run_config.radar.sweep.points}"
|
||||
)
|
||||
self._log(
|
||||
f"Config profile saved: path={output_path}, "
|
||||
f"combos={len(profile.run_config.combos)}, "
|
||||
f"sweep={profile.run_config.radar.sweep.start_hz:g}.."
|
||||
f"{profile.run_config.radar.sweep.stop_hz:g} Hz, "
|
||||
f"points={profile.run_config.radar.sweep.points}, "
|
||||
f"{points_text}, "
|
||||
f"ifbw={profile.run_config.radar.sweep.if_bandwidth_hz:g} Hz, "
|
||||
f"power={profile.run_config.radar.sweep.power_dbm:g} dBm, "
|
||||
f"processing_mode={profile.gui.processing.selected_mode}"
|
||||
@@ -203,6 +254,9 @@ class AppWindowConfigProfileIOMixin:
|
||||
self._gpr_max_depth_m,
|
||||
self._gpr_range_comp_power,
|
||||
self._gpr_angle_comp_power,
|
||||
self._gpr_score_mode,
|
||||
self._gpr_max_detected_objects_to_draw,
|
||||
self._gpr_draw_top_m_objects,
|
||||
self._gpr_start_freq_mhz,
|
||||
self._gpr_stop_freq_mhz,
|
||||
self._gpr_background_subtract_enabled,
|
||||
@@ -225,6 +279,7 @@ class AppWindowConfigProfileIOMixin:
|
||||
self._legacy_gpr_start_freq_mhz,
|
||||
self._legacy_gpr_stop_freq_mhz,
|
||||
self._legacy_gpr_speed_m_s,
|
||||
self._legacy_gpr_ignore_socket_speed_enabled,
|
||||
self._legacy_gpr_look_angle_deg,
|
||||
self._legacy_gpr_background_subtract_enabled,
|
||||
self._legacy_gpr_background_mean_count,
|
||||
@@ -237,6 +292,35 @@ class AppWindowConfigProfileIOMixin:
|
||||
self._save_count,
|
||||
self._save_path_input,
|
||||
self._save_name_input,
|
||||
self._adc_project_dir_input,
|
||||
self._adc_executable_path_input,
|
||||
self._adc_tty_path_input,
|
||||
self._adc_args_input,
|
||||
self._adc_env_input,
|
||||
self._adc_startup_timeout_s_input,
|
||||
self._adc_sweep_timeout_s_input,
|
||||
self._adc_stop_timeout_s_input,
|
||||
self._optical_enabled_checkbox,
|
||||
self._optical_port_input,
|
||||
self._optical_mode_combo,
|
||||
self._optical_pi_coeff1_p_input,
|
||||
self._optical_pi_coeff1_i_input,
|
||||
self._optical_pi_coeff2_p_input,
|
||||
self._optical_pi_coeff2_i_input,
|
||||
self._optical_manual_temp1_input,
|
||||
self._optical_manual_temp2_input,
|
||||
self._optical_manual_current1_input,
|
||||
self._optical_manual_current2_input,
|
||||
self._optical_variation_type_combo,
|
||||
self._optical_static_temp1_input,
|
||||
self._optical_static_temp2_input,
|
||||
self._optical_static_current1_input,
|
||||
self._optical_static_current2_input,
|
||||
self._optical_min_value_input,
|
||||
self._optical_max_value_input,
|
||||
self._optical_step_input,
|
||||
self._optical_time_step_input,
|
||||
self._optical_delay_time_input,
|
||||
)
|
||||
|
||||
with ExitStack() as blockers:
|
||||
@@ -249,6 +333,42 @@ class AppWindowConfigProfileIOMixin:
|
||||
self._ifbw_input.setText(f"{config.radar.sweep.if_bandwidth_hz:g}")
|
||||
self._power_input.setText(f"{config.radar.sweep.power_dbm:g}")
|
||||
self._settling_ms.setText(str(int(config.runtime.settling_ms)))
|
||||
self._set_radar_settings_mode(config.is_kamil_adc)
|
||||
|
||||
adc = config.radar.kamil_adc
|
||||
optical = config.radar.laser_control
|
||||
manual = optical.manual
|
||||
variation = optical.variation
|
||||
self._adc_project_dir_input.setText(str(adc.project_dir))
|
||||
self._adc_executable_path_input.setText(str(adc.executable_path))
|
||||
self._adc_tty_path_input.setText(str(adc.tty_path))
|
||||
self._adc_args_input.setPlainText("\n".join(adc.args))
|
||||
self._adc_env_input.setPlainText(json.dumps(adc.env, indent=2, sort_keys=True) if adc.env else "{}")
|
||||
self._adc_startup_timeout_s_input.setText(f"{float(adc.startup_timeout_s):g}")
|
||||
self._adc_sweep_timeout_s_input.setText(f"{float(adc.sweep_timeout_s):g}")
|
||||
self._adc_stop_timeout_s_input.setText(f"{float(adc.stop_timeout_s):g}")
|
||||
self._optical_enabled_checkbox.setChecked(bool(optical.enabled))
|
||||
self._optical_port_input.setText(str(optical.port))
|
||||
self._set_combo_current_text(self._optical_mode_combo, str(optical.mode))
|
||||
self._optical_pi_coeff1_p_input.setText(str(int(optical.pi_coeff1_p)))
|
||||
self._optical_pi_coeff1_i_input.setText(str(int(optical.pi_coeff1_i)))
|
||||
self._optical_pi_coeff2_p_input.setText(str(int(optical.pi_coeff2_p)))
|
||||
self._optical_pi_coeff2_i_input.setText(str(int(optical.pi_coeff2_i)))
|
||||
self._optical_manual_temp1_input.setText(f"{float(manual.temp1):g}")
|
||||
self._optical_manual_temp2_input.setText(f"{float(manual.temp2):g}")
|
||||
self._optical_manual_current1_input.setText(f"{float(manual.current1):g}")
|
||||
self._optical_manual_current2_input.setText(f"{float(manual.current2):g}")
|
||||
self._set_combo_current_text(self._optical_variation_type_combo, str(variation.variation_type))
|
||||
self._optical_static_temp1_input.setText(f"{float(variation.static_temp1):g}")
|
||||
self._optical_static_temp2_input.setText(f"{float(variation.static_temp2):g}")
|
||||
self._optical_static_current1_input.setText(f"{float(variation.static_current1):g}")
|
||||
self._optical_static_current2_input.setText(f"{float(variation.static_current2):g}")
|
||||
self._optical_min_value_input.setText(f"{float(variation.min_value):g}")
|
||||
self._optical_max_value_input.setText(f"{float(variation.max_value):g}")
|
||||
self._optical_step_input.setText(f"{float(variation.step):g}")
|
||||
self._optical_time_step_input.setText(str(int(variation.time_step)))
|
||||
self._optical_delay_time_input.setText(str(int(variation.delay_time)))
|
||||
self._sync_adc_settings_controls()
|
||||
|
||||
self._combos_text.setText(str(gui_state.switches.combos_text))
|
||||
self._single_combo_output.setText(str(gui_state.switches.single_output))
|
||||
@@ -289,6 +409,11 @@ class AppWindowConfigProfileIOMixin:
|
||||
self._gpr_max_depth_m.setValue(float(gui_state.processing.gpr.max_depth_m))
|
||||
self._gpr_range_comp_power.setValue(float(gui_state.processing.gpr.range_comp_power))
|
||||
self._gpr_angle_comp_power.setValue(float(gui_state.processing.gpr.angle_comp_power))
|
||||
self._set_combo_current_text(self._gpr_score_mode, gui_state.processing.gpr.score_mode)
|
||||
self._gpr_max_detected_objects_to_draw.setValue(
|
||||
int(gui_state.processing.gpr.max_detected_objects_to_draw)
|
||||
)
|
||||
self._gpr_draw_top_m_objects.setValue(int(gui_state.processing.gpr.draw_top_m_objects))
|
||||
self._gpr_start_freq_mhz.setValue(float(gui_state.processing.gpr.start_freq_mhz))
|
||||
self._gpr_stop_freq_mhz.setValue(float(gui_state.processing.gpr.stop_freq_mhz))
|
||||
self._gpr_background_subtract_enabled.setChecked(
|
||||
@@ -316,6 +441,9 @@ 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)
|
||||
)
|
||||
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)
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
|
||||
from python_app.models.gui_profile_model import (
|
||||
GuiBscanStateModel,
|
||||
GuiDataActionsStateModel,
|
||||
@@ -42,6 +44,27 @@ class AppWindowConfigStateBuildersMixin:
|
||||
values.append(int(token))
|
||||
return values
|
||||
|
||||
@staticmethod
|
||||
def _parse_multiline_string_list(text: str) -> list[str]:
|
||||
"""Parse one command argument per non-empty line."""
|
||||
return [line.strip() for line in text.splitlines() if line.strip()]
|
||||
|
||||
@staticmethod
|
||||
def _parse_string_env_json(text: str) -> dict[str, str]:
|
||||
"""Parse strict string-to-string environment JSON."""
|
||||
cleaned = text.strip()
|
||||
if not cleaned:
|
||||
return {}
|
||||
payload = json.loads(cleaned)
|
||||
if not isinstance(payload, dict):
|
||||
raise ValueError("Environment JSON must be an object")
|
||||
env: dict[str, str] = {}
|
||||
for key, value in payload.items():
|
||||
if not isinstance(key, str) or not isinstance(value, str):
|
||||
raise ValueError("Environment JSON must contain only string keys and values")
|
||||
env[key] = value
|
||||
return env
|
||||
|
||||
@staticmethod
|
||||
def _parse_gpr_tx_geometry_text(text: str) -> list[GprTxGeometryModel]:
|
||||
"""Parse line-based Tx geometry editor text."""
|
||||
@@ -172,6 +195,9 @@ class AppWindowConfigStateBuildersMixin:
|
||||
max_depth_m=14.0,
|
||||
range_comp_power=0.28,
|
||||
angle_comp_power=0.10,
|
||||
score_mode="combined",
|
||||
max_detected_objects_to_draw=5,
|
||||
draw_top_m_objects=2,
|
||||
start_freq_mhz=3000.0,
|
||||
stop_freq_mhz=6000.0,
|
||||
background_subtract_enabled=True,
|
||||
@@ -193,6 +219,7 @@ class AppWindowConfigStateBuildersMixin:
|
||||
start_freq_mhz=3000.0,
|
||||
stop_freq_mhz=6000.0,
|
||||
speed_m_s=0.0,
|
||||
ignore_socket_speed_enabled=False,
|
||||
look_angle_deg=0.0,
|
||||
snr_thresh=4.5,
|
||||
snr_comp_max=25.0,
|
||||
@@ -280,6 +307,9 @@ class AppWindowConfigStateBuildersMixin:
|
||||
max_depth_m=float(self._gpr_max_depth_m.value()),
|
||||
range_comp_power=float(self._gpr_range_comp_power.value()),
|
||||
angle_comp_power=float(self._gpr_angle_comp_power.value()),
|
||||
score_mode=self._gpr_score_mode.currentText(),
|
||||
max_detected_objects_to_draw=int(self._gpr_max_detected_objects_to_draw.value()),
|
||||
draw_top_m_objects=int(self._gpr_draw_top_m_objects.value()),
|
||||
start_freq_mhz=float(self._gpr_start_freq_mhz.value()),
|
||||
stop_freq_mhz=float(self._gpr_stop_freq_mhz.value()),
|
||||
background_subtract_enabled=bool(self._gpr_background_subtract_enabled.isChecked()),
|
||||
@@ -302,6 +332,7 @@ class AppWindowConfigStateBuildersMixin:
|
||||
start_freq_mhz=float(self._legacy_gpr_start_freq_mhz.value()),
|
||||
stop_freq_mhz=float(self._legacy_gpr_stop_freq_mhz.value()),
|
||||
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()),
|
||||
snr_thresh=float(self._legacy_gpr_snr_thresh.value()),
|
||||
snr_comp_max=float(self._legacy_gpr_snr_comp_max.value()),
|
||||
@@ -343,6 +374,8 @@ class AppWindowConfigStateBuildersMixin:
|
||||
config.radar.sweep.points = int(self._points_input.text().strip())
|
||||
config.radar.sweep.if_bandwidth_hz = float(self._ifbw_input.text().strip())
|
||||
config.radar.sweep.power_dbm = float(self._power_input.text().strip())
|
||||
if config.is_kamil_adc:
|
||||
self._apply_adc_settings_to_config(config)
|
||||
|
||||
config.runtime.settling_ms = int(self._settling_ms.text().strip())
|
||||
config.runtime.processing_live_config_path = str(self._live_config_writer.path)
|
||||
@@ -396,6 +429,16 @@ class AppWindowConfigStateBuildersMixin:
|
||||
def _radar_key_from_ui(self) -> str:
|
||||
"""Build current radar key directly from radar widgets only."""
|
||||
model_name = self._defaults_config.radar.model or RunConfigModel.LIBREVNA_MODEL
|
||||
extra_parts = self._defaults_config.radar_key_extra_parts()
|
||||
if self._defaults_config.is_kamil_adc:
|
||||
config = self._defaults_config.clone()
|
||||
config.radar.sweep.start_hz = float(self._start_hz_input.text().strip())
|
||||
config.radar.sweep.stop_hz = float(self._stop_hz_input.text().strip())
|
||||
config.radar.sweep.points = int(self._points_input.text().strip())
|
||||
config.radar.sweep.if_bandwidth_hz = float(self._ifbw_input.text().strip())
|
||||
config.radar.sweep.power_dbm = float(self._power_input.text().strip())
|
||||
self._apply_adc_settings_to_config(config)
|
||||
extra_parts = config.radar_key_extra_parts()
|
||||
return radar_key_from_config(
|
||||
model_name=model_name,
|
||||
serial=self._defaults_config.radar.serial,
|
||||
@@ -404,9 +447,46 @@ class AppWindowConfigStateBuildersMixin:
|
||||
sweep_points=int(self._points_input.text().strip()),
|
||||
ifbw_hz=float(self._ifbw_input.text().strip()),
|
||||
power_dbm=float(self._power_input.text().strip()),
|
||||
extra_serials=self._defaults_config.radar_key_extra_parts() or None,
|
||||
extra_serials=extra_parts or None,
|
||||
)
|
||||
|
||||
def _apply_adc_settings_to_config(self, config: RunConfigModel) -> None:
|
||||
"""Copy visible ADC collector and optical-board settings into config."""
|
||||
adc = config.radar.kamil_adc
|
||||
adc.project_dir = self._adc_project_dir_input.text().strip()
|
||||
adc.executable_path = self._adc_executable_path_input.text().strip()
|
||||
adc.tty_path = self._adc_tty_path_input.text().strip()
|
||||
adc.args = self._parse_multiline_string_list(self._adc_args_input.toPlainText())
|
||||
adc.env = self._parse_string_env_json(self._adc_env_input.toPlainText())
|
||||
adc.startup_timeout_s = float(self._adc_startup_timeout_s_input.text().strip())
|
||||
adc.sweep_timeout_s = float(self._adc_sweep_timeout_s_input.text().strip())
|
||||
adc.stop_timeout_s = float(self._adc_stop_timeout_s_input.text().strip())
|
||||
|
||||
optical = config.radar.laser_control
|
||||
optical.enabled = bool(self._optical_enabled_checkbox.isChecked())
|
||||
optical.port = self._optical_port_input.text().strip()
|
||||
optical.mode = self._optical_mode_combo.currentText()
|
||||
optical.pi_coeff1_p = int(self._optical_pi_coeff1_p_input.text().strip())
|
||||
optical.pi_coeff1_i = int(self._optical_pi_coeff1_i_input.text().strip())
|
||||
optical.pi_coeff2_p = int(self._optical_pi_coeff2_p_input.text().strip())
|
||||
optical.pi_coeff2_i = int(self._optical_pi_coeff2_i_input.text().strip())
|
||||
|
||||
optical.manual.temp1 = float(self._optical_manual_temp1_input.text().strip())
|
||||
optical.manual.temp2 = float(self._optical_manual_temp2_input.text().strip())
|
||||
optical.manual.current1 = float(self._optical_manual_current1_input.text().strip())
|
||||
optical.manual.current2 = float(self._optical_manual_current2_input.text().strip())
|
||||
|
||||
optical.variation.variation_type = self._optical_variation_type_combo.currentText()
|
||||
optical.variation.static_temp1 = float(self._optical_static_temp1_input.text().strip())
|
||||
optical.variation.static_temp2 = float(self._optical_static_temp2_input.text().strip())
|
||||
optical.variation.static_current1 = float(self._optical_static_current1_input.text().strip())
|
||||
optical.variation.static_current2 = float(self._optical_static_current2_input.text().strip())
|
||||
optical.variation.min_value = float(self._optical_min_value_input.text().strip())
|
||||
optical.variation.max_value = float(self._optical_max_value_input.text().strip())
|
||||
optical.variation.step = float(self._optical_step_input.text().strip())
|
||||
optical.variation.time_step = int(self._optical_time_step_input.text().strip())
|
||||
optical.variation.delay_time = int(self._optical_delay_time_input.text().strip())
|
||||
|
||||
@staticmethod
|
||||
def _switches_are_effectively_static(config: RunConfigModel) -> bool:
|
||||
"""Return `True` when non-GPR switch setup effectively yields one fixed combo."""
|
||||
|
||||
@@ -8,6 +8,7 @@ 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
|
||||
from python_app.hardware_full.kamil_adc_service import apply_kamil_adc_laser_control
|
||||
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
|
||||
@@ -86,7 +87,7 @@ class AppWindowPipelineMixin:
|
||||
self._config_writer.prepare_preprocess_bundles(self._store, radar_key, config)
|
||||
config.runtime.continuous = not single_capture
|
||||
|
||||
if not single_capture:
|
||||
if not single_capture and not config.is_kamil_adc:
|
||||
self._prepare_radar_for_native_acquisition(config)
|
||||
|
||||
config_path = self._config_writer.write(config, self._project_root / "python_app/runtime/run_config.json")
|
||||
@@ -168,11 +169,16 @@ class AppWindowPipelineMixin:
|
||||
"matches the current config"
|
||||
)
|
||||
self._prepare_radar_for_native_acquisition(config)
|
||||
points_text = (
|
||||
"points=from Kamil ADC stream"
|
||||
if config.is_kamil_adc
|
||||
else f"points={config.radar.sweep.points}"
|
||||
)
|
||||
self._log(
|
||||
"Radar settings applied: "
|
||||
f"start={config.radar.sweep.start_hz:g} Hz, "
|
||||
f"stop={config.radar.sweep.stop_hz:g} Hz, "
|
||||
f"points={config.radar.sweep.points}, "
|
||||
f"{points_text}, "
|
||||
f"ifbw={config.radar.sweep.if_bandwidth_hz:g} Hz, "
|
||||
f"power={config.radar.sweep.power_dbm:g} dBm"
|
||||
)
|
||||
@@ -193,7 +199,10 @@ class AppWindowPipelineMixin:
|
||||
self._log("Multi-device raw producer will configure all LibreVNA devices")
|
||||
return
|
||||
if config.is_kamil_adc:
|
||||
self._log("Kamil ADC raw producer will apply laser_control and start the external collector")
|
||||
if apply_kamil_adc_laser_control(config):
|
||||
self._log("Kamil ADC laser_control applied via Apply Radar")
|
||||
else:
|
||||
self._log("Kamil ADC laser_control skipped because it is disabled")
|
||||
return
|
||||
|
||||
radar_service = create_single_radar_service(config)
|
||||
@@ -272,6 +281,7 @@ class AppWindowPipelineMixin:
|
||||
|
||||
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()
|
||||
@@ -483,6 +493,8 @@ class AppWindowPipelineMixin:
|
||||
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):
|
||||
@@ -491,7 +503,14 @@ class AppWindowPipelineMixin:
|
||||
if current_speed_m_s == previous_speed_m_s:
|
||||
return
|
||||
|
||||
self._write_live_processing_config()
|
||||
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."""
|
||||
@@ -501,6 +520,7 @@ class AppWindowPipelineMixin:
|
||||
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:
|
||||
|
||||
@@ -254,7 +254,15 @@ class AppWindowGprPlotMixin:
|
||||
|
||||
points_payload = self._collection_payload_by_name(collection, "gpr_points", kind=4)
|
||||
if points_payload is not None and np.asarray(points_payload.table).size > 0:
|
||||
points = np.asarray(points_payload.table, dtype=np.float32)
|
||||
points = (
|
||||
self._filtered_gpr_object_rows(collection)
|
||||
if self._processing_mode.currentText() == "gpr"
|
||||
else np.asarray(points_payload.table, dtype=np.float32)
|
||||
)
|
||||
else:
|
||||
points = np.zeros((0, 3), dtype=np.float32)
|
||||
|
||||
if points.size > 0:
|
||||
self._gpr_points_item.setData(
|
||||
x=points[:, 0],
|
||||
y=points[:, 1],
|
||||
@@ -368,6 +376,26 @@ class AppWindowGprPlotMixin:
|
||||
return float(self._legacy_gpr_min_visible_pair_count.value())
|
||||
return float(self._gpr_min_visible_score.value())
|
||||
|
||||
def _gpr_draw_limits(self) -> tuple[int, int] | None:
|
||||
"""Return GPR object draw limits, or None for legacy GPR."""
|
||||
if self._processing_mode.currentText() == "legacy_gpr":
|
||||
return None
|
||||
return (
|
||||
int(self._gpr_max_detected_objects_to_draw.value()),
|
||||
int(self._gpr_draw_top_m_objects.value()),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _apply_object_draw_limits(rows: np.ndarray, limits: tuple[int, int] | None) -> np.ndarray:
|
||||
"""Apply object count/top-M drawing rules to already-filtered rows."""
|
||||
if limits is None or rows.size == 0:
|
||||
return rows
|
||||
|
||||
max_detected_objects, draw_top_objects = limits
|
||||
if rows.shape[0] > int(max_detected_objects):
|
||||
return np.zeros((0, rows.shape[1]), dtype=rows.dtype)
|
||||
return rows[: max(0, int(draw_top_objects))]
|
||||
|
||||
@staticmethod
|
||||
def _gpr_display_y_min(z_min: float, z_max: float) -> float:
|
||||
"""Return lower display bound, preserving surface markers only when surface is visible."""
|
||||
@@ -483,7 +511,7 @@ class AppWindowGprPlotMixin:
|
||||
return extract_gpr_object_rows(collection)
|
||||
|
||||
def _filtered_gpr_object_rows(self, collection: ResultCollection) -> np.ndarray:
|
||||
"""Return object rows filtered by minimum pair count and visible X/Z bounds."""
|
||||
"""Return object rows filtered by threshold, visible X/Z bounds, and active GPR draw limits."""
|
||||
rows = self._gpr_object_rows(collection)
|
||||
if rows.size == 0:
|
||||
return rows
|
||||
@@ -499,7 +527,7 @@ class AppWindowGprPlotMixin:
|
||||
& (rows[:, 1] >= z_min)
|
||||
& (rows[:, 1] <= z_max)
|
||||
)
|
||||
return rows[visible_mask]
|
||||
return self._apply_object_draw_limits(rows[visible_mask], self._gpr_draw_limits())
|
||||
|
||||
def _draw_gpr_objects_only(self, collection: ResultCollection) -> bool:
|
||||
"""Draw only detected GPR objects inside configured X/Z bounds."""
|
||||
|
||||
@@ -3,12 +3,14 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from python_app.gui.preprocess_dialog import PreprocessDialog
|
||||
from python_app.hardware_full.kamil_adc_service import KamilAdcService
|
||||
from python_app.orchestration.preprocess_assets import (
|
||||
PREPROCESS_ASSET_SPECS,
|
||||
VISIBLE_PREPROCESS_ASSET_KEYS,
|
||||
preprocess_asset_channel,
|
||||
preprocess_asset_display_name,
|
||||
)
|
||||
from python_app.workflows.kamil_adc_neutral_preprocess import build_kamil_adc_neutral_s21_sets
|
||||
from python_app.workflows.multi_radar_capture_workflow import (
|
||||
MultiRadarCaptureBatch,
|
||||
MultiRadarSequentialCaptureSession,
|
||||
@@ -185,6 +187,8 @@ class AppWindowPreprocessMixin:
|
||||
dialog.undo_last_requested.connect(self._undo_last_capture)
|
||||
dialog.finalize_sequence_requested.connect(self._finalize_capture_sequence)
|
||||
dialog.abort_sequence_requested.connect(self._abort_capture_sequence)
|
||||
dialog.create_kamil_adc_neutral_sets_requested.connect(self._create_kamil_adc_neutral_sets)
|
||||
dialog.set_kamil_adc_neutral_sets_visible(self._defaults_config.is_kamil_adc)
|
||||
dialog.set_radar_config_summary(
|
||||
directory_path=self._preprocess_radar_scan_summary.directory_path,
|
||||
json_file_count=self._preprocess_radar_scan_summary.json_file_count,
|
||||
@@ -254,6 +258,7 @@ class AppWindowPreprocessMixin:
|
||||
f"{preprocess_asset_display_name(key)}={len(names)}"
|
||||
for key, names in available_sets.items()
|
||||
)
|
||||
dialog.set_kamil_adc_neutral_sets_visible(self._defaults_config.is_kamil_adc)
|
||||
self._log(f"Preprocess set lists refreshed: radar_key={radar_key}, {available_counts}")
|
||||
if unavailable_selections:
|
||||
self._log_warning(
|
||||
@@ -338,6 +343,88 @@ class AppWindowPreprocessMixin:
|
||||
self._show_exception(f"Failed to start {kind} sequence", exc)
|
||||
self._resume_pipeline_if_needed()
|
||||
|
||||
def _create_kamil_adc_neutral_sets(self) -> None:
|
||||
"""Save neutral S21 calibration/reference sets for the current Kamil ADC settings."""
|
||||
if self._capture_session is not None:
|
||||
self._show_error(
|
||||
"Cannot create neutral sets during active capture sequence",
|
||||
details=self._capture_state_details(),
|
||||
)
|
||||
return
|
||||
|
||||
dialog = self._ensure_preprocess_dialog()
|
||||
set_name = dialog.set_name()
|
||||
if not set_name:
|
||||
self._show_error("Set name is required")
|
||||
return
|
||||
|
||||
pipeline_was_paused = False
|
||||
try:
|
||||
config = self._build_config()
|
||||
if not config.is_kamil_adc:
|
||||
self._show_error("Neutral S21 sets are available only for kamil_adc")
|
||||
return
|
||||
|
||||
radar_key = self._radar_key(config)
|
||||
duplicate_assets = [
|
||||
preprocess_asset_display_name(key)
|
||||
for key in ("s21_calibration", "s21_reference")
|
||||
if set_name in self._store.list_sets(PREPROCESS_ASSET_SPECS[key].set_kind, radar_key)
|
||||
]
|
||||
if duplicate_assets:
|
||||
raise RuntimeError(
|
||||
f"Set '{set_name}' already exists for: " + ", ".join(duplicate_assets)
|
||||
)
|
||||
|
||||
if self._supervisor.is_running():
|
||||
self._log("Pipeline paused for Kamil ADC neutral-set creation")
|
||||
self._stop_run()
|
||||
pipeline_was_paused = True
|
||||
|
||||
point_count = self._read_kamil_adc_point_count(config)
|
||||
calibration, reference = build_kamil_adc_neutral_s21_sets(config, point_count)
|
||||
self._store.save_set("s21_calibration", radar_key, set_name, calibration)
|
||||
self._store.save_set("s21_reference", radar_key, set_name, reference)
|
||||
|
||||
self._selected_preprocess_sets["s21_calibration"] = set_name
|
||||
self._selected_preprocess_sets["s21_reference"] = set_name
|
||||
self._selected_preprocess_radar_key = radar_key
|
||||
self._processor_run_signature = None
|
||||
self._history_run_signature = None
|
||||
self._reset_runtime_history()
|
||||
self._refresh_sets()
|
||||
dialog.set_status(
|
||||
f"Neutral S21 sets saved: {set_name} ({len(calibration.traces)} combos, {point_count} points)"
|
||||
)
|
||||
self._log(
|
||||
"Kamil ADC neutral S21 sets saved: "
|
||||
f"set={set_name}, radar_key={radar_key}, combos={len(calibration.traces)}, points={point_count}"
|
||||
)
|
||||
except Exception as exc: # noqa: BLE001
|
||||
self._show_exception("Failed to create Kamil ADC neutral sets", exc)
|
||||
finally:
|
||||
if pipeline_was_paused:
|
||||
self._start_run()
|
||||
|
||||
def _read_kamil_adc_point_count(self, config) -> int:
|
||||
"""Read one Kamil ADC sweep and return its actual point count."""
|
||||
dialog = self._ensure_preprocess_dialog()
|
||||
dialog.set_status("Reading one Kamil ADC sweep to detect point count...")
|
||||
self._log("Reading one Kamil ADC sweep to detect neutral-set point count")
|
||||
|
||||
radar = KamilAdcService(config)
|
||||
try:
|
||||
radar.open()
|
||||
radar.configure(config.radar.sweep)
|
||||
sweep = radar.acquire()
|
||||
finally:
|
||||
radar.close()
|
||||
|
||||
point_count = int(sweep.x.size)
|
||||
if point_count <= 0:
|
||||
raise RuntimeError("Kamil ADC returned an empty sweep while detecting point count")
|
||||
return point_count
|
||||
|
||||
def _build_single_radar_capture_session(
|
||||
self,
|
||||
*,
|
||||
|
||||
@@ -209,6 +209,18 @@ def build_processing_group(owner) -> QGroupBox:
|
||||
owner._gpr_angle_comp_power.setSingleStep(0.01)
|
||||
owner._gpr_angle_comp_power.setValue(float(gpr_live_defaults.angle_comp_power))
|
||||
|
||||
owner._gpr_score_mode = QComboBox()
|
||||
owner._gpr_score_mode.addItems(["peak", "combined"])
|
||||
owner._set_combo_current_text(owner._gpr_score_mode, gpr_live_defaults.score_mode)
|
||||
|
||||
owner._gpr_max_detected_objects_to_draw = QSpinBox()
|
||||
owner._gpr_max_detected_objects_to_draw.setRange(0, 10_000)
|
||||
owner._gpr_max_detected_objects_to_draw.setValue(int(gpr_live_defaults.max_detected_objects_to_draw))
|
||||
|
||||
owner._gpr_draw_top_m_objects = QSpinBox()
|
||||
owner._gpr_draw_top_m_objects.setRange(0, 10_000)
|
||||
owner._gpr_draw_top_m_objects.setValue(int(gpr_live_defaults.draw_top_m_objects))
|
||||
|
||||
owner._gpr_start_freq_mhz = QDoubleSpinBox()
|
||||
owner._gpr_start_freq_mhz.setDecimals(1)
|
||||
owner._gpr_start_freq_mhz.setRange(100.0, 8800.0)
|
||||
@@ -274,8 +286,11 @@ def build_processing_group(owner) -> QGroupBox:
|
||||
("Max depth m", owner._gpr_max_depth_m),
|
||||
("Range comp power", owner._gpr_range_comp_power),
|
||||
("Angle comp power", owner._gpr_angle_comp_power),
|
||||
("Score mode", owner._gpr_score_mode),
|
||||
("Render mode", owner._gpr_render_mode),
|
||||
("Min visible score", owner._gpr_min_visible_score),
|
||||
("Max detected objects", owner._gpr_max_detected_objects_to_draw),
|
||||
("Draw top M objects", owner._gpr_draw_top_m_objects),
|
||||
("Start MHz", owner._gpr_start_freq_mhz),
|
||||
("Stop MHz", owner._gpr_stop_freq_mhz),
|
||||
("Visible X min m", owner._gpr_visible_x_min_m),
|
||||
@@ -348,6 +363,11 @@ def build_processing_group(owner) -> QGroupBox:
|
||||
owner._legacy_gpr_speed_m_s.setSingleStep(0.01)
|
||||
owner._legacy_gpr_speed_m_s.setValue(float(legacy_gpr_defaults.speed_m_s))
|
||||
|
||||
owner._legacy_gpr_ignore_socket_speed_enabled = QCheckBox("Ignore socket speed")
|
||||
owner._legacy_gpr_ignore_socket_speed_enabled.setChecked(
|
||||
bool(legacy_gpr_defaults.ignore_socket_speed_enabled)
|
||||
)
|
||||
|
||||
owner._legacy_gpr_look_angle_deg = QDoubleSpinBox()
|
||||
owner._legacy_gpr_look_angle_deg.setDecimals(2)
|
||||
owner._legacy_gpr_look_angle_deg.setRange(-90.0, 90.0)
|
||||
@@ -405,6 +425,7 @@ def build_processing_group(owner) -> QGroupBox:
|
||||
("SNR thresh", owner._legacy_gpr_snr_thresh),
|
||||
("SNR comp max", owner._legacy_gpr_snr_comp_max),
|
||||
("Speed m/s", owner._legacy_gpr_speed_m_s),
|
||||
owner._legacy_gpr_ignore_socket_speed_enabled,
|
||||
("Render mode", owner._legacy_gpr_render_mode),
|
||||
("Min visible pairs", owner._legacy_gpr_min_visible_pair_count),
|
||||
("Start MHz", owner._legacy_gpr_start_freq_mhz),
|
||||
@@ -445,6 +466,7 @@ def build_processing_group(owner) -> QGroupBox:
|
||||
owner._gpr_max_depth_m.valueChanged.connect(owner._on_processing_live_settings_changed)
|
||||
owner._gpr_range_comp_power.valueChanged.connect(owner._on_processing_live_settings_changed)
|
||||
owner._gpr_angle_comp_power.valueChanged.connect(owner._on_processing_live_settings_changed)
|
||||
owner._gpr_score_mode.currentTextChanged.connect(owner._on_processing_live_settings_changed)
|
||||
owner._gpr_start_freq_mhz.valueChanged.connect(owner._on_processing_live_settings_changed)
|
||||
owner._gpr_stop_freq_mhz.valueChanged.connect(owner._on_processing_live_settings_changed)
|
||||
owner._gpr_background_subtract_enabled.toggled.connect(owner._on_processing_live_settings_changed)
|
||||
@@ -452,6 +474,8 @@ def build_processing_group(owner) -> QGroupBox:
|
||||
owner._gpr_remove_sidelobe_objects_enabled.toggled.connect(owner._on_processing_live_settings_changed)
|
||||
owner._gpr_render_mode.currentTextChanged.connect(owner._on_gpr_visual_settings_changed)
|
||||
owner._gpr_min_visible_score.valueChanged.connect(owner._on_gpr_locator_threshold_changed)
|
||||
owner._gpr_max_detected_objects_to_draw.valueChanged.connect(owner._on_gpr_locator_threshold_changed)
|
||||
owner._gpr_draw_top_m_objects.valueChanged.connect(owner._on_gpr_locator_threshold_changed)
|
||||
owner._gpr_visible_x_min_m.valueChanged.connect(owner._on_gpr_locator_window_changed)
|
||||
owner._gpr_visible_x_max_m.valueChanged.connect(owner._on_gpr_locator_window_changed)
|
||||
owner._gpr_visible_z_min_m.valueChanged.connect(owner._on_gpr_locator_window_changed)
|
||||
|
||||
@@ -2,7 +2,18 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from PyQt6.QtWidgets import QGroupBox, QLabel, QLineEdit, QVBoxLayout
|
||||
import json
|
||||
|
||||
from PyQt6.QtWidgets import (
|
||||
QCheckBox,
|
||||
QComboBox,
|
||||
QGroupBox,
|
||||
QLabel,
|
||||
QLineEdit,
|
||||
QPlainTextEdit,
|
||||
QVBoxLayout,
|
||||
QWidget,
|
||||
)
|
||||
|
||||
from python_app.gui.controllers.sections.layout_helpers import build_two_column_form_widget
|
||||
|
||||
@@ -15,6 +26,11 @@ def build_radar_group(owner) -> QGroupBox:
|
||||
layout.setSpacing(8)
|
||||
defaults = owner._defaults_config.radar
|
||||
|
||||
owner._vna_radar_settings_panel = QWidget(group)
|
||||
vna_layout = QVBoxLayout(owner._vna_radar_settings_panel)
|
||||
vna_layout.setContentsMargins(0, 0, 0, 0)
|
||||
vna_layout.setSpacing(8)
|
||||
|
||||
owner._start_hz_input = QLineEdit(f"{defaults.sweep.start_hz:g}")
|
||||
owner._stop_hz_input = QLineEdit(f"{defaults.sweep.stop_hz:g}")
|
||||
owner._points_input = QLineEdit(str(defaults.sweep.points))
|
||||
@@ -36,7 +52,7 @@ def build_radar_group(owner) -> QGroupBox:
|
||||
owner._radar_limits_hint = QLabel("Device limits are not available.")
|
||||
owner._radar_limits_hint.setObjectName("hintLabel")
|
||||
|
||||
layout.addWidget(
|
||||
vna_layout.addWidget(
|
||||
build_two_column_form_widget(
|
||||
group,
|
||||
[
|
||||
@@ -48,5 +64,174 @@ def build_radar_group(owner) -> QGroupBox:
|
||||
],
|
||||
)
|
||||
)
|
||||
layout.addWidget(owner._radar_limits_hint)
|
||||
vna_layout.addWidget(owner._radar_limits_hint)
|
||||
|
||||
owner._adc_radar_settings_panel = _build_adc_settings_panel(owner, group)
|
||||
|
||||
layout.addWidget(owner._vna_radar_settings_panel)
|
||||
layout.addWidget(owner._adc_radar_settings_panel)
|
||||
owner._set_radar_settings_mode(owner._defaults_config.is_kamil_adc)
|
||||
return group
|
||||
|
||||
|
||||
def _build_adc_settings_panel(owner, parent: QWidget) -> QWidget:
|
||||
"""Create controls for the external ADC collector and optical board."""
|
||||
panel = QWidget(parent)
|
||||
layout = QVBoxLayout(panel)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setSpacing(8)
|
||||
|
||||
adc = owner._defaults_config.radar.kamil_adc
|
||||
optical = owner._defaults_config.radar.laser_control
|
||||
manual = optical.manual
|
||||
variation = optical.variation
|
||||
|
||||
owner._adc_project_dir_input = QLineEdit(adc.project_dir)
|
||||
owner._adc_executable_path_input = QLineEdit(adc.executable_path)
|
||||
owner._adc_tty_path_input = QLineEdit(adc.tty_path)
|
||||
owner._adc_args_input = _plain_text("\n".join(adc.args))
|
||||
owner._adc_env_input = _plain_text(json.dumps(adc.env, indent=2, sort_keys=True) if adc.env else "{}")
|
||||
owner._adc_startup_timeout_s_input = QLineEdit(f"{adc.startup_timeout_s:g}")
|
||||
owner._adc_sweep_timeout_s_input = QLineEdit(f"{adc.sweep_timeout_s:g}")
|
||||
owner._adc_stop_timeout_s_input = QLineEdit(f"{adc.stop_timeout_s:g}")
|
||||
|
||||
owner._optical_enabled_checkbox = QCheckBox()
|
||||
owner._optical_enabled_checkbox.setChecked(bool(optical.enabled))
|
||||
owner._optical_port_input = QLineEdit(optical.port)
|
||||
owner._optical_mode_combo = QComboBox()
|
||||
owner._optical_mode_combo.addItems(["manual", "variation"])
|
||||
owner._set_combo_current_text(owner._optical_mode_combo, optical.mode)
|
||||
owner._optical_pi_coeff1_p_input = QLineEdit(str(optical.pi_coeff1_p))
|
||||
owner._optical_pi_coeff1_i_input = QLineEdit(str(optical.pi_coeff1_i))
|
||||
owner._optical_pi_coeff2_p_input = QLineEdit(str(optical.pi_coeff2_p))
|
||||
owner._optical_pi_coeff2_i_input = QLineEdit(str(optical.pi_coeff2_i))
|
||||
|
||||
owner._optical_manual_temp1_input = QLineEdit(f"{manual.temp1:g}")
|
||||
owner._optical_manual_temp2_input = QLineEdit(f"{manual.temp2:g}")
|
||||
owner._optical_manual_current1_input = QLineEdit(f"{manual.current1:g}")
|
||||
owner._optical_manual_current2_input = QLineEdit(f"{manual.current2:g}")
|
||||
|
||||
owner._optical_variation_type_combo = QComboBox()
|
||||
owner._optical_variation_type_combo.addItems(
|
||||
[
|
||||
"CHANGE_CURRENT_LD1",
|
||||
"CHANGE_CURRENT_LD2",
|
||||
"CHANGE_TEMPERATURE_LD1",
|
||||
"CHANGE_TEMPERATURE_LD2",
|
||||
]
|
||||
)
|
||||
owner._set_combo_current_text(owner._optical_variation_type_combo, variation.variation_type)
|
||||
owner._optical_static_temp1_input = QLineEdit(f"{variation.static_temp1:g}")
|
||||
owner._optical_static_temp2_input = QLineEdit(f"{variation.static_temp2:g}")
|
||||
owner._optical_static_current1_input = QLineEdit(f"{variation.static_current1:g}")
|
||||
owner._optical_static_current2_input = QLineEdit(f"{variation.static_current2:g}")
|
||||
owner._optical_min_value_input = QLineEdit(f"{variation.min_value:g}")
|
||||
owner._optical_max_value_input = QLineEdit(f"{variation.max_value:g}")
|
||||
owner._optical_step_input = QLineEdit(f"{variation.step:g}")
|
||||
owner._optical_time_step_input = QLineEdit(str(variation.time_step))
|
||||
owner._optical_delay_time_input = QLineEdit(str(variation.delay_time))
|
||||
|
||||
layout.addWidget(
|
||||
build_two_column_form_widget(
|
||||
panel,
|
||||
[
|
||||
("Project dir", owner._adc_project_dir_input),
|
||||
("Executable path", owner._adc_executable_path_input),
|
||||
("TTY path", owner._adc_tty_path_input),
|
||||
("Arguments", owner._adc_args_input),
|
||||
("Environment JSON", owner._adc_env_input),
|
||||
("Startup timeout s", owner._adc_startup_timeout_s_input),
|
||||
("Read timeout s", owner._adc_sweep_timeout_s_input),
|
||||
("Stop timeout s", owner._adc_stop_timeout_s_input),
|
||||
("Board enabled", owner._optical_enabled_checkbox),
|
||||
("Board port", owner._optical_port_input),
|
||||
("Board mode", owner._optical_mode_combo),
|
||||
("PI 1 P", owner._optical_pi_coeff1_p_input),
|
||||
("PI 1 I", owner._optical_pi_coeff1_i_input),
|
||||
("PI 2 P", owner._optical_pi_coeff2_p_input),
|
||||
("PI 2 I", owner._optical_pi_coeff2_i_input),
|
||||
],
|
||||
split_index=8,
|
||||
)
|
||||
)
|
||||
|
||||
owner._optical_manual_panel = build_two_column_form_widget(
|
||||
panel,
|
||||
[
|
||||
("Temperature 1", owner._optical_manual_temp1_input),
|
||||
("Temperature 2", owner._optical_manual_temp2_input),
|
||||
("Current 1", owner._optical_manual_current1_input),
|
||||
("Current 2", owner._optical_manual_current2_input),
|
||||
],
|
||||
)
|
||||
owner._optical_variation_panel = build_two_column_form_widget(
|
||||
panel,
|
||||
[
|
||||
("Variation type", owner._optical_variation_type_combo),
|
||||
("Static temp 1", owner._optical_static_temp1_input),
|
||||
("Static temp 2", owner._optical_static_temp2_input),
|
||||
("Static current 1", owner._optical_static_current1_input),
|
||||
("Static current 2", owner._optical_static_current2_input),
|
||||
("Min value", owner._optical_min_value_input),
|
||||
("Max value", owner._optical_max_value_input),
|
||||
("Step", owner._optical_step_input),
|
||||
("Time step", owner._optical_time_step_input),
|
||||
("Delay time", owner._optical_delay_time_input),
|
||||
],
|
||||
split_index=5,
|
||||
)
|
||||
layout.addWidget(owner._optical_manual_panel)
|
||||
layout.addWidget(owner._optical_variation_panel)
|
||||
|
||||
_connect_adc_settings(owner)
|
||||
owner._sync_adc_settings_controls()
|
||||
return panel
|
||||
|
||||
|
||||
def _plain_text(text: str) -> QPlainTextEdit:
|
||||
widget = QPlainTextEdit(text)
|
||||
widget.setMaximumHeight(76)
|
||||
return widget
|
||||
|
||||
|
||||
def _connect_adc_settings(owner) -> None:
|
||||
text_widgets = [
|
||||
owner._adc_project_dir_input,
|
||||
owner._adc_executable_path_input,
|
||||
owner._adc_tty_path_input,
|
||||
owner._adc_startup_timeout_s_input,
|
||||
owner._adc_sweep_timeout_s_input,
|
||||
owner._adc_stop_timeout_s_input,
|
||||
owner._optical_port_input,
|
||||
owner._optical_pi_coeff1_p_input,
|
||||
owner._optical_pi_coeff1_i_input,
|
||||
owner._optical_pi_coeff2_p_input,
|
||||
owner._optical_pi_coeff2_i_input,
|
||||
owner._optical_manual_temp1_input,
|
||||
owner._optical_manual_temp2_input,
|
||||
owner._optical_manual_current1_input,
|
||||
owner._optical_manual_current2_input,
|
||||
owner._optical_static_temp1_input,
|
||||
owner._optical_static_temp2_input,
|
||||
owner._optical_static_current1_input,
|
||||
owner._optical_static_current2_input,
|
||||
owner._optical_min_value_input,
|
||||
owner._optical_max_value_input,
|
||||
owner._optical_step_input,
|
||||
owner._optical_time_step_input,
|
||||
owner._optical_delay_time_input,
|
||||
]
|
||||
for widget in text_widgets:
|
||||
widget.editingFinished.connect(owner._reset_preprocess_selection_after_radar_key_change)
|
||||
owner._adc_args_input.textChanged.connect(owner._reset_preprocess_selection_after_radar_key_change)
|
||||
owner._adc_env_input.textChanged.connect(owner._reset_preprocess_selection_after_radar_key_change)
|
||||
owner._optical_variation_type_combo.currentTextChanged.connect(
|
||||
owner._reset_preprocess_selection_after_radar_key_change
|
||||
)
|
||||
|
||||
def sync_and_reset() -> None:
|
||||
owner._sync_adc_settings_controls()
|
||||
owner._reset_preprocess_selection_after_radar_key_change()
|
||||
|
||||
owner._optical_enabled_checkbox.toggled.connect(sync_and_reset)
|
||||
owner._optical_mode_combo.currentTextChanged.connect(sync_and_reset)
|
||||
|
||||
Reference in New Issue
Block a user