sar3d: GPU port of the notebook with four views

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.
This commit is contained in:
Ayzen
2026-10-02 16:05:22 +03:00
parent 61ef8720e1
commit 45861f4047
50 changed files with 1737 additions and 8144 deletions
+34 -240
View File
@@ -2,6 +2,7 @@
from __future__ import annotations
import dataclasses
import logging
from typing import Any
@@ -172,6 +173,21 @@ def gui_profile_from_dict(payload: dict[str, Any]) -> GuiProfileModel:
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(
@@ -455,198 +471,21 @@ def gui_profile_from_dict(payload: dict[str, Any]) -> GuiProfileModel:
visible_z_max_m=legacy_float("visible_z_max_m", gui.processing.legacy_gpr.visible_z_max_m),
),
sar3d=GuiSar3dStateModel(
input_positions=_optional_string(
sar3d_object,
"input_positions",
gui.processing.sar3d.input_positions,
"gui.processing.sar3d",
),
output_positions=_optional_string(
sar3d_object,
"output_positions",
gui.processing.sar3d.output_positions,
"gui.processing.sar3d",
),
min_depth_m=_optional_float(
sar3d_object,
"min_depth_m",
gui.processing.sar3d.min_depth_m,
"gui.processing.sar3d",
),
max_depth_m=_optional_float(
sar3d_object,
"max_depth_m",
gui.processing.sar3d.max_depth_m,
"gui.processing.sar3d",
),
start_freq_mhz=_optional_float(
sar3d_object,
"start_freq_mhz",
gui.processing.sar3d.start_freq_mhz,
"gui.processing.sar3d",
),
stop_freq_mhz=_optional_float(
sar3d_object,
"stop_freq_mhz",
gui.processing.sar3d.stop_freq_mhz,
"gui.processing.sar3d",
),
speed_m_s=_optional_float(
sar3d_object,
"speed_m_s",
gui.processing.sar3d.speed_m_s,
"gui.processing.sar3d",
),
ignore_socket_speed_enabled=_optional_bool(
sar3d_object,
"ignore_socket_speed_enabled",
gui.processing.sar3d.ignore_socket_speed_enabled,
"gui.processing.sar3d",
),
look_angle_deg=_optional_float(
sar3d_object,
"look_angle_deg",
gui.processing.sar3d.look_angle_deg,
"gui.processing.sar3d",
),
direction_sign=_optional_float(
sar3d_object,
"direction_sign",
gui.processing.sar3d.direction_sign,
"gui.processing.sar3d",
),
apply_freq_phase_correction=_optional_bool(
sar3d_object,
"apply_freq_phase_correction",
gui.processing.sar3d.apply_freq_phase_correction,
"gui.processing.sar3d",
),
frame_radius=_optional_int(
sar3d_object,
"frame_radius",
gui.processing.sar3d.frame_radius,
"gui.processing.sar3d",
),
edge_frame_weight=_optional_float(
sar3d_object,
"edge_frame_weight",
gui.processing.sar3d.edge_frame_weight,
"gui.processing.sar3d",
),
pitch_override_enabled=_optional_bool(
sar3d_object,
"pitch_override_enabled",
gui.processing.sar3d.pitch_override_enabled,
"gui.processing.sar3d",
),
pitch_deg=_optional_float(
sar3d_object,
"pitch_deg",
gui.processing.sar3d.pitch_deg,
"gui.processing.sar3d",
),
ground_depth_m=_optional_float(
sar3d_object,
"ground_depth_m",
gui.processing.sar3d.ground_depth_m,
"gui.processing.sar3d",
),
x_min_m=_optional_float(
sar3d_object,
"x_min_m",
gui.processing.sar3d.x_min_m,
"gui.processing.sar3d",
),
x_max_m=_optional_float(
sar3d_object,
"x_max_m",
gui.processing.sar3d.x_max_m,
"gui.processing.sar3d",
),
y_min_m=_optional_float(
sar3d_object,
"y_min_m",
gui.processing.sar3d.y_min_m,
"gui.processing.sar3d",
),
y_max_m=_optional_float(
sar3d_object,
"y_max_m",
gui.processing.sar3d.y_max_m,
"gui.processing.sar3d",
),
z_min_m=_optional_float(
sar3d_object,
"z_min_m",
gui.processing.sar3d.z_min_m,
"gui.processing.sar3d",
),
z_max_m=_optional_float(
sar3d_object,
"z_max_m",
gui.processing.sar3d.z_max_m,
"gui.processing.sar3d",
),
voxel_x_m=_optional_float(
sar3d_object,
"voxel_x_m",
gui.processing.sar3d.voxel_x_m,
"gui.processing.sar3d",
),
voxel_y_m=_optional_float(
sar3d_object,
"voxel_y_m",
gui.processing.sar3d.voxel_y_m,
"gui.processing.sar3d",
),
voxel_z_m=_optional_float(
sar3d_object,
"voxel_z_m",
gui.processing.sar3d.voxel_z_m,
"gui.processing.sar3d",
),
norm_edge_margin_m=_optional_float(
sar3d_object,
"norm_edge_margin_m",
gui.processing.sar3d.norm_edge_margin_m,
"gui.processing.sar3d",
),
smooth_sigma=_optional_float(
sar3d_object,
"smooth_sigma",
gui.processing.sar3d.smooth_sigma,
"gui.processing.sar3d",
),
comp_range_power=_optional_float(
sar3d_object,
"comp_range_power",
gui.processing.sar3d.comp_range_power,
"gui.processing.sar3d",
),
tx_per_segment=_optional_int(
sar3d_object,
"tx_per_segment",
gui.processing.sar3d.tx_per_segment,
"gui.processing.sar3d",
),
use_compensation=_optional_bool(
sar3d_object,
"use_compensation",
gui.processing.sar3d.use_compensation,
"gui.processing.sar3d",
),
normalize_pairs=_optional_bool(
sar3d_object,
"normalize_pairs",
gui.processing.sar3d.normalize_pairs,
"gui.processing.sar3d",
),
bottom_view_deg=_optional_float(
sar3d_object,
"bottom_view_deg",
gui.processing.sar3d.bottom_view_deg,
"gui.processing.sar3d",
),
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"}:
@@ -689,20 +528,8 @@ def gui_profile_from_dict(payload: dict[str, Any]) -> GuiProfileModel:
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 0.0 <= gui.processing.sar3d.edge_frame_weight <= 1.0:
raise ValueError("gui.processing.sar3d.edge_frame_weight must be within [0, 1]")
if min(gui.processing.sar3d.voxel_x_m, gui.processing.sar3d.voxel_y_m, gui.processing.sar3d.voxel_z_m) <= 0.0:
raise ValueError("gui.processing.sar3d.voxel_x/y/z_m must each be > 0")
if gui.processing.sar3d.x_max_m <= gui.processing.sar3d.x_min_m:
raise ValueError("gui.processing.sar3d.x_max_m must be > x_min_m")
if gui.processing.sar3d.y_max_m <= gui.processing.sar3d.y_min_m:
raise ValueError("gui.processing.sar3d.y_max_m must be > y_min_m")
if gui.processing.sar3d.z_max_m <= gui.processing.sar3d.z_min_m:
raise ValueError("gui.processing.sar3d.z_max_m must be > z_min_m")
if gui.processing.sar3d.ground_depth_m < 0.0:
raise ValueError("gui.processing.sar3d.ground_depth_m must be >= 0")
if gui.processing.sar3d.tx_per_segment < 1:
raise ValueError("gui.processing.sar3d.tx_per_segment must be >= 1")
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(
@@ -854,40 +681,7 @@ def gui_profile_to_dict(model: GuiProfileModel) -> dict[str, Any]:
"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": {
"input_positions": gui.processing.sar3d.input_positions,
"output_positions": gui.processing.sar3d.output_positions,
"min_depth_m": gui.processing.sar3d.min_depth_m,
"max_depth_m": gui.processing.sar3d.max_depth_m,
"start_freq_mhz": gui.processing.sar3d.start_freq_mhz,
"stop_freq_mhz": gui.processing.sar3d.stop_freq_mhz,
"speed_m_s": gui.processing.sar3d.speed_m_s,
"ignore_socket_speed_enabled": gui.processing.sar3d.ignore_socket_speed_enabled,
"look_angle_deg": gui.processing.sar3d.look_angle_deg,
"direction_sign": gui.processing.sar3d.direction_sign,
"apply_freq_phase_correction": gui.processing.sar3d.apply_freq_phase_correction,
"frame_radius": gui.processing.sar3d.frame_radius,
"edge_frame_weight": gui.processing.sar3d.edge_frame_weight,
"pitch_override_enabled": gui.processing.sar3d.pitch_override_enabled,
"pitch_deg": gui.processing.sar3d.pitch_deg,
"ground_depth_m": gui.processing.sar3d.ground_depth_m,
"x_min_m": gui.processing.sar3d.x_min_m,
"x_max_m": gui.processing.sar3d.x_max_m,
"y_min_m": gui.processing.sar3d.y_min_m,
"y_max_m": gui.processing.sar3d.y_max_m,
"z_min_m": gui.processing.sar3d.z_min_m,
"z_max_m": gui.processing.sar3d.z_max_m,
"voxel_x_m": gui.processing.sar3d.voxel_x_m,
"voxel_y_m": gui.processing.sar3d.voxel_y_m,
"voxel_z_m": gui.processing.sar3d.voxel_z_m,
"norm_edge_margin_m": gui.processing.sar3d.norm_edge_margin_m,
"smooth_sigma": gui.processing.sar3d.smooth_sigma,
"comp_range_power": gui.processing.sar3d.comp_range_power,
"tx_per_segment": gui.processing.sar3d.tx_per_segment,
"use_compensation": gui.processing.sar3d.use_compensation,
"normalize_pairs": gui.processing.sar3d.normalize_pairs,
"bottom_view_deg": gui.processing.sar3d.bottom_view_deg,
},
"sar3d": dataclasses.asdict(gui.processing.sar3d),
},
"data_actions": {
"save_count": gui.data_actions.save_count,
+8 -28
View File
@@ -127,45 +127,25 @@ class GuiLegacyGprStateModel:
class GuiSar3dStateModel:
"""UI-only defaults for the 3D SAR (sar3d) live settings.
The band, depth gate, antenna selection and motion knobs mirror the shared
gpr_* live-config fields; the sar3d_* knobs mirror the reference notebook
(Multi_antenna_3D_clean_v1.ipynb) defaults used by the C++ sar3d processor.
Values are the reference notebook's (Multi_antenna_3D_clean_v1.ipynb), whose
names the GUI shows: INPUT_IDX, F_START, MIN_DEPTH, LOOK_ANGLE_DEG, ...
"""
input_positions: str = ""
output_positions: str = ""
min_depth_m: float = 2.5
min_depth_m: float = 2.0
max_depth_m: float = 8.0
start_freq_mhz: float = 2000.0
start_freq_mhz: float = 1500.0
stop_freq_mhz: float = 6000.0
speed_m_s: float = 0.0
ignore_socket_speed_enabled: bool = False
look_angle_deg: float = 17.5
direction_sign: float = 1.0
apply_freq_phase_correction: bool = True
look_angle_deg: float = 19.5
frame_radius: int = 0
edge_frame_weight: float = 0.8
pitch_override_enabled: bool = False
pitch_deg: float = 17.5
ground_depth_m: float = 1.25
x_min_m: float = -2.0
x_max_m: float = 2.0
y_min_m: float = -0.5
y_max_m: float = 2.0
z_min_m: float = 0.0
z_max_m: float = 8.0
voxel_x_m: float = 0.025
voxel_y_m: float = 0.02
voxel_z_m: float = 0.02
norm_edge_margin_m: float = 0.3
smooth_sigma: float = 1.5
voxel_m: float = 0.02
bp_ground_depth_m: float = 1.25
comp_range_power: float = 0.3
tx_per_segment: int = 2
use_compensation: bool = True
normalize_pairs: bool = True
# Bottom-view projection angle around the vertical Y axis, degrees:
# 0 = front view (X-Y), 90 = side view (Z-Y).
bottom_view_deg: float = 0.0
normalize_each_frame: bool = True
@dataclass(slots=True)