This commit is contained in:
Ayzen
2026-05-06 16:08:24 +03:00
parent cb4ba861d9
commit cf3f3cbd67
24 changed files with 670 additions and 247 deletions
+138 -61
View File
@@ -8,6 +8,7 @@ from python_app.models.gui_profile_schema import (
GuiBscanStateModel,
GuiDataActionsStateModel,
GuiGprStateModel,
GuiLegacyGprStateModel,
GuiPassThroughStateModel,
GuiPreprocessDialogStateModel,
GuiProcessingStateModel,
@@ -59,6 +60,15 @@ def _optional_float(object_payload: dict[str, Any], key: str, fallback: float, c
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)
@@ -91,20 +101,72 @@ def gui_profile_from_dict(payload: dict[str, Any]) -> GuiProfileModel:
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")
gpr_algorithm_default = gui.processing.gpr.algorithm
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":
gpr_algorithm_default = "legacy_point"
legacy_mode_default = "point"
elif root_gpr_object.get("mode") == "extended":
gpr_algorithm_default = "legacy_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):
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=_optional_string(
processing_object,
"selected_mode",
gui.processing.selected_mode,
"gui.processing",
),
selected_mode=selected_mode,
pass_through=GuiPassThroughStateModel(
show_magnitude=_optional_bool(
pass_through_object,
@@ -167,12 +229,6 @@ def gui_profile_from_dict(payload: dict[str, Any]) -> GuiProfileModel:
),
),
gpr=GuiGprStateModel(
algorithm=_optional_string(
gpr_object,
"algorithm",
gpr_algorithm_default,
"gui.processing.gpr",
),
input_positions=_optional_string(
gpr_object,
"input_positions",
@@ -209,36 +265,6 @@ def gui_profile_from_dict(payload: dict[str, Any]) -> GuiProfileModel:
gui.processing.gpr.angle_comp_power,
"gui.processing.gpr",
),
comp_power=_optional_float(
gpr_object,
"comp_power",
gui.processing.gpr.comp_power,
"gui.processing.gpr",
),
speed_m_s=_optional_float(
gpr_object,
"speed_m_s",
gui.processing.gpr.speed_m_s,
"gui.processing.gpr",
),
look_angle_deg=_optional_float(
gpr_object,
"look_angle_deg",
gui.processing.gpr.look_angle_deg,
"gui.processing.gpr",
),
snr_thresh=_optional_float(
gpr_object,
"snr_thresh",
gui.processing.gpr.snr_thresh,
"gui.processing.gpr",
),
snr_comp_max=_optional_float(
gpr_object,
"snr_comp_max",
gui.processing.gpr.snr_comp_max,
"gui.processing.gpr",
),
start_freq_mhz=_optional_float(
gpr_object,
"start_freq_mhz",
@@ -306,27 +332,62 @@ def gui_profile_from_dict(payload: dict[str, Any]) -> GuiProfileModel:
"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),
look_angle_deg=legacy_float("look_angle_deg", gui.processing.legacy_gpr.look_angle_deg),
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"}:
raise ValueError("gui.processing.selected_mode must be one of: pass_through, bscan, gpr")
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.algorithm not in {"backprojection", "legacy_point", "legacy_extended"}:
raise ValueError("gui.processing.gpr.algorithm must be one of: backprojection, legacy_point, legacy_extended")
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.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.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.comp_power < 0.0:
raise ValueError("gui.processing.gpr.comp_power must be >= 0")
if gui.processing.gpr.snr_thresh < 0.0:
raise ValueError("gui.processing.gpr.snr_thresh must be >= 0")
if gui.processing.gpr.snr_comp_max < 0.0:
raise ValueError("gui.processing.gpr.snr_comp_max 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.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(
@@ -407,18 +468,12 @@ def gui_profile_to_dict(model: GuiProfileModel) -> dict[str, Any]:
"subtract_mean_ascan": gui.processing.bscan.subtract_mean_ascan,
},
"gpr": {
"algorithm": gui.processing.gpr.algorithm,
"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,
"comp_power": gui.processing.gpr.comp_power,
"speed_m_s": gui.processing.gpr.speed_m_s,
"look_angle_deg": gui.processing.gpr.look_angle_deg,
"snr_thresh": gui.processing.gpr.snr_thresh,
"snr_comp_max": gui.processing.gpr.snr_comp_max,
"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,
@@ -431,6 +486,28 @@ def gui_profile_to_dict(model: GuiProfileModel) -> dict[str, Any]:
"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,
"look_angle_deg": gui.processing.legacy_gpr.look_angle_deg,
"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,
+2
View File
@@ -5,6 +5,7 @@ from python_app.models.gui_profile_schema import (
GuiBscanStateModel,
GuiDataActionsStateModel,
GuiGprStateModel,
GuiLegacyGprStateModel,
GuiPassThroughStateModel,
GuiPreprocessDialogStateModel,
GuiProcessingStateModel,
@@ -17,6 +18,7 @@ __all__ = [
"GuiBscanStateModel",
"GuiDataActionsStateModel",
"GuiGprStateModel",
"GuiLegacyGprStateModel",
"GuiPassThroughStateModel",
"GuiPreprocessDialogStateModel",
"GuiProcessingStateModel",
+28 -7
View File
@@ -47,20 +47,14 @@ class GuiBscanStateModel:
@dataclass(slots=True)
class GuiGprStateModel:
"""UI-only defaults for GPR live settings."""
"""UI-only defaults for coherent backprojection GPR live settings."""
algorithm: str = "backprojection"
input_positions: str = ""
output_positions: str = ""
min_depth_m: float = 2.0
max_depth_m: float = 14.0
range_comp_power: float = 0.28
angle_comp_power: float = 0.10
comp_power: float = 0.2
speed_m_s: float = 0.0
look_angle_deg: float = 0.0
snr_thresh: float = 4.5
snr_comp_max: float = 25.0
start_freq_mhz: float = 3000.0
stop_freq_mhz: float = 6000.0
background_subtract_enabled: bool = True
@@ -74,6 +68,32 @@ class GuiGprStateModel:
visible_z_max_m: float = 14.0
@dataclass(slots=True)
class GuiLegacyGprStateModel:
"""UI-only defaults for legacy point/extended GPR live settings."""
mode: str = "point"
input_positions: str = ""
output_positions: str = ""
min_depth_m: float = 2.0
max_depth_m: float = 14.0
comp_power: float = 0.2
start_freq_mhz: float = 3000.0
stop_freq_mhz: float = 6000.0
speed_m_s: float = 0.0
look_angle_deg: float = 0.0
snr_thresh: float = 4.5
snr_comp_max: float = 25.0
background_subtract_enabled: bool = True
background_mean_count: int = 10
render_mode: str = "heatmap"
min_visible_pair_count: int = 1
visible_x_min_m: float = -2.0
visible_x_max_m: float = 2.0
visible_z_min_m: float = 0.0
visible_z_max_m: float = 14.0
@dataclass(slots=True)
class GuiProcessingStateModel:
"""UI-only processing-section defaults."""
@@ -82,6 +102,7 @@ class GuiProcessingStateModel:
pass_through: GuiPassThroughStateModel = field(default_factory=GuiPassThroughStateModel)
bscan: GuiBscanStateModel = field(default_factory=GuiBscanStateModel)
gpr: GuiGprStateModel = field(default_factory=GuiGprStateModel)
legacy_gpr: GuiLegacyGprStateModel = field(default_factory=GuiLegacyGprStateModel)
@dataclass(slots=True)