Rework radar system

This commit is contained in:
Ayzen
2026-10-06 12:32:26 +03:00
parent 552cdc688a
commit 5bb86b7165
299 changed files with 13537 additions and 27763 deletions
+19 -46
View File
@@ -19,24 +19,20 @@ never see time jump backwards. ``collection_id`` increases forever.
from __future__ import annotations
import argparse
from contextlib import ExitStack
import json
import logging
from pathlib import Path
import re
import signal
import statistics
import threading
import time
import numpy as np
from python_app.logging_setup import coerce_level
from python_app.models.dataset_model import ComboKey, SweepCollection, TraceData
from python_app.models.run_config_model import RunConfigModel
from python_app.orchestration.shm import ShmRingWriter
from python_app.storage.npz.serialize import RAW_MAGIC, serialize_trace_collection
from python_app.orchestration.shm.decoder import RAW_MAGIC
from python_app.scripts.producer_common import RawPublisher, load_producer_config
from python_app.storage.npz.serialize import serialize_trace_collection
logger = logging.getLogger(__name__)
@@ -105,10 +101,8 @@ def _load_dataset(dataset_dir: Path, combos: list[ComboKey], expected_points: in
)
s21 = np.load(s21_path, allow_pickle=False).astype(np.complex64, copy=False)
freq = np.load(freq_path, allow_pickle=False).astype(np.float32, copy=False)
s11_path = folder / f"i{combo.input}_o{combo.output}_s11.npy"
s11 = np.load(s11_path, allow_pickle=False).astype(np.complex64, copy=False) if s11_path.is_file() else np.zeros_like(s21)
if freq.ndim != 1 or s21.shape != freq.shape or s11.shape != freq.shape:
raise RuntimeError(f"Snapshot {name}: frequency, S11 and S21 must be equally sized 1D arrays")
if freq.ndim != 1 or s21.shape != freq.shape:
raise RuntimeError(f"Snapshot {name}: frequency and S21 must be equally sized 1D arrays")
if expected_points and s21.size != expected_points:
raise RuntimeError(
f"{s21_path}: {s21.size} points, but radar.sweep.points = "
@@ -119,7 +113,6 @@ def _load_dataset(dataset_dir: Path, combos: list[ComboKey], expected_points: in
TraceData(
combo=combo,
frequency_hz=freq,
s11=s11,
s21=s21,
capture_start_ns=window[0],
capture_end_ns=window[1],
@@ -157,7 +150,6 @@ def _build_collection(
TraceData(
combo=source.combo,
frequency_hz=source.frequency_hz,
s11=source.s11,
s21=source.s21,
capture_start_ns=capture[0],
capture_end_ns=capture[1],
@@ -174,22 +166,7 @@ def _build_collection(
def main() -> int:
"""Replay the configured dataset into the raw SHM rings until stopped."""
parser = argparse.ArgumentParser(description="Replay a recorded dataset to raw SHM rings")
parser.add_argument("--config", required=True, type=Path, help="Path to run_config.json")
args = parser.parse_args()
logging.basicConfig(level=logging.INFO, format="%(levelname)s %(name)s: %(message)s")
stop_requested = threading.Event()
def request_stop(_signum: int, _frame: object) -> None:
stop_requested.set()
signal.signal(signal.SIGINT, request_stop)
signal.signal(signal.SIGTERM, request_stop)
config = RunConfigModel.load_from_path(args.config)
config.apply_device_model_constraints()
logging.getLogger("python_app").setLevel(coerce_level(config.logging.level))
_, config, stop_requested = load_producer_config("Replay a recorded dataset to raw SHM rings")
replay = config.radar.replay
if not replay.dataset_dir:
@@ -211,23 +188,15 @@ def main() -> int:
cleanup = ExitStack()
try:
raw_writer = ShmRingWriter(
config.rings.raw.name,
config.rings.raw.capacity,
config.rings.raw.slot_size_bytes,
)
cleanup.callback(raw_writer.close)
raw_tap_writer = ShmRingWriter(
config.rings.raw_tap.name,
config.rings.raw_tap.capacity,
config.rings.raw_tap.slot_size_bytes,
)
cleanup.callback(raw_tap_writer.close)
publisher = RawPublisher(config.rings, cleanup)
payload_size = len(serialize_trace_collection(
_build_collection(dataset, 0, 1, time.monotonic_ns()), RAW_MAGIC
_build_collection(dataset, 0, 1, time.monotonic_ns()), RAW_MAGIC, require_capture_window=True,
))
for ring in (config.rings.raw, config.rings.raw_tap):
for name in ("raw", "raw_tap", "preprocessed", "preprocessed_tap"):
ring = getattr(config.rings, name)
if name.endswith("_tap") and not ring.name:
continue
if ring.slot_size_bytes < payload_size:
raise ValueError(f"Replay requires {payload_size} bytes per slot; ring {ring.name} has {ring.slot_size_bytes}")
@@ -257,10 +226,14 @@ def main() -> int:
break
collection = _build_collection(dataset, index, collection_id, frame_ns)
payload = serialize_trace_collection(collection, RAW_MAGIC)
if not raw_writer.push(payload):
# Acquisition identity uses real monotonic time, even at accelerated
# replay rates. Keep the recorded duration for legacy GPR and the
# virtual per-trace timeline for SAR aperture/motion reconstruction.
duration_ns = collection.capture_end_ns - collection.capture_start_ns
collection.capture_start_ns = time.monotonic_ns()
collection.capture_end_ns = collection.capture_start_ns + duration_ns
if not publisher.publish(collection):
raise RuntimeError(f"Replay raw ring rejected collection {collection_id}")
raw_tap_writer.push(payload)
if collection_id == 1 or collection_id % 50 == 0:
logger.info(
"replay collection %d published (snapshot %d/%d, cycle %d)",