The sar3d processor is rewritten as a checked port of Multi_antenna_3D_clean_v1.ipynb: NVRTC kernels, timing from the trace stamps, notebook parameter names and one cubic SAR_VOXEL_M. It adds front, side, top and 3D views, drops gpr_cpp and the parity tools, and the GUI speed now applies only while the socket speed is ignored.
700 lines
31 KiB
Python
700 lines
31 KiB
Python
"""Encoding and decoding logic for :mod:`python_app.models.gui_profile_schema`."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import dataclasses
|
|
import logging
|
|
from typing import Any
|
|
|
|
from python_app.models.gui_profile_schema import (
|
|
GuiBscanStateModel,
|
|
GuiDataActionsStateModel,
|
|
GuiGprStateModel,
|
|
GuiLegacyGprStateModel,
|
|
GuiPassThroughStateModel,
|
|
GuiPreprocessDialogStateModel,
|
|
GuiProcessingStateModel,
|
|
GuiProfileModel,
|
|
GuiSar3dStateModel,
|
|
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")
|
|
sar3d_object = _as_dict(processing_object.get("sar3d"), "gui.processing.sar3d")
|
|
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,
|
|
)
|
|
|
|
sar3d_context = "gui.processing.sar3d"
|
|
sar3d_defaults = GuiSar3dStateModel()
|
|
|
|
def sar3d_string(key: str) -> str:
|
|
return _optional_string(sar3d_object, key, getattr(sar3d_defaults, key), sar3d_context)
|
|
|
|
def sar3d_bool(key: str) -> bool:
|
|
return _optional_bool(sar3d_object, key, getattr(sar3d_defaults, key), sar3d_context)
|
|
|
|
def sar3d_int(key: str) -> int:
|
|
return _optional_int(sar3d_object, key, getattr(sar3d_defaults, key), sar3d_context)
|
|
|
|
def sar3d_float(key: str) -> float:
|
|
return _optional_float(sar3d_object, key, getattr(sar3d_defaults, key), sar3d_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",
|
|
),
|
|
history_window_scans=_optional_int(
|
|
bscan_object,
|
|
"history_window_scans",
|
|
gui.processing.bscan.history_window_scans,
|
|
"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",
|
|
),
|
|
object_min_frac=_optional_float(
|
|
gpr_object,
|
|
"object_min_frac",
|
|
gui.processing.gpr.object_min_frac,
|
|
"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",
|
|
),
|
|
object_approach_min_frames=_optional_int(
|
|
gpr_object,
|
|
"object_approach_min_frames",
|
|
gui.processing.gpr.object_approach_min_frames,
|
|
"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",
|
|
),
|
|
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),
|
|
),
|
|
sar3d=GuiSar3dStateModel(
|
|
input_positions=sar3d_string("input_positions"),
|
|
output_positions=sar3d_string("output_positions"),
|
|
min_depth_m=sar3d_float("min_depth_m"),
|
|
max_depth_m=sar3d_float("max_depth_m"),
|
|
start_freq_mhz=sar3d_float("start_freq_mhz"),
|
|
stop_freq_mhz=sar3d_float("stop_freq_mhz"),
|
|
speed_m_s=sar3d_float("speed_m_s"),
|
|
ignore_socket_speed_enabled=sar3d_bool("ignore_socket_speed_enabled"),
|
|
look_angle_deg=sar3d_float("look_angle_deg"),
|
|
frame_radius=sar3d_int("frame_radius"),
|
|
voxel_m=sar3d_float("voxel_m"),
|
|
bp_ground_depth_m=sar3d_float("bp_ground_depth_m"),
|
|
comp_range_power=sar3d_float("comp_range_power"),
|
|
use_compensation=sar3d_bool("use_compensation"),
|
|
normalize_each_frame=sar3d_bool("normalize_each_frame"),
|
|
),
|
|
)
|
|
if gui.processing.selected_mode not in {"pass_through", "bscan", "gpr", "legacy_gpr", "sar3d"}:
|
|
raise ValueError(
|
|
"gui.processing.selected_mode must be one of: pass_through, bscan, gpr, legacy_gpr, sar3d"
|
|
)
|
|
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 not 0.0 <= gui.processing.gpr.object_min_frac <= 1.0:
|
|
raise ValueError("gui.processing.gpr.object_min_frac must be within [0, 1]")
|
|
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.gpr.object_approach_min_frames < 1:
|
|
raise ValueError("gui.processing.gpr.object_approach_min_frames must be >= 1")
|
|
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")
|
|
if gui.processing.sar3d.frame_radius < 0:
|
|
raise ValueError("gui.processing.sar3d.frame_radius must be >= 0")
|
|
if not gui.processing.sar3d.voxel_m > 0.0:
|
|
raise ValueError("gui.processing.sar3d.voxel_m must be > 0")
|
|
|
|
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,
|
|
"history_window_scans": gui.processing.bscan.history_window_scans,
|
|
},
|
|
"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,
|
|
"object_min_frac": gui.processing.gpr.object_min_frac,
|
|
"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,
|
|
"object_approach_min_frames": gui.processing.gpr.object_approach_min_frames,
|
|
"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,
|
|
"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,
|
|
},
|
|
"sar3d": dataclasses.asdict(gui.processing.sar3d),
|
|
},
|
|
"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
|