sar3d: python plumbing for the new parameters and real replay timing
- replay_raw_producer carries the RECORDED per-trace capture windows from each snapshot's meta.json (re-anchored by the same shift as the frame time) instead of one synthetic window for all traces — without this the segmented timing model degenerates in replay; - GUI/profile/orchestration swap the removed window knobs (center_depth, half_width_*, grid_n*) for the notebook parameters (ground depth, pitch override, voxel pitch, Y range, X margin, norm edge margin, smooth sigma, comp power, tx per segment). Headless replay smoke on the 20260922_mine_fast_true dataset passes: 51 sar3d result collections, transparent preprocessor. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -20,6 +20,7 @@ never see time jump backwards. ``collection_id`` increases forever.
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import logging
|
||||
from pathlib import Path
|
||||
import re
|
||||
@@ -75,6 +76,22 @@ def _load_dataset(dataset_dir: Path, combos: list[ComboKey], expected_points: in
|
||||
timestamps_ns: list[int] = []
|
||||
for name, timestamp_ns in named:
|
||||
folder = dataset_dir / name
|
||||
# Recorded per-trace capture windows from the snapshot's meta.json: the
|
||||
# sar3d segmented timing model reconstructs the acquisition segments
|
||||
# from them. Missing/partial meta leaves the zero "unmeasured" sentinel
|
||||
# and the consumer falls back to its uniform-segment model.
|
||||
windows: dict[tuple[int, int], tuple[int, int]] = {}
|
||||
meta_path = folder / "meta.json"
|
||||
if meta_path.is_file():
|
||||
try:
|
||||
meta = json.loads(meta_path.read_text())
|
||||
for entry in meta.get("traces", []):
|
||||
windows[(int(entry["input"]), int(entry["output"]))] = (
|
||||
int(entry["capture_start_ns"]),
|
||||
int(entry["capture_end_ns"]),
|
||||
)
|
||||
except (OSError, ValueError, KeyError, TypeError):
|
||||
windows = {}
|
||||
traces: list[TraceData] = []
|
||||
for combo in combos:
|
||||
s21_path = folder / f"i{combo.input}_o{combo.output}_s21.npy"
|
||||
@@ -94,12 +111,15 @@ def _load_dataset(dataset_dir: Path, combos: list[ComboKey], expected_points: in
|
||||
f"{s21_path}: {s21.size} points, but radar.sweep.points = "
|
||||
f"{expected_points}. Align the profile's sweep with the dataset."
|
||||
)
|
||||
window = windows.get((combo.input, combo.output), (0, 0))
|
||||
traces.append(
|
||||
TraceData(
|
||||
combo=combo,
|
||||
frequency_hz=freq,
|
||||
s11=np.zeros_like(s21),
|
||||
s21=s21,
|
||||
capture_start_ns=window[0],
|
||||
capture_end_ns=window[1],
|
||||
)
|
||||
)
|
||||
snapshots.append(traces)
|
||||
@@ -114,28 +134,38 @@ def _build_collection(
|
||||
collection_id: int,
|
||||
frame_ns: int,
|
||||
) -> SweepCollection:
|
||||
"""Wrap one preloaded snapshot in a SweepCollection at the given frame time."""
|
||||
"""Wrap one preloaded snapshot in a SweepCollection at the given frame time.
|
||||
|
||||
Recorded per-trace capture windows are re-anchored by the same shift as the
|
||||
frame time, preserving the real intra-frame segment layout. Traces without
|
||||
recorded windows get a synthetic window spanning the median frame gap.
|
||||
"""
|
||||
span_ns = dataset.median_gap_ns
|
||||
start_ns = frame_ns - span_ns // 2
|
||||
end_ns = frame_ns + span_ns // 2
|
||||
shift_ns = frame_ns - dataset.timestamps_ns[index]
|
||||
traces = []
|
||||
for source in dataset.snapshots[index]:
|
||||
if source.capture_end_ns > source.capture_start_ns:
|
||||
capture = (source.capture_start_ns + shift_ns, source.capture_end_ns + shift_ns)
|
||||
else:
|
||||
capture = (start_ns, end_ns)
|
||||
traces.append(
|
||||
TraceData(
|
||||
combo=source.combo,
|
||||
frequency_hz=source.frequency_hz,
|
||||
s11=source.s11,
|
||||
s21=source.s21,
|
||||
capture_start_ns=start_ns,
|
||||
capture_end_ns=end_ns,
|
||||
capture_start_ns=capture[0],
|
||||
capture_end_ns=capture[1],
|
||||
)
|
||||
)
|
||||
return SweepCollection(
|
||||
collection_id=collection_id,
|
||||
monotonic_ns=frame_ns,
|
||||
traces=traces,
|
||||
capture_start_ns=start_ns,
|
||||
capture_end_ns=end_ns,
|
||||
capture_start_ns=min(t.capture_start_ns for t in traces),
|
||||
capture_end_ns=max(t.capture_end_ns for t in traces),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -84,14 +84,25 @@ def _sar3d_live_config(profile_path: Path) -> ProcessingLiveConfig:
|
||||
live.sar3d_edge_frame_weight = float(
|
||||
sar3d.get("edge_frame_weight", live.sar3d_edge_frame_weight)
|
||||
)
|
||||
live.sar3d_pitch_override_enabled = bool(
|
||||
sar3d.get("pitch_override_enabled", live.sar3d_pitch_override_enabled)
|
||||
)
|
||||
live.sar3d_pitch_deg = float(sar3d.get("pitch_deg", live.sar3d_pitch_deg))
|
||||
live.sar3d_center_depth_m = float(sar3d.get("center_depth_m", live.sar3d_center_depth_m))
|
||||
live.sar3d_half_width_x_m = float(sar3d.get("half_width_x_m", live.sar3d_half_width_x_m))
|
||||
live.sar3d_half_width_y_m = float(sar3d.get("half_width_y_m", live.sar3d_half_width_y_m))
|
||||
live.sar3d_half_width_z_m = float(sar3d.get("half_width_z_m", live.sar3d_half_width_z_m))
|
||||
live.sar3d_grid_nx = int(sar3d.get("grid_nx", live.sar3d_grid_nx))
|
||||
live.sar3d_grid_ny = int(sar3d.get("grid_ny", live.sar3d_grid_ny))
|
||||
live.sar3d_grid_nz = int(sar3d.get("grid_nz", live.sar3d_grid_nz))
|
||||
live.sar3d_ground_depth_m = float(sar3d.get("ground_depth_m", live.sar3d_ground_depth_m))
|
||||
live.sar3d_x_margin_m = float(sar3d.get("x_margin_m", live.sar3d_x_margin_m))
|
||||
live.sar3d_y_min_m = float(sar3d.get("y_min_m", live.sar3d_y_min_m))
|
||||
live.sar3d_y_max_m = float(sar3d.get("y_max_m", live.sar3d_y_max_m))
|
||||
live.sar3d_voxel_x_m = float(sar3d.get("voxel_x_m", live.sar3d_voxel_x_m))
|
||||
live.sar3d_voxel_y_m = float(sar3d.get("voxel_y_m", live.sar3d_voxel_y_m))
|
||||
live.sar3d_voxel_z_m = float(sar3d.get("voxel_z_m", live.sar3d_voxel_z_m))
|
||||
live.sar3d_norm_edge_margin_m = float(
|
||||
sar3d.get("norm_edge_margin_m", live.sar3d_norm_edge_margin_m)
|
||||
)
|
||||
live.sar3d_smooth_sigma = float(sar3d.get("smooth_sigma", live.sar3d_smooth_sigma))
|
||||
live.sar3d_comp_range_power = float(
|
||||
sar3d.get("comp_range_power", live.sar3d_comp_range_power)
|
||||
)
|
||||
live.sar3d_tx_per_segment = int(sar3d.get("tx_per_segment", live.sar3d_tx_per_segment))
|
||||
live.sar3d_use_compensation = bool(sar3d.get("use_compensation", live.sar3d_use_compensation))
|
||||
live.sar3d_normalize_pairs = bool(sar3d.get("normalize_pairs", live.sar3d_normalize_pairs))
|
||||
live.sar3d_bottom_view_deg = float(sar3d.get("bottom_view_deg", live.sar3d_bottom_view_deg))
|
||||
|
||||
Reference in New Issue
Block a user