Rework radar system
This commit is contained in:
@@ -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)",
|
||||
|
||||
Reference in New Issue
Block a user