Files
radar_system/python_app/models/gui_profile_codec.py
T
2026-06-23 12:19:31 +03:00

651 lines
28 KiB
Python

"""Encoding and decoding logic for :mod:`python_app.models.gui_profile_schema`."""
from __future__ import annotations
import logging
from typing import Any
from python_app.models.gui_profile_schema import (
GuiBscanStateModel,
GuiDataActionsStateModel,
GuiGprStateModel,
GuiLegacyGprStateModel,
GuiPassThroughStateModel,
GuiPreprocessDialogStateModel,
GuiProcessingStateModel,
GuiProfileModel,
GuiStateModel,
GuiSwitchStateModel,
)
from python_app.models.run_config_model import RunConfigModel
logger = logging.getLogger(__name__)
def _as_dict(value: Any, context: str) -> dict[str, Any]:
"""Validate payload node is object-like, treating missing values as empty object."""
if value is None:
return {}
if not isinstance(value, dict):
raise ValueError(f"{context} must be a JSON object")
return value
def _optional_string(object_payload: dict[str, Any], key: str, fallback: str, context: str) -> str:
"""Return optional string field with type validation."""
raw_value = object_payload.get(key, fallback)
if not isinstance(raw_value, str):
raise ValueError(f"{context}.{key} must be a JSON string")
return raw_value
def _optional_bool(object_payload: dict[str, Any], key: str, fallback: bool, context: str) -> bool:
"""Return optional boolean field with type validation."""
raw_value = object_payload.get(key, fallback)
if not isinstance(raw_value, bool):
raise ValueError(f"{context}.{key} must be a JSON bool")
return raw_value
def _optional_int(object_payload: dict[str, Any], key: str, fallback: int, context: str) -> int:
"""Return optional integer field with type validation."""
raw_value = object_payload.get(key, fallback)
if isinstance(raw_value, bool) or not isinstance(raw_value, int):
raise ValueError(f"{context}.{key} must be a JSON integer")
return int(raw_value)
def _optional_float(object_payload: dict[str, Any], key: str, fallback: float, context: str) -> float:
"""Return optional numeric field with type validation."""
raw_value = object_payload.get(key, fallback)
if isinstance(raw_value, bool) or not isinstance(raw_value, (int, float)):
raise ValueError(f"{context}.{key} must be a JSON number")
return float(raw_value)
def _legacy_gpr_mode_from_algorithm(value: str) -> str | None:
"""Translate the short-lived nested GPR algorithm field into legacy mode."""
if value == "legacy_point":
return "point"
if value == "legacy_extended":
return "extended"
return None
def gui_profile_from_dict(payload: dict[str, Any]) -> GuiProfileModel:
"""Decode JSON-like payload into :class:`GuiProfileModel`."""
profile = GuiProfileModel(run_config=RunConfigModel.from_dict(payload), gui=None)
gui_payload = payload.get("gui")
if gui_payload is None:
logger.debug("Decoded GUI profile without a 'gui' section; UI state left unset")
return profile
gui_object = _as_dict(gui_payload, "gui")
gui = GuiStateModel()
gui.version = _optional_int(gui_object, "version", gui.version, "gui")
if gui.version != 1:
raise ValueError(f"Unsupported gui.version: {gui.version}")
switches_object = _as_dict(gui_object.get("switches"), "gui.switches")
gui.switches = GuiSwitchStateModel(
combo_mode=_optional_string(switches_object, "combo_mode", gui.switches.combo_mode, "gui.switches"),
combos_text=_optional_string(switches_object, "combos_text", gui.switches.combos_text, "gui.switches"),
single_input=_optional_string(switches_object, "single_input", gui.switches.single_input, "gui.switches"),
single_output=_optional_string(
switches_object,
"single_output",
gui.switches.single_output,
"gui.switches",
),
)
if gui.switches.combo_mode not in {"text", "single"}:
raise ValueError("gui.switches.combo_mode must be either 'text' or 'single'")
processing_object = _as_dict(gui_object.get("processing"), "gui.processing")
pass_through_object = _as_dict(processing_object.get("pass_through"), "gui.processing.pass_through")
bscan_object = _as_dict(processing_object.get("bscan"), "gui.processing.bscan")
gpr_object = _as_dict(processing_object.get("gpr"), "gui.processing.gpr")
legacy_gpr_object = _as_dict(processing_object.get("legacy_gpr"), "gui.processing.legacy_gpr")
root_gpr_object = payload.get("gpr")
selected_mode = _optional_string(
processing_object,
"selected_mode",
gui.processing.selected_mode,
"gui.processing",
)
legacy_mode_default = gui.processing.legacy_gpr.mode
if isinstance(root_gpr_object, dict):
if root_gpr_object.get("mode") == "point":
legacy_mode_default = "point"
elif root_gpr_object.get("mode") == "extended":
legacy_mode_default = "extended"
legacy_algorithm_mode = _legacy_gpr_mode_from_algorithm(str(gpr_object.get("algorithm", "")))
if legacy_algorithm_mode is not None:
legacy_mode_default = legacy_algorithm_mode
has_legacy_root_gpr_mode = (
isinstance(root_gpr_object, dict)
and root_gpr_object.get("mode") in {"point", "extended"}
)
if selected_mode == "gpr" and (legacy_algorithm_mode is not None or has_legacy_root_gpr_mode):
logger.debug("Migrating legacy GPR profile: rewriting selected_mode 'gpr' -> 'legacy_gpr'")
selected_mode = "legacy_gpr"
gpr_context = "gui.processing.gpr"
legacy_gpr_context = "gui.processing.legacy_gpr"
def legacy_string(key: str, fallback: str) -> str:
"""Read a legacy field, falling back to the old nested gpr object."""
return _optional_string(
legacy_gpr_object,
key,
_optional_string(gpr_object, key, fallback, gpr_context),
legacy_gpr_context,
)
def legacy_bool(key: str, fallback: bool) -> bool:
"""Read a legacy bool, falling back to the old nested gpr object."""
return _optional_bool(
legacy_gpr_object,
key,
_optional_bool(gpr_object, key, fallback, gpr_context),
legacy_gpr_context,
)
def legacy_int(key: str, fallback: int) -> int:
"""Read a legacy integer, falling back to the old nested gpr object."""
return _optional_int(
legacy_gpr_object,
key,
_optional_int(gpr_object, key, fallback, gpr_context),
legacy_gpr_context,
)
def legacy_float(key: str, fallback: float) -> float:
"""Read a legacy float, falling back to the old nested gpr object."""
return _optional_float(
legacy_gpr_object,
key,
_optional_float(gpr_object, key, fallback, gpr_context),
legacy_gpr_context,
)
gui.processing = GuiProcessingStateModel(
selected_mode=selected_mode,
pass_through=GuiPassThroughStateModel(
show_magnitude=_optional_bool(
pass_through_object,
"show_magnitude",
gui.processing.pass_through.show_magnitude,
"gui.processing.pass_through",
),
show_phase=_optional_bool(
pass_through_object,
"show_phase",
gui.processing.pass_through.show_phase,
"gui.processing.pass_through",
),
unwrap_phase=_optional_bool(
pass_through_object,
"unwrap_phase",
gui.processing.pass_through.unwrap_phase,
"gui.processing.pass_through",
),
combo_filter=_optional_string(
pass_through_object,
"combo_filter",
gui.processing.pass_through.combo_filter,
"gui.processing.pass_through",
),
fixed_y_enabled=_optional_bool(
pass_through_object,
"fixed_y_enabled",
gui.processing.pass_through.fixed_y_enabled,
"gui.processing.pass_through",
),
y_min_db=_optional_float(
pass_through_object,
"y_min_db",
gui.processing.pass_through.y_min_db,
"gui.processing.pass_through",
),
y_max_db=_optional_float(
pass_through_object,
"y_max_db",
gui.processing.pass_through.y_max_db,
"gui.processing.pass_through",
),
),
bscan=GuiBscanStateModel(
axis=_optional_string(bscan_object, "axis", gui.processing.bscan.axis, "gui.processing.bscan"),
cut_m=_optional_float(bscan_object, "cut_m", gui.processing.bscan.cut_m, "gui.processing.bscan"),
max_depth_m=_optional_float(
bscan_object,
"max_depth_m",
gui.processing.bscan.max_depth_m,
"gui.processing.bscan",
),
gain=_optional_float(bscan_object, "gain", gui.processing.bscan.gain, "gui.processing.bscan"),
start_freq_mhz=_optional_float(
bscan_object,
"start_freq_mhz",
gui.processing.bscan.start_freq_mhz,
"gui.processing.bscan",
),
stop_freq_mhz=_optional_float(
bscan_object,
"stop_freq_mhz",
gui.processing.bscan.stop_freq_mhz,
"gui.processing.bscan",
),
subtract_mean_ascan=_optional_bool(
bscan_object,
"subtract_mean_ascan",
gui.processing.bscan.subtract_mean_ascan,
"gui.processing.bscan",
),
),
gpr=GuiGprStateModel(
input_positions=_optional_string(
gpr_object,
"input_positions",
gui.processing.gpr.input_positions,
"gui.processing.gpr",
),
output_positions=_optional_string(
gpr_object,
"output_positions",
gui.processing.gpr.output_positions,
"gui.processing.gpr",
),
min_depth_m=_optional_float(
gpr_object,
"min_depth_m",
gui.processing.gpr.min_depth_m,
"gui.processing.gpr",
),
max_depth_m=_optional_float(
gpr_object,
"max_depth_m",
gui.processing.gpr.max_depth_m,
"gui.processing.gpr",
),
range_comp_power=_optional_float(
gpr_object,
"range_comp_power",
gui.processing.gpr.range_comp_power,
"gui.processing.gpr",
),
angle_comp_power=_optional_float(
gpr_object,
"angle_comp_power",
gui.processing.gpr.angle_comp_power,
"gui.processing.gpr",
),
score_mode=_optional_string(
gpr_object,
"score_mode",
gui.processing.gpr.score_mode,
"gui.processing.gpr",
),
motion_mode=_optional_string(
gpr_object,
"motion_mode",
gui.processing.gpr.motion_mode,
"gui.processing.gpr",
),
look_angle_deg=_optional_float(
gpr_object,
"look_angle_deg",
gui.processing.gpr.look_angle_deg,
"gui.processing.gpr",
),
direction_sign=_optional_float(
gpr_object,
"direction_sign",
gui.processing.gpr.direction_sign,
"gui.processing.gpr",
),
speed_m_s=_optional_float(
gpr_object,
"speed_m_s",
gui.processing.gpr.speed_m_s,
"gui.processing.gpr",
),
ignore_socket_speed_enabled=_optional_bool(
gpr_object,
"ignore_socket_speed_enabled",
gui.processing.gpr.ignore_socket_speed_enabled,
"gui.processing.gpr",
),
max_detected_objects_to_draw=_optional_int(
gpr_object,
"max_detected_objects_to_draw",
gui.processing.gpr.max_detected_objects_to_draw,
"gui.processing.gpr",
),
draw_top_m_objects=_optional_int(
gpr_object,
"draw_top_m_objects",
gui.processing.gpr.draw_top_m_objects,
"gui.processing.gpr",
),
start_freq_mhz=_optional_float(
gpr_object,
"start_freq_mhz",
gui.processing.gpr.start_freq_mhz,
"gui.processing.gpr",
),
stop_freq_mhz=_optional_float(
gpr_object,
"stop_freq_mhz",
gui.processing.gpr.stop_freq_mhz,
"gui.processing.gpr",
),
background_subtract_enabled=_optional_bool(
gpr_object,
"background_subtract_enabled",
gui.processing.gpr.background_subtract_enabled,
"gui.processing.gpr",
),
background_mean_count=_optional_int(
gpr_object,
"background_mean_count",
gui.processing.gpr.background_mean_count,
"gui.processing.gpr",
),
remove_sidelobe_objects_enabled=_optional_bool(
gpr_object,
"remove_sidelobe_objects_enabled",
gui.processing.gpr.remove_sidelobe_objects_enabled,
"gui.processing.gpr",
),
imaging_plane_y_m=_optional_float(
gpr_object,
"imaging_plane_y_m",
gui.processing.gpr.imaging_plane_y_m,
"gui.processing.gpr",
),
render_mode=_optional_string(
gpr_object,
"render_mode",
gui.processing.gpr.render_mode,
"gui.processing.gpr",
),
min_visible_score=_optional_float(
gpr_object,
"min_visible_score",
gui.processing.gpr.min_visible_score,
"gui.processing.gpr",
),
visible_x_min_m=_optional_float(
gpr_object,
"visible_x_min_m",
gui.processing.gpr.visible_x_min_m,
"gui.processing.gpr",
),
visible_x_max_m=_optional_float(
gpr_object,
"visible_x_max_m",
gui.processing.gpr.visible_x_max_m,
"gui.processing.gpr",
),
visible_z_min_m=_optional_float(
gpr_object,
"visible_z_min_m",
gui.processing.gpr.visible_z_min_m,
"gui.processing.gpr",
),
visible_z_max_m=_optional_float(
gpr_object,
"visible_z_max_m",
gui.processing.gpr.visible_z_max_m,
"gui.processing.gpr",
),
),
legacy_gpr=GuiLegacyGprStateModel(
mode=legacy_string("mode", legacy_mode_default),
input_positions=legacy_string("input_positions", gui.processing.legacy_gpr.input_positions),
output_positions=legacy_string("output_positions", gui.processing.legacy_gpr.output_positions),
min_depth_m=legacy_float("min_depth_m", gui.processing.legacy_gpr.min_depth_m),
max_depth_m=legacy_float("max_depth_m", gui.processing.legacy_gpr.max_depth_m),
comp_power=legacy_float("comp_power", gui.processing.legacy_gpr.comp_power),
start_freq_mhz=legacy_float("start_freq_mhz", gui.processing.legacy_gpr.start_freq_mhz),
stop_freq_mhz=legacy_float("stop_freq_mhz", gui.processing.legacy_gpr.stop_freq_mhz),
speed_m_s=legacy_float("speed_m_s", gui.processing.legacy_gpr.speed_m_s),
ignore_socket_speed_enabled=legacy_bool(
"ignore_socket_speed_enabled",
gui.processing.legacy_gpr.ignore_socket_speed_enabled,
),
look_angle_deg=legacy_float("look_angle_deg", gui.processing.legacy_gpr.look_angle_deg),
apply_freq_phase_correction=legacy_bool(
"apply_freq_phase_correction",
gui.processing.legacy_gpr.apply_freq_phase_correction,
),
reference_mode=legacy_string(
"reference_mode",
gui.processing.legacy_gpr.reference_mode,
),
snr_thresh=legacy_float("snr_thresh", gui.processing.legacy_gpr.snr_thresh),
snr_comp_max=legacy_float("snr_comp_max", gui.processing.legacy_gpr.snr_comp_max),
background_subtract_enabled=legacy_bool(
"background_subtract_enabled",
gui.processing.legacy_gpr.background_subtract_enabled,
),
background_mean_count=legacy_int(
"background_mean_count",
gui.processing.legacy_gpr.background_mean_count,
),
render_mode=legacy_string("render_mode", gui.processing.legacy_gpr.render_mode),
min_visible_pair_count=legacy_int(
"min_visible_pair_count",
gui.processing.legacy_gpr.min_visible_pair_count,
),
visible_x_min_m=legacy_float("visible_x_min_m", gui.processing.legacy_gpr.visible_x_min_m),
visible_x_max_m=legacy_float("visible_x_max_m", gui.processing.legacy_gpr.visible_x_max_m),
visible_z_min_m=legacy_float("visible_z_min_m", gui.processing.legacy_gpr.visible_z_min_m),
visible_z_max_m=legacy_float("visible_z_max_m", gui.processing.legacy_gpr.visible_z_max_m),
),
)
if gui.processing.selected_mode not in {"pass_through", "bscan", "gpr", "legacy_gpr"}:
raise ValueError("gui.processing.selected_mode must be one of: pass_through, bscan, gpr, legacy_gpr")
if gui.processing.bscan.axis not in {"abs", "real", "phase"}:
raise ValueError("gui.processing.bscan.axis must be one of: abs, real, phase")
if gui.processing.gpr.render_mode not in {"heatmap", "objects_only"}:
raise ValueError("gui.processing.gpr.render_mode must be one of: heatmap, objects_only")
if gui.processing.gpr.score_mode not in {"peak", "combined"}:
raise ValueError("gui.processing.gpr.score_mode must be one of: peak, combined")
if gui.processing.gpr.motion_mode not in {"int_minus", "int_focus"}:
raise ValueError("gui.processing.gpr.motion_mode must be one of: int_minus, int_focus")
if gui.processing.legacy_gpr.mode not in {"point", "extended"}:
raise ValueError("gui.processing.legacy_gpr.mode must be one of: point, extended")
if gui.processing.legacy_gpr.render_mode not in {"heatmap", "objects_only"}:
raise ValueError("gui.processing.legacy_gpr.render_mode must be one of: heatmap, objects_only")
if gui.processing.legacy_gpr.reference_mode not in {"frame_center", "first_tx_event"}:
raise ValueError(
"gui.processing.legacy_gpr.reference_mode must be one of: frame_center, first_tx_event"
)
if gui.processing.gpr.range_comp_power < 0.0:
raise ValueError("gui.processing.gpr.range_comp_power must be >= 0")
if gui.processing.gpr.angle_comp_power < 0.0:
raise ValueError("gui.processing.gpr.angle_comp_power must be >= 0")
if gui.processing.gpr.min_visible_score < 0.0:
raise ValueError("gui.processing.gpr.min_visible_score must be >= 0")
if gui.processing.gpr.max_detected_objects_to_draw < 0:
raise ValueError("gui.processing.gpr.max_detected_objects_to_draw must be >= 0")
if gui.processing.gpr.draw_top_m_objects < 0:
raise ValueError("gui.processing.gpr.draw_top_m_objects must be >= 0")
if gui.processing.legacy_gpr.comp_power < 0.0:
raise ValueError("gui.processing.legacy_gpr.comp_power must be >= 0")
if gui.processing.legacy_gpr.snr_thresh < 0.0:
raise ValueError("gui.processing.legacy_gpr.snr_thresh must be >= 0")
if gui.processing.legacy_gpr.snr_comp_max < 0.0:
raise ValueError("gui.processing.legacy_gpr.snr_comp_max must be >= 0")
if gui.processing.legacy_gpr.min_visible_pair_count < 1:
raise ValueError("gui.processing.legacy_gpr.min_visible_pair_count must be >= 1")
data_actions_object = _as_dict(gui_object.get("data_actions"), "gui.data_actions")
gui.data_actions = GuiDataActionsStateModel(
save_count=_optional_int(
data_actions_object,
"save_count",
gui.data_actions.save_count,
"gui.data_actions",
),
save_path=_optional_string(
data_actions_object,
"save_path",
gui.data_actions.save_path,
"gui.data_actions",
),
save_name=_optional_string(
data_actions_object,
"save_name",
gui.data_actions.save_name,
"gui.data_actions",
),
record_count=_optional_int(
data_actions_object,
"record_count",
gui.data_actions.record_count,
"gui.data_actions",
),
)
preprocess_dialog_object = _as_dict(gui_object.get("preprocess_dialog"), "gui.preprocess_dialog")
gui.preprocess_dialog = GuiPreprocessDialogStateModel(
set_name=_optional_string(
preprocess_dialog_object,
"set_name",
gui.preprocess_dialog.set_name,
"gui.preprocess_dialog",
),
radar_config_dir=_optional_string(
preprocess_dialog_object,
"radar_config_dir",
gui.preprocess_dialog.radar_config_dir,
"gui.preprocess_dialog",
),
use_all_radar_configs=_optional_bool(
preprocess_dialog_object,
"use_all_radar_configs",
gui.preprocess_dialog.use_all_radar_configs,
"gui.preprocess_dialog",
),
median_sweep_count=max(
1,
_optional_int(
preprocess_dialog_object,
"median_sweep_count",
gui.preprocess_dialog.median_sweep_count,
"gui.preprocess_dialog",
),
),
)
profile.gui = gui
logger.debug("Decoded GUI profile: selected_mode=%s", gui.processing.selected_mode)
return profile
def gui_profile_to_dict(model: GuiProfileModel) -> dict[str, Any]:
"""Encode :class:`GuiProfileModel` into JSON-serializable dictionary."""
payload = model.run_config.to_dict()
gui = model.gui if model.gui is not None else GuiStateModel()
payload["gui"] = {
"version": gui.version,
"switches": {
"combo_mode": gui.switches.combo_mode,
"combos_text": gui.switches.combos_text,
"single_input": gui.switches.single_input,
"single_output": gui.switches.single_output,
},
"processing": {
"selected_mode": gui.processing.selected_mode,
"pass_through": {
"show_magnitude": gui.processing.pass_through.show_magnitude,
"show_phase": gui.processing.pass_through.show_phase,
"unwrap_phase": gui.processing.pass_through.unwrap_phase,
"combo_filter": gui.processing.pass_through.combo_filter,
"fixed_y_enabled": gui.processing.pass_through.fixed_y_enabled,
"y_min_db": gui.processing.pass_through.y_min_db,
"y_max_db": gui.processing.pass_through.y_max_db,
},
"bscan": {
"axis": gui.processing.bscan.axis,
"cut_m": gui.processing.bscan.cut_m,
"max_depth_m": gui.processing.bscan.max_depth_m,
"gain": gui.processing.bscan.gain,
"start_freq_mhz": gui.processing.bscan.start_freq_mhz,
"stop_freq_mhz": gui.processing.bscan.stop_freq_mhz,
"subtract_mean_ascan": gui.processing.bscan.subtract_mean_ascan,
},
"gpr": {
"input_positions": gui.processing.gpr.input_positions,
"output_positions": gui.processing.gpr.output_positions,
"min_depth_m": gui.processing.gpr.min_depth_m,
"max_depth_m": gui.processing.gpr.max_depth_m,
"range_comp_power": gui.processing.gpr.range_comp_power,
"angle_comp_power": gui.processing.gpr.angle_comp_power,
"score_mode": gui.processing.gpr.score_mode,
"motion_mode": gui.processing.gpr.motion_mode,
"look_angle_deg": gui.processing.gpr.look_angle_deg,
"direction_sign": gui.processing.gpr.direction_sign,
"speed_m_s": gui.processing.gpr.speed_m_s,
"ignore_socket_speed_enabled": gui.processing.gpr.ignore_socket_speed_enabled,
"max_detected_objects_to_draw": gui.processing.gpr.max_detected_objects_to_draw,
"draw_top_m_objects": gui.processing.gpr.draw_top_m_objects,
"start_freq_mhz": gui.processing.gpr.start_freq_mhz,
"stop_freq_mhz": gui.processing.gpr.stop_freq_mhz,
"background_subtract_enabled": gui.processing.gpr.background_subtract_enabled,
"background_mean_count": gui.processing.gpr.background_mean_count,
"remove_sidelobe_objects_enabled": gui.processing.gpr.remove_sidelobe_objects_enabled,
"imaging_plane_y_m": gui.processing.gpr.imaging_plane_y_m,
"render_mode": gui.processing.gpr.render_mode,
"min_visible_score": gui.processing.gpr.min_visible_score,
"visible_x_min_m": gui.processing.gpr.visible_x_min_m,
"visible_x_max_m": gui.processing.gpr.visible_x_max_m,
"visible_z_min_m": gui.processing.gpr.visible_z_min_m,
"visible_z_max_m": gui.processing.gpr.visible_z_max_m,
},
"legacy_gpr": {
"mode": gui.processing.legacy_gpr.mode,
"input_positions": gui.processing.legacy_gpr.input_positions,
"output_positions": gui.processing.legacy_gpr.output_positions,
"min_depth_m": gui.processing.legacy_gpr.min_depth_m,
"max_depth_m": gui.processing.legacy_gpr.max_depth_m,
"comp_power": gui.processing.legacy_gpr.comp_power,
"start_freq_mhz": gui.processing.legacy_gpr.start_freq_mhz,
"stop_freq_mhz": gui.processing.legacy_gpr.stop_freq_mhz,
"speed_m_s": gui.processing.legacy_gpr.speed_m_s,
"ignore_socket_speed_enabled": gui.processing.legacy_gpr.ignore_socket_speed_enabled,
"look_angle_deg": gui.processing.legacy_gpr.look_angle_deg,
"apply_freq_phase_correction": gui.processing.legacy_gpr.apply_freq_phase_correction,
"reference_mode": gui.processing.legacy_gpr.reference_mode,
"snr_thresh": gui.processing.legacy_gpr.snr_thresh,
"snr_comp_max": gui.processing.legacy_gpr.snr_comp_max,
"background_subtract_enabled": gui.processing.legacy_gpr.background_subtract_enabled,
"background_mean_count": gui.processing.legacy_gpr.background_mean_count,
"render_mode": gui.processing.legacy_gpr.render_mode,
"min_visible_pair_count": gui.processing.legacy_gpr.min_visible_pair_count,
"visible_x_min_m": gui.processing.legacy_gpr.visible_x_min_m,
"visible_x_max_m": gui.processing.legacy_gpr.visible_x_max_m,
"visible_z_min_m": gui.processing.legacy_gpr.visible_z_min_m,
"visible_z_max_m": gui.processing.legacy_gpr.visible_z_max_m,
},
},
"data_actions": {
"save_count": gui.data_actions.save_count,
"save_path": gui.data_actions.save_path,
"save_name": gui.data_actions.save_name,
"record_count": gui.data_actions.record_count,
},
"preprocess_dialog": {
"set_name": gui.preprocess_dialog.set_name,
"radar_config_dir": gui.preprocess_dialog.radar_config_dir,
"use_all_radar_configs": gui.preprocess_dialog.use_all_radar_configs,
"median_sweep_count": gui.preprocess_dialog.median_sweep_count,
},
}
return payload