Browse Source
feat(radar): Implement Radar Metrology Suite (Iteration 18)
feat(radar): Implement Radar Metrology Suite (Iteration 18)
- Engine: Modified CFAR and Processor to extract raw Range-Doppler and Range-Azimuth energy heatmaps. - Visuals: Integrated high-fidelity Matplotlib colormapping (Viridis/Magma) for Foxglove image streaming. - Data: Implemented 32-bit raw .npy persistence and JSONL telemetry for frame-level SNR analysis. - Tools: Added a dedicated verification utility for end-to-end signal flow validation. - Docs: Comprehensive documentation for the new 'Radar Lab' architecture in /intel/.1843_integration
6 changed files with 321 additions and 17 deletions
-
61intel/radar/metrology_suite/README.md
-
45scripts/ISOLATE/model_wrapper.py
-
20scripts/ISOLATE/sim_radar_utils/cfar_detector.py
-
24scripts/ISOLATE/sim_radar_utils/radar_processor.py
-
88scripts/analysis/verify_metrology_logic.py
-
98scripts/test_shenron.py
@ -0,0 +1,61 @@ |
|||
# Radar Metrology Suite: Implementation Hub 🛰️ |
|||
|
|||
This directory serves as the source of truth for the **C-SHENRON Radar Metrology Suite**. The goal of this suite is to transform the physics-based radar simulation from a "Black Box" into a transparent "Radar Lab" environment. |
|||
|
|||
--- |
|||
|
|||
## 🎯 1. The Core Objective |
|||
|
|||
To provide radar application engineers with 100% visibility into the signal processing chain. This is achieved by extracting and visualizing the internal energy states of the simulation before they are filtered into discrete points. |
|||
|
|||
### Key Deliverables: |
|||
1. **Visual Heatmaps:** Real-time Range-Doppler (RD) and Range-Azimuth (RA) streams in Foxglove. |
|||
2. **CFAR Transparency:** A visual mask of the adaptive threshold plane. |
|||
3. **Metrology Persistence:** Raw `.npy` storage of all FFT buffers for offline validation. |
|||
4. **Signal Telemetry:** JSON-based SNR and Noise Floor tracking. |
|||
|
|||
--- |
|||
|
|||
## 🏗️ 2. Technical Architecture |
|||
|
|||
The suite hooks into the **ISOLATE** engine at the following points: |
|||
|
|||
### A. Signal Generation (`heatmap_gen_fast.py`) |
|||
Current state: Successfully converts LiDAR points into complex ADC time-series. |
|||
* **Future Goal:** Implement Multi-Path interference logic. |
|||
|
|||
### B. Signal Processing (`radar_processor.py` & `cfar_detector.py`) |
|||
Current state: Performs 2D FFT and CA-CFAR detection. |
|||
* **Modification Plan:** |
|||
- Retain the **3D FFT Cube** (Range x Doppler x Angle). |
|||
- Extract the **Threshold Matrix** from `CA_CFAR.__call__`. |
|||
- Compute global **Range-Azimuth** energy maps via mean-Doppler reduction. |
|||
|
|||
--- |
|||
|
|||
## 🗺️ 3. Active Implementation Roadmap |
|||
|
|||
### Phase 1: Engine Heatmap Extraction (IN PROGRESS) |
|||
- [ ] Modify `cfar_detector.py` to return the `rd_avg_noise_power` (Threshold Baseline). |
|||
- [ ] Update `radar_processor.py` to capture RD and compute global RA heatmaps. |
|||
- [ ] Update `model_wrapper.py` to expose heatmaps and signal telemetry. |
|||
|
|||
### Phase 2: Signal-to-Visual Pipeline (PENDING) |
|||
- [ ] Implement 8-bit normalization and Viridis colormapping for radar image topics. |
|||
- [ ] Update `test_shenron.py` to register new `/heatmaps/` image channels in MCAP. |
|||
- [ ] Add JSON telemetry packaging for SNR/Noise metrics. |
|||
|
|||
### Phase 3: Raw Data Persistence (PENDING) |
|||
- [ ] Create `metrology/rd`, `metrology/ra`, and `metrology/cfar` directory structure. |
|||
- [ ] Implement `.npy` serialization for every frame during the simulation loop. |
|||
|
|||
--- |
|||
|
|||
## 🧭 4. Pointers for Future Agents |
|||
|
|||
* **Coordinate Frame:** Always remember: `Index 0 = Side (Y)`, `Index 1 = Forward (X)`. |
|||
* **Normalization:** When converting raw FFT magnitudes to images, use a log-dB scale to preserve dynamic range. |
|||
* **Performance:** Keep the `signal.convolve2d` calls optimized; we must maintain at least 1.0 FPS for UX. |
|||
|
|||
--- |
|||
*Created by Antigravity | Project: Fox CARLA ADAS | 2026-04-07* |
|||
@ -0,0 +1,88 @@ |
|||
import numpy as np |
|||
import os |
|||
import sys |
|||
from pathlib import Path |
|||
|
|||
# Add project root and ISOLATE paths |
|||
project_root = Path(__file__).parent.parent.parent |
|||
sys.path.append(str(project_root)) |
|||
sys.path.append(str(project_root / 'scripts' / 'ISOLATE')) |
|||
|
|||
try: |
|||
from scripts.ISOLATE.model_wrapper import ShenronRadarModel |
|||
print("✅ Model Import: SUCCESS") |
|||
except Exception as e: |
|||
print(f"❌ Model Import: FAILED ({e})") |
|||
sys.exit(1) |
|||
|
|||
def verify_signal_flow(frame_idx=190): |
|||
print(f"\n--- 🛰️ Radar Lab Verification (Frame {frame_idx}) ---") |
|||
|
|||
# 1. Initialize Engine |
|||
try: |
|||
model = ShenronRadarModel(radar_type='awrl1432') |
|||
print(f"✅ Engine Init: awrl1432 (Gain: {model.radar_obj.gain}dB)") |
|||
except Exception as e: |
|||
print(f"❌ Engine Init: FAILED ({e})") |
|||
return |
|||
|
|||
# 2. Load Real LiDAR Data |
|||
lidar_path = project_root / 'Shenron_debug' / 'logs' / 'lidar' / f"frame_{frame_idx:06d}.npy" |
|||
if not lidar_path.exists(): |
|||
print(f"❌ Lidar Data: NOT FOUND at {lidar_path}") |
|||
return |
|||
|
|||
data = np.load(lidar_path) |
|||
# Pad to 7-col [x,y,z,int,cos,obj,tag] if old 6-col format |
|||
if data.shape[1] == 6: |
|||
padded = np.zeros((data.shape[0], 7), dtype=np.float32) |
|||
padded[:, 0:3] = data[:, 0:3] |
|||
padded[:, 4:7] = data[:, 3:6] |
|||
data = padded |
|||
print(f"✅ Lidar Data Loaded: {data.shape[0]} points") |
|||
|
|||
# 3. Process Frame |
|||
try: |
|||
pcd = model.process(data) |
|||
print(f"✅ Signal Process: SUCCESS ({len(pcd)} detections)") |
|||
except Exception as e: |
|||
print(f"❌ Signal Process: FAILED ({e})") |
|||
return |
|||
|
|||
# 4. Verify Metrology Extraction |
|||
try: |
|||
met = model.get_last_metrology() |
|||
keys = met.keys() |
|||
expected = ["rd_heatmap", "ra_heatmap", "threshold_matrix"] |
|||
|
|||
status = True |
|||
for k in expected: |
|||
if k not in keys: |
|||
print(f"❌ Metrology Key Missing: {k}") |
|||
status = False |
|||
else: |
|||
val = met[k] |
|||
if np.max(val) <= 0: |
|||
print(f"❌ Metrology Data Null: {k} (Max: {np.max(val)})") |
|||
status = False |
|||
|
|||
if status: |
|||
print("✅ Metrology Extraction: ALL KEYS VALID") |
|||
print(f" -> RD Heatmap: {met['rd_heatmap'].shape} (Peak Power: {np.max(met['rd_heatmap']):.2e})") |
|||
print(f" -> RA Heatmap: {met['ra_heatmap'].shape} (Peak Power: {np.max(met['ra_heatmap']):.2e})") |
|||
print(f" -> CFAR Threshold: {met['threshold_matrix'].shape} (Mean: {np.mean(met['threshold_matrix']):.2e})") |
|||
|
|||
except Exception as e: |
|||
print(f"❌ Metrology Extraction: FAILED ({e})") |
|||
|
|||
# 5. Verify Signal Metrics |
|||
try: |
|||
metrics = model.get_signal_metrics() |
|||
print(f"✅ Signal Metrics: {metrics}") |
|||
if metrics['peak_snr_db'] <= 0: |
|||
print("⚠️ WARNING: Very low SNR detected (< 0dB). Check gain.") |
|||
except Exception as e: |
|||
print(f"❌ Signal Metrics: FAILED ({e})") |
|||
|
|||
if __name__ == "__main__": |
|||
verify_signal_flow(190) |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue