init commit
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
"""Manual smoke scenario for end-to-end pipeline check without GUI."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import ctypes
|
||||
import ctypes.util
|
||||
import os
|
||||
from pathlib import Path
|
||||
import sys
|
||||
import time
|
||||
|
||||
import numpy as np
|
||||
|
||||
PROJECT_ROOT = Path(__file__).resolve().parents[2]
|
||||
if str(PROJECT_ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(PROJECT_ROOT))
|
||||
|
||||
from python_app.models.dataset_model import ComboKey, SweepCollection, TraceData
|
||||
from python_app.models.run_config_model import RunConfigModel
|
||||
from python_app.orchestration.config_writer import ConfigWriter
|
||||
from python_app.orchestration.process_supervisor import ProcessSupervisor
|
||||
from python_app.orchestration.shm_reader import ShmRingReader
|
||||
from python_app.storage.npz_store import NpzStore, radar_key_from_config
|
||||
|
||||
|
||||
def _make_unique_ring_name(prefix: str) -> str:
|
||||
"""Build unique POSIX SHM ring name."""
|
||||
stamp = time.monotonic_ns()
|
||||
return f"/{prefix}_{os.getpid()}_{stamp}"
|
||||
|
||||
|
||||
def _shm_unlink(name: str) -> None:
|
||||
"""Best-effort unlink for POSIX shared-memory object."""
|
||||
libc_name = ctypes.util.find_library("c")
|
||||
if libc_name is None:
|
||||
return
|
||||
|
||||
libc = ctypes.CDLL(libc_name, use_errno=True)
|
||||
libc.shm_unlink.argtypes = [ctypes.c_char_p]
|
||||
libc.shm_unlink.restype = ctypes.c_int
|
||||
|
||||
result = libc.shm_unlink(name.encode("utf-8"))
|
||||
if result == 0:
|
||||
return
|
||||
|
||||
err = ctypes.get_errno()
|
||||
if err != 2: # ENOENT
|
||||
raise OSError(err, f"shm_unlink failed for {name}")
|
||||
|
||||
|
||||
def build_synthetic_collection(config: RunConfigModel, value_scale: float) -> SweepCollection:
|
||||
"""Build synthetic sweep collection for all configured switch combos."""
|
||||
traces: list[TraceData] = []
|
||||
combos = RunConfigModel.build_full_combos(config.input_switch.positions, config.output_switch.positions)
|
||||
|
||||
for combo in combos:
|
||||
frequency_hz = np.linspace(
|
||||
config.radar.sweep.start_hz,
|
||||
config.radar.sweep.stop_hz,
|
||||
config.radar.sweep.points,
|
||||
dtype=np.float32,
|
||||
)
|
||||
phase = np.linspace(0.0, np.pi * 2.0, config.radar.sweep.points, dtype=np.float32)
|
||||
s21 = value_scale * (np.cos(phase) + 1j * np.sin(phase)).astype(np.complex64)
|
||||
|
||||
traces.append(
|
||||
TraceData(
|
||||
combo=ComboKey(input_pos=combo.input, output_pos=combo.output),
|
||||
frequency_hz=frequency_hz,
|
||||
s21=s21,
|
||||
)
|
||||
)
|
||||
|
||||
return SweepCollection(collection_id=1, monotonic_ns=time.monotonic_ns(), traces=traces)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
"""Run manual smoke-test pipeline scenario."""
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--duration", type=float, default=3.0)
|
||||
args = parser.parse_args()
|
||||
|
||||
project_root = PROJECT_ROOT
|
||||
store = NpzStore(project_root / "python_app/data")
|
||||
config_writer = ConfigWriter(project_root / "python_app/runtime")
|
||||
supervisor = ProcessSupervisor(project_root)
|
||||
|
||||
config = RunConfigModel.load_from_path(project_root / "run_config.json")
|
||||
config.radar.driver_mode = "mock"
|
||||
config.input_switch.driver_mode = "mock"
|
||||
config.output_switch.driver_mode = "mock"
|
||||
config.combos = RunConfigModel.build_full_combos(config.input_switch.positions, config.output_switch.positions)
|
||||
config.rings.raw.name = _make_unique_ring_name("radar_raw_smoke")
|
||||
config.rings.raw_tap.name = _make_unique_ring_name("radar_raw_tap_smoke")
|
||||
config.rings.preprocessed.name = _make_unique_ring_name("radar_preprocessed_smoke")
|
||||
config.rings.preprocessed_tap.name = _make_unique_ring_name("radar_preprocessed_tap_smoke")
|
||||
config.rings.results.name = _make_unique_ring_name("radar_results_smoke")
|
||||
|
||||
radar_key = radar_key_from_config(
|
||||
model_name=config.radar.model,
|
||||
serial=config.radar.serial,
|
||||
sweep_start_hz=config.radar.sweep.start_hz,
|
||||
sweep_stop_hz=config.radar.sweep.stop_hz,
|
||||
sweep_points=config.radar.sweep.points,
|
||||
ifbw_hz=config.radar.sweep.if_bandwidth_hz,
|
||||
power_dbm=config.radar.sweep.power_dbm,
|
||||
)
|
||||
|
||||
calibration_set = build_synthetic_collection(config, value_scale=1.0)
|
||||
reference_set = build_synthetic_collection(config, value_scale=0.3)
|
||||
|
||||
store.save_set("calibration", radar_key, "smoke_cal", calibration_set)
|
||||
store.save_set("reference", radar_key, "smoke_ref", reference_set)
|
||||
|
||||
calibration_bundle, reference_bundle = config_writer.prepare_bundles(store, radar_key, "smoke_cal", "smoke_ref")
|
||||
config.preprocess.calibration_set = "smoke_cal"
|
||||
config.preprocess.reference_set = "smoke_ref"
|
||||
config.preprocess.calibration_bundle_path = str(calibration_bundle)
|
||||
config.preprocess.reference_bundle_path = str(reference_bundle)
|
||||
|
||||
config_path = config_writer.write(config, project_root / "python_app/runtime/run_config_smoke.json")
|
||||
|
||||
result_reader: ShmRingReader | None = None
|
||||
try:
|
||||
supervisor.start(config_path)
|
||||
result_reader = ShmRingReader(config.rings.results.name)
|
||||
deadline = time.monotonic() + args.duration
|
||||
received = 0
|
||||
|
||||
while time.monotonic() < deadline:
|
||||
result = result_reader.pop_result_collection() if result_reader is not None else None
|
||||
if result is not None:
|
||||
received += 1
|
||||
time.sleep(0.02)
|
||||
|
||||
print(f"Received result collections: {received}")
|
||||
finally:
|
||||
supervisor.stop_all()
|
||||
if result_reader is not None:
|
||||
result_reader.close()
|
||||
_shm_unlink(config.rings.raw.name)
|
||||
_shm_unlink(config.rings.raw_tap.name)
|
||||
_shm_unlink(config.rings.preprocessed.name)
|
||||
_shm_unlink(config.rings.preprocessed_tap.name)
|
||||
_shm_unlink(config.rings.results.name)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user