From 9c028d606ea357dd94cabd197faddd0ca4804495 Mon Sep 17 00:00:00 2001 From: rakadu1 Date: Thu, 7 May 2026 09:33:21 +0530 Subject: [PATCH] docs: update context.md manifest and fix radar SNR/telemetry logic - Expanded intel/internal/context.md with modern stage-based architecture and internal documentation structure. - Corrected SNR calculation in model_wrapper.py by dividing out the CFAR threshold. - Tuned CFAR threshold to 19dB for increased point cloud density. - Integrated stop flag detection into PipelineManager and ShenronOrchestrator for graceful halting. - Synchronized metrology gain offsets (-78.0dB) across production and testbench scripts. - Added 'ti_cascade' support to default radar list and testbench visualization. --- intel/internal/context.md | 180 ++++++++++++-------- scripts/ISOLATE/model_wrapper.py | 21 ++- scripts/ISOLATE/shenron_orchestrator.py | 11 ++ scripts/ISOLATE/sim_radar_utils/config.yaml | 4 +- scripts/data_to_mcap.py | 7 +- scripts/generate_shenron.py | 6 +- scripts/test_shenron.py | 7 +- src/pipeline/manager.py | 12 ++ 8 files changed, 162 insertions(+), 86 deletions(-) diff --git a/intel/internal/context.md b/intel/internal/context.md index 5d0a2d5..ad0b948 100644 --- a/intel/internal/context.md +++ b/intel/internal/context.md @@ -26,30 +26,40 @@ multi-modal sensor data that can be visualized and analysed in Foxglove Studio. Fox/ ├── dashboard.bat ← One-click launcher for the GUI orchestrator (Flask) ├── run.bat ← One-click launcher (activates carla312 conda env) -├── config.py ← All tuneable constants (FPS, sensor params, scenario defaults) +├── config.py ← All tuneable constants (FPS, sensor params, ego models) +├── gemini.md ← The primary agent instruction protocol +├── README.md ← Project setup and replication guide +│ +├── src/ +│ ├── main.py ← Thin CLI wrapper for the PipelineManager +│ ├── pipeline/ ← Stage-based orchestration (Sim → Shenron → MCAP → Video) +│ ├── processing/ ← Physics utilities (radial velocity, ADAS metrology) +│ ├── sensors.py ← SensorManager (camera, radar, lidar sync queues) +│ ├── recorder.py ← Asynchronous frame recorder (PNG / NPY / JSONL) +│ ├── scenario_loader.py ← Dynamic scenario loader via importlib +│ └── utils.py ← Shared project helpers (e.g., weather mapping) │ ├── scripts/ -│ ├── data_to_mcap.py ← Converts recorded dataset folders → .mcap files -│ └── data_inspector.py ← Utility to inspect / debug recorded datasets +│ ├── ISOLATE/ ← High-fidelity Shenron radar physics engine +│ │ ├── shenron_orchestrator.py ← Unified processing loop (production & testbench) +│ │ ├── model_wrapper.py ← Hardware spec syncer and physics execution +│ │ └── sim_radar_utils/ ← DSP 3D-FFT chain and visualization heatmaps +│ ├── generate_shenron.py ← Production wrapper invoked by the pipeline (with SSE telemetry) +│ ├── test_shenron.py ← Standalone iterative lab for radar testing +│ └── data_to_mcap.py ← Converts datasets into Foxglove `.mcap` formats │ ├── dashboard/ ← Flask backend and web frontend (GUI) │ ├── app.py ← Web server bridging API requests to run.bat -│ ├── static/ ← Frontend logic and styling assets -│ └── templates/ ← HTML views -│ -├── src/ -│ ├── main.py ← Orchestrator — scenario-agnostic entry point -│ ├── sensors.py ← SensorManager (camera, radar, lidar setup + sync queues) -│ ├── recorder.py ← Recorder (writes PNG / NPY / JSONL per frame) -│ ├── scenario_loader.py ← Dynamic scenario loader via importlib -│ └── utils.py ← Shared project helpers (weather mapping, etc.) +│ ├── static/ ← Frontend logic (app.js with SSE parser) +│ └── templates/ ← HTML views (index.html with ADAS Explorer panel) │ ├── scenarios/ -│ ├── __init__.py ← Package marker (empty to avoid side-effect imports) +│ ├── __init__.py │ ├── base.py ← ScenarioBase abstract class (the plugin contract) │ ├── braking.py ← Lead vehicle hard braking scenario │ ├── cutin.py ← Adjacent lane cut-in scenario -│ └── obstacle.py ← Static obstacle (traffic cone) scenario +│ ├── obstacle.py ← Static obstacle (traffic cone) scenario +│ └── showcase.py ← Complex Left-Turn Across Path demo │ ├── data/ ← Auto-created; one subfolder per recording session │ └── _YYYYMMDD_HHMMSS/ @@ -58,25 +68,37 @@ Fox/ │ ├── lidar/ ← frame_XXXXXX.npy (shape: [N, 4] — x/y/z/intensity) │ └── frames.jsonl ← One JSON record per frame (metadata + scenario info) │ +├── carla_examples/ ← Standard CARLA PythonAPI examples (not part of core Fox pipeline) +│ ├── tmp/ ← Staging area for IPC flags (e.g., stop.flag) │ -└── intel/ - ├── radar/ ← [CRITICAL] Physics, Material RCS, and Debug logs - │ ├── Shenron_debug.md ← The "Source of Truth" for radar calibration - │ └── Sceneset_deepdive.md ← Reflection & Electromagnetic math - ├── scenarios/ ← Operational manuals for driving simulations - │ ├── dashboard.md ← Web GUI Architecture - │ └── showcase.md ← Scenario-specific post-mortems - └── internal/ ← Project-wide context and developer guides - └── context.md ← This file (Primary entry point) +└── intel/ ← Project Knowledge Base & Developer Guides + ├── radar/ ← Deep-dives into physics math and RCS diagnostics + │ ├── core/ ← Calibration and architecture guides + │ ├── diagnostics/ ← Shenron_debug.md and energy suppression logs + │ ├── metrology_suite/ ← Heatmap and CFAR integration docs + │ ├── research/ ← Mathematical deep-dives (e.g. Isotropic Illumination) + │ └── ADC_Data.md ← FMCW and I/Q sampling reference + ├── scenarios/ ← Operational manuals for driving simulations + │ ├── braking.md + │ ├── dashboard.md + │ └── showcase.md + ├── internal/ ← Context manifest and legacy architecture references + │ ├── context.md ← This file + │ └── old_implement.md + └── CHRONICLES.md ← Running timeline of weekly updates and feature completions ``` --- ## Key Files — Detailed Reference -### `dashboard.bat` & `dashboard/` — Web GUI Orchestrator -A Flask-based web dashboard that provides an intuitive interface for running CARLA scenarios without the CLI. It dynamically fetches available scenarios and config params, translates user choices into `run.bat` commands as a background subprocess, and heavily streams the unbuffered Python stdout text back to the browser using Server-Sent Events (SSE). +### `dashboard.bat` & `dashboard/app.py` — Web GUI Orchestrator +A Flask-based web dashboard (`app.py`) that provides an intuitive interface for running CARLA scenarios without the CLI. +- **API Endpoints**: Dynamically fetches available scenarios (`/api/config`), scenario-specific parameters (`/api/scenario_params/`), and manages the CARLA simulator lifecycle (launching, killing, and putting the GPU into "idle" mode). +- **Execution**: Translates user choices into `run.bat` commands spawned as background subprocesses. +- **Streaming**: Streams the unbuffered Python stdout text back to the browser using Server-Sent Events (SSE). + > **Full Architecture details:** See `intel/scenarios/dashboard.md` for a complete breakdown of API routing and extension guidelines. --- @@ -98,38 +120,37 @@ Single source of truth for all simulation-wide defaults. It NO LONGER contains s --- -### `src/main.py` — Orchestrator -**Responsibilities:** CARLA connection, ego spawn, sensor init, scenario load, main loop, shutdown. -**Does NOT contain any scenario-specific logic.** +### `src/pipeline/` — Stage-Based Architecture +The simulation pipeline has been refactored into modular, sequential stages. +- **`src/pipeline/base.py`**: Defines the `PipelineContext` (shared state container) and the `PipelineStage` abstract base class (requires `name`, `run()`, and `cleanup()`). +- **`src/pipeline/manager.py`**: `PipelineManager` orchestrates the sequential execution of stages. It handles `skip_stages` flags, propagates `stop.flag` early halts, and guarantees `cleanup()` is called in reverse order for all started stages if an error occurs. +- **`src/pipeline/stages/`**: Individual worker implementations. + - `sim_stage.py` (`SimulationStage`): Runs the live CARLA capture loop. Connects to CARLA, handles Ego/NPC spawning, applies weather, runs the `world.tick()` loop, reads sensor queues, and records frames via the `Recorder`. Detects `tmp/stop.flag` for graceful shutdown. + - `shenron_stage.py` (`ShenronStage`): Runs the physics-based radar synthesis over the recorded dataset (calls `generate_shenron.py`). Preserves SSE progress tags (`[SHENRON_INIT]`) for the dashboard. + - `mcap_stage.py` (`McapStage`): Performs Foxglove serialization via `data_to_mcap.py`. + - `video_stage.py` (`VideoStage`): Stitches captured camera frames into MP4 previews. -**CLI:** -```powershell -python src/main.py --scenario braking --params "BRAKE_FRAME=100" -python src/main.py --scenario cutin --frames 120 --weather Rain -python src/main.py --list-scenarios -``` +--- + +### `src/main.py` — Entry Point +**Role:** Thin CLI wrapper that initializes the `PipelineManager`. +**Features:** +- Parses arguments and initializes the `PipelineContext`. +- Supports selective execution via flags like `--only-mcap`, `--only-shenron`, `--skip-shenron`, `--skip-mcap`, `--skip-sim`. +- Supports session reuse via `--session ` to re-process existing data (e.g., running Shenron or MCAP conversions on old recordings without re-running CARLA). + +--- + +### `src/utils.py` — Utilities +Contains shared project helpers, currently featuring `get_weather_preset(name)`, which safely maps simple string names (e.g., "Clear", "Rain") to the corresponding `carla.WeatherParameters` objects. + +--- -**New Flags:** -- `--params`: Scenario-specific adjustments (e.g. `SPEED=40`). -- `--weather`: Override scenario default (e.g. `Sunset`, `Wet`). -- `--no-record`: Dry-run simulation tracking with no disk I/O. - -**Execution flow:** -1. Parse args → `load_scenario(name)` → returns `ScenarioBase` instance -2. **Handle Parameter Injection**: Call `scenario.apply_parameters(args.params)` -3. Connect CARLA, settle sync mode, apply chosen weather (CLI > Scenario > Config) -4. Clear existing actors, spawn ego at **`ego_spawn_point`** (deterministic index or Transform) -5. `SensorManager.spawn_sensors()` → attach camera / radar / lidar to ego -6. `Recorder(scenario_name=scenario.name)` → creates `data/_/` -7. **Settle Physics**: Call `world.tick()` once to synchronize Ego location before setup -8. `scenario.setup(world, ego, traffic_manager)` -9. **Main loop** per frame: `world.tick()` → **IPC Stop Check (`tmp/stop.flag`)** → `sensor_manager.get_data()` → - `recorder.save(..., extra_meta=scenario.get_scenario_metadata())` → `scenario.step(frame, ego, pbar)` -10. `finally`: `scenario.cleanup()` → `sensor_manager.destroy()` → restore async mode - -> **Key invariant:** `main.py` never imports a scenario module by name. -> The CARLA import itself is deferred until after `--list-scenarios` early-exit so the dry-run -> works without a running CARLA server. +### `src/processing/physics.py` — Centralized Math & Physics +Standalone utilities for sensor data augmentation and ADAS metrology. +- **`calculate_radial_velocity()`**: Injects radial speed onto LiDAR points by projecting relative velocity onto the line-of-sight vector (used heavily by Shenron synthesis). +- **`calculate_relative_metrics()`**: Computes ground-truth Range, Azimuth, and Closing Velocity between the Ego and NPCs. +- **`get_actor_class()`**: Categorizes CARLA actors into broad ADAS classes (`vehicle`, `vru`, `pedestrian`). --- @@ -229,7 +250,6 @@ NPCs should be spawned with a **0.5m Z-offset (lift)** relative to the road wayp ### Implemented Scenarios -| File | Class | Trigger | Effect | | File | Class | Default Effect | Deterministic? | |---|---|---|---| | `braking.py` | `BrakingScenario` | Lead vehicle brakes at frame 80 | Yes (Spawn-and-Move) | @@ -241,38 +261,42 @@ All scenarios now encapsulate their own defaults and support CLI injection via ` --- -### `scripts/data_to_mcap.py` — MCAP Converter -Scans `data/` for subfolders containing `frames.jsonl` and converts each to a `.mcap` file. -Output is written as `data//.mcap` (skips if already exists). +### `scripts/` — Production Utilities & Shenron Engine +The `scripts/` folder houses post-processing and conversion utilities, most notably the physics-based radar synthesis engine (Shenron). + +#### Shenron Engine (`scripts/ISOLATE/`) +- **`scripts/ISOLATE/shenron_orchestrator.py`**: The unified orchestration engine. It ensures total parity between the production pipeline and iterative lab tests. Manages the directory structures, initializes radar models, processes LiDAR frames, and saves ADC data, pointclouds, and metrology `.npy` files. +- **`scripts/generate_shenron.py`**: The production wrapper invoked by the `ShenronStage`. Feeds the orchestrator with LiDAR data from the session and handles telemetry string reporting (`[SHENRON_INIT]`, `[SHENRON_STEP]`) back to the Flask dashboard. +- **`scripts/ISOLATE/model_wrapper.py`**: Defines `ShenronRadarModel`, providing an object-oriented interface over the underlying physics engine and DSP. It synchronizes hardware specifications (bandwidth, chirps) from `config.yaml` to the global configuration used by the processor. +- **`scripts/ISOLATE/sim_radar_utils/radar_processor.py`**: The core DSP chain. Simulates the TI mmWave hardware accelerators by executing the Range FFT, Doppler FFT, CFAR detection, peak grouping (NMS), and Angle/Azimuth beamforming (3D-FFT) to convert raw ADC cubes into a final 3D point cloud. +- **`scripts/ISOLATE/sim_radar_utils/plots.py`**: Visualization engine. Contains `FastHeatmapEngine` (a stateful matplotlib renderer optimized for high-speed frame-by-frame rendering by reusing figure memory) and other rendering functions for RA/RD heatmaps. + +#### Foxglove Serialization +- **`scripts/data_to_mcap.py`**: Converts the raw data folders (`data//`) into Foxglove-compatible `.mcap` files. -**Foxglove topics produced:** +**Foxglove topics produced by `data_to_mcap.py`:** | Topic | Schema | Content | |---|---|---| | `/camera` | `foxglove.CompressedImage` | Base64-encoded PNG | | `/lidar` | `foxglove.PointCloud` | X/Y/Z float32, Y-axis flipped for ROS convention | -| `/radar` | `foxglove.PointCloud` | Spherical → Cartesian conversion, Y-flipped | +| `/radar/*` | `foxglove.PointCloud` | Synthesized Shenron pointclouds | | `/ego_pose` | `foxglove.Pose` | Position + quaternion from yaw angle | +| `/metrology/*` | `foxglove.Grid` | Heatmaps and CFAR diagnostic matrices | > **Coordinate system note:** CARLA uses left-handed coords (Y increases right). > The converter negates Y and yaw to match ROS/Foxglove right-handed convention. -**Run:** -``` -python scripts/data_to_mcap.py -``` -Processes all unprocessed session folders in `data/` automatically. - --- ## Full Pipeline — End-to-End ``` 1. CARLA server running (CarlaUE4.exe) -2. run.bat braking → data/braking_/ (PNG + NPY + JSONL) -3. run.bat cutin → data/cutin_/ -4. run.bat obstacle → data/obstacle_/ -5. python scripts/data_to_mcap.py → data/*/.mcap +2. run.bat braking → [SIMULATION] → data/braking_/ (PNG + NPY + JSONL) +3. → [SHENRON] → synthesizes radar physics → data/braking_// +4. → [MCAP] → scripts/data_to_mcap.py → data/braking_/.mcap +5. → [VIDEO] → mp4 preview stitcher 6. Open .mcap in Foxglove Studio ``` @@ -328,6 +352,20 @@ class MyScenario(ScenarioBase): --- +## Knowledge Base (`intel/`) +The `intel/` directory is the project's brain, storing deep-dive documentation, research logs, and agent protocols. Unlike the codebase sections above, these are markdown files (`.md`). + +- **`intel/radar/`**: The core research repository for the Shenron physics engine. + - `ADC_Data.md`: Comprehensive technical reference on raw ADC data, FMCW, I/Q sampling, and signal synthesis. + - `core/`: High-level architecture of the Shenron engine, antenna gain calibration, and implementation guides. + - `diagnostics/`: Crucial debugging logs (e.g., `Shenron_debug.md` is the source of truth for RCS calibration and 3D energy suppression issues). + - `metrology_suite/`: Documentation on the Foxglove integration (RARD, CFAR heatmaps, Auto MCAP). + - `research/`: Mathematical deep-dives into physics problems like isotropic illumination and symmetric RCS reflection. +- **`intel/scenarios/`**: Operational manuals for driving simulations (e.g., dashboard architecture, showcase walkthroughs). +- **`intel/internal/`**: Project-wide context (like this file) and legacy implementation archives. + +--- + ## Known Limitations & Future Work | Area | Status | Notes | diff --git a/scripts/ISOLATE/model_wrapper.py b/scripts/ISOLATE/model_wrapper.py index 01a4978..853a019 100644 --- a/scripts/ISOLATE/model_wrapper.py +++ b/scripts/ISOLATE/model_wrapper.py @@ -18,7 +18,7 @@ if utils_root not in sys.path: from e2e_agent_sem_lidar2shenron_package.lidar import run_lidar from e2e_agent_sem_lidar2shenron_package.ConfigureRadar import radar from sim_radar_utils.radar_processor import RadarProcessor -from sim_radar_utils.utils_radar import reformat_adc_shenron +from sim_radar_utils.utils_radar import reformat_adc_shenron, config class ShenronRadarModel: def __init__(self, radar_type='radarbook'): @@ -155,12 +155,21 @@ class ShenronRadarModel: ra = self.last_metrology['ra_heatmap'] peak_mag = np.max(rd) - avg_noise = np.mean(noise) - snr = 10 * np.log10(peak_mag / avg_noise) if avg_noise > 0 else 0 + + # 'noise' here is actually the detection_gate (threshold_matrix) from CFAR. + # It already has the 10**(threshold/10) multiplier applied. + # We must divide it out to get the true physical average noise floor. + threshold_db = config['CFAR'].get('threshold', 20.0) + threshold_linear = 10 ** (threshold_db / 10.0) + + avg_noise_gate = np.mean(noise) + true_avg_noise = avg_noise_gate / threshold_linear + + snr = 10 * np.log10(peak_mag / true_avg_noise) if true_avg_noise > 0 else 0 metrics = { "peak_magnitude": float(peak_mag), - "avg_noise_floor": float(avg_noise), + "avg_noise_floor": float(true_avg_noise), "peak_snr_db": float(snr), "active_bins": int(np.sum(rd > noise)) } @@ -203,9 +212,9 @@ class ShenronRadarModel: else: ego_power = 0.0 - clutter_bins = (rd > avg_noise) & (rd <= noise) + clutter_bins = (rd > true_avg_noise) & (rd <= noise) clutter_ratio = np.sum(clutter_bins) / rd.size - avg_clutter = np.mean(rd[clutter_bins]) if np.any(clutter_bins) else avg_noise + avg_clutter = np.mean(rd[clutter_bins]) if np.any(clutter_bins) else true_avg_noise scr = 10 * np.log10(peak_mag / avg_clutter) if avg_clutter > 0 else snr metrics["dynamic_range_db"] = float(dyn_range) diff --git a/scripts/ISOLATE/shenron_orchestrator.py b/scripts/ISOLATE/shenron_orchestrator.py index 48ac771..52d0175 100644 --- a/scripts/ISOLATE/shenron_orchestrator.py +++ b/scripts/ISOLATE/shenron_orchestrator.py @@ -26,6 +26,10 @@ class ShenronOrchestrator: def init_models(self, output_root: Path): """Initializes models and prepares directory structure.""" specs = {} + # Resolve project root for stop flag check (root/scripts/ISOLATE/shenron_orchestrator.py) + self.project_root = Path(__file__).resolve().parents[2] + self.flag_path = self.project_root / "tmp" / "stop.flag" + for r_type in self.radar_types: try: print(f" - Initializing Shenron {r_type} engine...") @@ -127,3 +131,10 @@ class ShenronOrchestrator: continue return frame_results + + def check_stop_flag(self) -> bool: + """Check if a stop request has been issued by the user/dashboard.""" + if hasattr(self, 'flag_path') and self.flag_path.exists(): + print(f"\n[SHENRON] Stop flag detected at {self.flag_path}! Halting processing...") + return True + return False diff --git a/scripts/ISOLATE/sim_radar_utils/config.yaml b/scripts/ISOLATE/sim_radar_utils/config.yaml index da90ecc..0b3c9d2 100644 --- a/scripts/ISOLATE/sim_radar_utils/config.yaml +++ b/scripts/ISOLATE/sim_radar_utils/config.yaml @@ -50,8 +50,8 @@ ROS: CFAR: win_param: [9, 9, 5, 5] # [Est. width, Est. height, Guard width, Guard height] - Widened guard to 11x11 to isolate nearby clutter - threshold: 20 # dB (Standard) - Reverted to 20dB as per user request to maintain baseline sensitivity - peak_grouping: true # Toggle Non-Maximum Suppression (NMS) to eliminate range/doppler sidelobes + threshold: 19 # dB (Standard) - Lowered from 20dB to 19dB as a balanced compromise to boost density without excessive noise + peak_grouping: false # Toggle Non-Maximum Suppression (NMS) to eliminate range/doppler sidelobes Visualize: diff --git a/scripts/data_to_mcap.py b/scripts/data_to_mcap.py index 9ba9916..f4d64a0 100644 --- a/scripts/data_to_mcap.py +++ b/scripts/data_to_mcap.py @@ -138,6 +138,7 @@ FOXGLOVE_SCENE_UPDATE_SCHEMA = { FRUSTUM_SPECS = { "awrl1432": {"az_deg": 75.0, "el_deg": 20.0, "max_r": 150.0, "color": {"r": 1.0, "g": 0.5, "b": 0.0, "a": 1.0}}, "radarbook": {"az_deg": 60.0, "el_deg": 10.0, "max_r": 150.0, "color": {"r": 0.0, "g": 1.0, "b": 1.0, "a": 1.0}}, + "ti_cascade": {"az_deg": 60.0, "el_deg": 10.0, "max_r": 150.0, "color": {"r": 0.0, "g": 1.0, "b": 0.0, "a": 1.0}}, } def load_frames(folder_path): @@ -173,7 +174,7 @@ def convert_folder(folder_path): lidar_channel_id = writer.register_channel(topic="/lidar", message_encoding="json", schema_id=lidar_schema_id) pose_channel_id = writer.register_channel(topic="/ego_pose", message_encoding="json", schema_id=pose_schema_id) radar_channel_id = writer.register_channel(topic="/radar/native", message_encoding="json", schema_id=lidar_schema_id) - radar_types = ['awrl1432', 'radarbook'] + radar_types = ['awrl1432', 'radarbook', 'ti_cascade'] shenron_channels = {} met_channels = {} cached_axes = {} @@ -395,7 +396,7 @@ def convert_folder(folder_path): rd_p = os.path.join(met_dir, "rd", f"{frame_name}.npy") if os.path.exists(rd_p): rd_data = np.load(rd_p) - rd_db = 10 * np.log10(np.clip(rd_data, 1e-9, None)) - 68.0 + rd_db = 10 * np.log10(np.clip(rd_data, 1e-9, None)) - 78.0 # Updated offset: -10dB to match Iteration 37 recalibration b64 = render_engines[r_type]['rd'].render(np.flipud(rd_db)) if b64: msg = {"timestamp": {"sec": ts_sec, "nsec": ts_nsec}, "frame_id": "ego_vehicle", "format": "png", "data": b64} @@ -433,7 +434,7 @@ def convert_folder(folder_path): cfar_p = os.path.join(met_dir, "cfar", f"{frame_name}.npy") if os.path.exists(cfar_p): cf_data = np.load(cfar_p) - cf_db = 10 * np.log10(np.clip(cf_data, 1e-9, None)) - 68.0 + cf_db = 10 * np.log10(np.clip(cf_data, 1e-9, None)) - 78.0 b64 = render_engines[r_type]['cfar'].render(np.flipud(cf_db)) if b64: msg = {"timestamp": {"sec": ts_sec, "nsec": ts_nsec}, "frame_id": "ego_vehicle", "format": "png", "data": b64} diff --git a/scripts/generate_shenron.py b/scripts/generate_shenron.py index 594a626..c12bb09 100644 --- a/scripts/generate_shenron.py +++ b/scripts/generate_shenron.py @@ -57,7 +57,7 @@ def process_session(session_path): from scripts.ISOLATE.shenron_orchestrator import ShenronOrchestrator - orchestrator = ShenronOrchestrator(radar_types=['awrl1432', 'radarbook']) + orchestrator = ShenronOrchestrator(radar_types=['awrl1432', 'radarbook', 'ti_cascade']) # ----------------------------------------------------------------------- # TELEMETRY: Init Phase @@ -86,6 +86,10 @@ def process_session(session_path): for frame_idx, lidar_file in enumerate(lidar_files): frame_start = time.time() + # Stop if requested + if orchestrator.check_stop_flag(): + break + # Process through the unified orchestrator frame_results = orchestrator.process_frame(lidar_file, session_path, save_adc=True) diff --git a/scripts/test_shenron.py b/scripts/test_shenron.py index 363c390..301eccb 100644 --- a/scripts/test_shenron.py +++ b/scripts/test_shenron.py @@ -156,7 +156,7 @@ def run_testbench(iter_name): return iter_dir.mkdir(parents=True, exist_ok=True) - radar_types = ['awrl1432', 'radarbook'] + radar_types = ['awrl1432', 'radarbook', 'ti_cascade'] print(f"\n======================================") print(f"SHENRON TESTBENCH ITERATION: {iter_name}") @@ -427,7 +427,7 @@ def run_testbench(iter_name): cf_data = np.load(cf_p) # Convert threshold power floor to pure threshold DB mask similar to RD - cf_db = 10 * np.log10(np.clip(cf_data, 1e-9, None)) - 68.0 + cf_db = 10 * np.log10(np.clip(cf_data, 1e-9, None)) - 78.0 # Flip UD so Range 0 (ego) is at the bottom # Revert to dynamic (None) scaling so threshold logic is easily visible @@ -501,7 +501,8 @@ def run_testbench(iter_name): color_map = { "awrl1432": {"r": 1.0, "g": 0.5, "b": 0.0, "a": 1.0}, # Solid Orange - "radarbook": {"r": 0.0, "g": 1.0, "b": 1.0, "a": 1.0} # Solid Cyan + "radarbook": {"r": 0.0, "g": 1.0, "b": 1.0, "a": 1.0}, # Solid Cyan + "ti_cascade": {"r": 0.0, "g": 1.0, "b": 0.0, "a": 1.0} # Solid Green } f_color = color_map.get(r_type, {"r": 1.0, "g": 1.0, "b": 1.0, "a": 1.0}) diff --git a/src/pipeline/manager.py b/src/pipeline/manager.py index 1ee2f04..f6871d6 100644 --- a/src/pipeline/manager.py +++ b/src/pipeline/manager.py @@ -51,6 +51,12 @@ class PipelineManager: True if all stages completed successfully. """ started_stages = [] + + # Resolve project root for stop flag check + import os + from pathlib import Path + root_dir = Path(__file__).resolve().parents[2] + flag_path = root_dir / "tmp" / "stop.flag" try: for stage in self._stages: @@ -59,6 +65,12 @@ class PipelineManager: print(f"[PIPELINE] Skipping stage: '{stage.name}'") continue + # Check if user requested a stop via dashboard + if os.path.exists(flag_path): + print(f"[PIPELINE] Stop flag detected! Halting before " + f"starting '{stage.name}'.") + break + # Check if a previous stage failed if not ctx.success: print(f"[PIPELINE] Halting — previous stage failed. "