Rework radar system
This commit is contained in:
@@ -1,585 +1,78 @@
|
||||
"""Encoding and decoding logic for :mod:`python_app.models.gui_profile_schema`."""
|
||||
"""Strict profile decoding driven by the typed field declarations."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import dataclasses
|
||||
import logging
|
||||
from dataclasses import asdict, fields, is_dataclass
|
||||
from typing import Any
|
||||
|
||||
from python_app.models.json_fields import finite_number, json_object as _as_dict
|
||||
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__)
|
||||
from python_app.models.gui_profile_schema import GuiProfileModel, GuiStateModel, Setting
|
||||
from python_app.models.json_fields import finite_number, json_object
|
||||
from python_app.models.run_config_schema import RunConfigModel
|
||||
|
||||
|
||||
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."""
|
||||
return finite_number(object_payload.get(key, fallback), f"{context}.{key}")
|
||||
|
||||
|
||||
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 _decode_state(model: object, payload: dict[str, Any], context: str, *, check_constraints: bool = True) -> None:
|
||||
"""Read known fields recursively; unknown fields do not survive re-encoding."""
|
||||
if not is_dataclass(model) or isinstance(model, type):
|
||||
raise TypeError("GUI state must be a dataclass instance")
|
||||
for member in fields(model):
|
||||
default = getattr(model, member.name)
|
||||
value = payload.get(member.name, default)
|
||||
name = f"{context}.{member.name}"
|
||||
if is_dataclass(default):
|
||||
_decode_state(default, json_object(payload.get(member.name), name), name, check_constraints=check_constraints)
|
||||
continue
|
||||
if isinstance(default, float):
|
||||
value = finite_number(value, name)
|
||||
elif type(value) is not type(default):
|
||||
raise ValueError(f"{name} must be a JSON {type(default).__name__}")
|
||||
spec = member.metadata.get("setting")
|
||||
if check_constraints and isinstance(spec, Setting):
|
||||
if spec.choices and value not in spec.choices:
|
||||
raise ValueError(f"{name} must be one of: {', '.join(spec.choices)}")
|
||||
if spec.minimum is not None:
|
||||
if value < spec.minimum or (spec.exclusive_minimum and value == spec.minimum):
|
||||
operator = ">" if spec.exclusive_minimum else ">="
|
||||
raise ValueError(f"{name} must be {operator} {spec.minimum:g}")
|
||||
if spec.maximum is not None and value > spec.maximum:
|
||||
raise ValueError(f"{name} must be <= {spec.maximum:g}")
|
||||
setattr(model, member.name, value)
|
||||
|
||||
|
||||
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")
|
||||
"""Read the pipeline config and optional GUI state, preserving old GPR profiles."""
|
||||
profile = GuiProfileModel(run_config=RunConfigModel.from_dict(payload))
|
||||
if payload.get("gui") is None:
|
||||
return profile
|
||||
|
||||
gui_object = _as_dict(gui_payload, "gui")
|
||||
gui_object = json_object(payload["gui"], "gui")
|
||||
processing = json_object(gui_object.get("processing"), "gui.processing")
|
||||
old_gpr = json_object(processing.get("gpr"), "gui.processing.gpr")
|
||||
gui = GuiStateModel()
|
||||
gui.version = _optional_int(gui_object, "version", gui.version, "gui")
|
||||
|
||||
# Older profiles kept both algorithms under gpr. Root mode and the short-lived
|
||||
# algorithm selector choose the legacy page; explicit legacy values win.
|
||||
root_gpr = payload.get("gpr")
|
||||
root_mode = root_gpr.get("mode") if isinstance(root_gpr, dict) else None
|
||||
algorithm_mode = {"legacy_point": "point", "legacy_extended": "extended"}.get(
|
||||
str(old_gpr.get("algorithm", "")))
|
||||
if root_mode in ("point", "extended"):
|
||||
gui.processing.legacy_gpr.mode = root_mode
|
||||
if algorithm_mode is not None:
|
||||
gui.processing.legacy_gpr.mode = algorithm_mode
|
||||
_decode_state(gui.processing.legacy_gpr, old_gpr, "gui.processing.gpr", check_constraints=False)
|
||||
_decode_state(gui, gui_object, "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_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",
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
if gui.processing.selected_mode == "gpr" and (
|
||||
algorithm_mode is not None or root_mode in ("point", "extended")
|
||||
):
|
||||
gui.processing.selected_mode = "legacy_gpr"
|
||||
gui.preprocess_dialog.median_sweep_count = max(1, gui.preprocess_dialog.median_sweep_count)
|
||||
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."""
|
||||
"""Encode the canonical run config and every declared GUI field."""
|
||||
payload = model.run_config.to_dict()
|
||||
payload["gui"] = dataclasses.asdict(model.gui if model.gui is not None else GuiStateModel())
|
||||
payload["gui"] = asdict(model.gui if model.gui is not None else GuiStateModel())
|
||||
return payload
|
||||
|
||||
Reference in New Issue
Block a user