Validate radar configs, prototype DSP, and generate training data without hardware. End-to-end 60 GHz FMCW pipeline with physical ray tracing, MIMO beamforming, and vital-signs-grade vibration recovery — all behind an interactive web dashboard. End-to-end 60 GHz FMCW radar simulation built on top of RadarSimPy. Models a TI xWR6843AOP radar with 3TX/4RX MIMO, performs CPU-based ray tracing with full bounce logging, GPU-accelerated pulse capture for vibration sensing, and serves the results through an interactive web dashboard with real-time 3D ray inspection.
The vibration recovery pipeline demonstrates the same DSP foundation used for non-contact vital signs monitoring (heart rate, respiration) on mmWave radar. The pipeline simulates a two-target scene (one vibrating, one static) using physical ray tracing, then processes the raw baseband through a complete DSP chain:
- Pass 1 — CPU Ray Tracing: Fires rays at STL mesh targets, logs every bounce path to HDF5, generates a 1-pulse baseband snapshot.
- Pass 2 — GPU Pulse Capture: Runs 2500 chirps on GPU to build a slow-time history of the vibrating target's micro-displacement.
- DSP Processing: Produces Bartlett beamforming maps, ray-traced ground truth masks, and vibration spectral analysis.
- Web Dashboard: FastAPI + Three.js + Plotly interface where hovering over any pixel in the ground truth map renders the corresponding 3D ray paths in real time.
- RF engineers validating chirp configs before silicon arrives.
- ML teams generating synthetic radar data.
- Biomedical researchers prototyping non-contact vital signs.
- Students learning mmWave DSP end-to-end.
| Bartlett Beamforming (CPU Raytracing) | Ground Truth Mask (Ray-Traced) |
|
|
Target displacement recovery (5 Hz)
FFT spectrum confirming the vibration frequency.
Models the TI xWR6843AOP (60–64 GHz, Antenna-on-Package):
| Parameter | Value |
|---|---|
| Frequency | 60.0 – 63.2 GHz |
| Bandwidth | ~3.16 GHz |
| Range Resolution | ~4.7 cm |
| TX / RX | 3 / 4 (12 virtual channels, TDM-MIMO) |
| ADC Sampling | 3 Msps, 416 samples/chirp |
| PRP (TDM) | ~437 µs |
Full derivation with antenna geometry and TI profileCfg parameters: radar_config.md
fmcw-radar-simulation/
├── lib/ # Core radar engine
│ ├── radar_config.py # xWR6843AOP hardware parameters
│ ├── radar_targets.py # Target placement & vibration model
│ ├── radar_viz.py # Plotly/Matplotlib visualization utilities
│ └── ideal_gt.py # HDF5 ray log parser & ground truth generator
├── src/radar_studio/ # Studio orchestrator
│ ├── run_studio.py # Pipeline entry point (runs all passes)
│ ├── config.json # Scene + simulation parameters
│ ├── simulation/
│ │ ├── sim_pass1_cpu.py # CPU ray tracing pass
│ │ ├── sim_pass2_gpu.py # GPU vibration pulse capture
│ │ ├── dsp_processor.py # Beamforming, GT mask, vibration DSP
│ │ └── visualizer.py # Diagnostic plot generation
│ └── web/
│ ├── app.py # FastAPI dashboard server
│ └── static/index.html # Three.js + Plotly frontend
├── models/curated/cross.stl # Target 3D mesh (cross shape)
├── envs/ # RadarSimPy binaries (not tracked)
│ ├── cpu/radarsimpy/ # CPU build (compiled extensions)
│ └── gpu/radarsimpy/ # GPU/CUDA build (compiled extensions)
├── radar_config.md # Detailed radar parameter reference
└── requirements.txt
- Python 3.10–3.12
- NVIDIA GPU + CUDA (for Pass 2; Pass 1 runs on CPU only)
- RadarSimPy pre-compiled binaries from radarsimx.com
RadarSimPy is not pip-installable. It ships as platform-specific compiled extensions (
.pydon Windows,.soon Linux) and must be placed manually. The simulation requires two separate builds — one for CPU ray tracing and one for GPU (CUDA) pulse generation.
git clone https://github.com/veylantis/fmcw-radar-simulation.git
cd fmcw-radar-simulationpython -m venv .venv
source .venv/bin/activate # Linux / macOS
source .venv/Scripts/activate # Windows (Git Bash)pip install -r requirements.txtDownload both CPU and GPU builds from radarsimx.com. Each download contains a radarsimpy/ folder with compiled extensions for your platform.
Place them so the directory structure looks like:
envs/
├── cpu/
│ └── radarsimpy/
│ ├── __init__.py
│ ├── radarsimpy.* # CPU-only build
│ └── ...
└── gpu/
└── radarsimpy/
├── __init__.py
├── radarsimpy.* # CUDA-enabled build
└── ...
You can copy the folders directly, or symlink if you already have them elsewhere:
# Linux / macOS
ln -s /path/to/your/radarsimpy-cpu envs/cpu
ln -s /path/to/your/radarsimpy-gpu envs/gpu# Windows (PowerShell, run as admin)
New-Item -ItemType Junction -Path envs\cpu -Target "X:\path\to\radarsimpy-cpu"
New-Item -ItemType Junction -Path envs\gpu -Target "X:\path\to\radarsimpy-gpu"Edit src/radar_studio/config.json and set the env_path_cpu and env_path_gpu fields to absolute paths on your system:
{
"simulation": {
"env_path_cpu": "/absolute/path/to/envs/cpu",
"env_path_gpu": "/absolute/path/to/envs/gpu"
}
}The orchestrator prepends these to PYTHONPATH at runtime so that each simulation pass imports the correct RadarSimPy build without conflicts.
python src/radar_studio/run_studio.pyThis executes all four phases sequentially. Output goes to src/radar_studio/workspace/ (arrays, ray logs, plots) and src/radar_studio/web/data/ (dashboard artifacts).
Expect approximately:
- Pass 1 (CPU): 2–5 min depending on
cpu_density - Pass 2 (GPU): 5–20 min depending on
gpu_pulsesand GPU model - DSP + Visualizer: < 30 sec
After the pipeline completes:
cd src/radar_studio/web
python app.pyOpen http://127.0.0.1:8000 in your browser.
The dashboard loads the ground truth heatmap on the left and the 3D scene on the right. Hover over any pixel in the heatmap to render the corresponding ray bounce paths in real time.
Edit src/radar_studio/config.json to modify the scene:
{
"scene": {
"vibrating_cross": {
"location": [2.5, -1.0, 0.0],
"rotation": [0.0, 0.0, 0.0],
"vibration_freq_hz": 5.0,
"vibration_amp_m": 0.002
},
"static_cross": {
"location": [2.5, 0.0, 0.0],
"rotation": [30.0, 45.0, 15.0]
}
},
"simulation": {
"cpu_density": 0.3,
"gpu_density": 0.1,
"gpu_pulses": 2500
}
}cpu_density/gpu_density— ray density per wavelength². Higher = more accurate but slower.gpu_pulses— number of slow-time samples. Controls vibration frequency resolution (PRF / N_pulses).
Pass 1 fires rays from the radar origin toward the scene meshes using RadarSimPy's CPU solver. Every ray-surface intersection (including multi-bounce reflections) is logged to HDF5 files with:
- Hit coordinates (range, azimuth)
- Received power (path loss + RCS)
- Full 3D bounce path geometry
The DSP processor then bins these hits into a range-azimuth grid to produce the ground truth mask — a noise-free reference map showing exactly where the targets are, based purely on geometry.
The same 1-pulse baseband from Pass 1 goes through range FFT (Chebyshev-windowed) followed by spatial FFT across the 8 azimuth virtual channels. This produces the conventional Bartlett power map — what the radar actually "sees" through its array processing, including sidelobes and resolution limits.
Pass 2 captures 2500 consecutive pulses with the vibrating target oscillating at a configured frequency. The DSP pipeline:
- Beamforms each pulse at the target's range-angle cell
- Extracts the complex phase history
- Unwraps and detrends the phase
- Converts phase to displacement via
Δr = Δφ · λ / (4π) - Computes the FFT spectrum to identify vibration frequency
This project is licensed under the MIT License.
RadarSimPy is a separate product — see radarsimx.com for its licensing terms.




