Skip to main content

WiTwin Radar

WiTwin Radar is a GPU-accelerated, differentiable radar simulator. A simulation consumes a witwin.core world and one-way propagation from witwin.channel, composes round trips, applies scattering and sensor/frontend effects, synthesizes radar waveforms, and produces typed signal-processing products.

The repository uses a breaking, concept-axis architecture. Compatibility modules and deprecated aliases are intentionally not retained.

For dynamic FMCW, Radar.simulate(..., motion=Motion.adaptive(...)) uses phase-controlled temporal interpolation and batched propagation. Import Motion from witwin.radar. Per-ADC sampling remains the exhaustive reference and is what the default Motion.auto() resolves to whenever anything moves; adaptive topology probes cannot certify arbitrarily brief events between observations. Receiver effects operate on beat samples before the requested range transform, and shared oscillator noise uses actual timestamps and path delays. Adaptive replay reuses a complete LOS topology in an empty world and batches propagation, joins, antenna weighting and ADC synthesis. The measured 128-sample × 1024-chirp rotor scene takes 270.99 ms per frame on RTX 5080 at the default interpolation order, or 174.28 ms at nodes=5 (the 2026-09-17 comparison); see performance evidence and limits. Dynamic FMCW simulation refreshes the scene at each ADC observation by default, including TDM timing. Motion.chirp() selects the faster stop-and-hop approximation. See docs/pipeline_guide.md for trajectories, path completeness, and typed micro-Doppler timestamps.

Installation and runtime

pip install witwin-radar[channel]

CUDA is required for propagation and native waveform synthesis. CPU construction and PyTorch signal-processing workflows remain useful without a GPU. Linux and Windows are supported.

Release policy is explicit and conservative:

  • CPython 3.10-3.14;
  • Linux wheels target manylinux_2_28_x86_64;
  • one packaged _radar_native library plus its identity sidecars;
  • exact Torch/CUDA/ABI runtime identity;
  • no JIT fallback and no success-by-loader-refusal path.

The release build matrix currently uses Torch 2.10 with CUDA 12.8 for each supported Python version. A different Torch or CUDA identity requires a separately built and validated artifact.

Architecture

witwin.core Scene/DynamicScene
        |
        v
Radar simulation session -> Channel propagation -> round-trip path composition
        |                                      |
        +-> scattering -> sensor/frontend -----+
                                               v
                       FMCW / OFDM / pulsed synthesis
                                               v
                          typed processing products

Production ownership is intentionally shallow:

  • witwin/radar/radar.py — the flat Radar record, its waveforms, its pose, and the flat-configuration loader;
  • witwin/radar/targets.py — the target vocabulary: positions, cross section or strength, trajectory, and identity;
  • witwin/radar/simulation.py — scene-session execution, motion sampling, and frame results;
  • witwin/radar/channel.py — the only production Channel importer;
  • witwin/radar/propagation.py and witwin/radar/paths.py — propagation policy and round-trip composition;
  • witwin/radar/scattering.py, sensors.py, and frontend.py — radar physics around the path;
  • witwin/radar/synthesis/ — native waveform synthesis;
  • witwin/radar/processing/ — range, Doppler, angle, detection, and tracking products;
  • witwin/radar/cuda/ — the native runtime and kernels.

FMCW: spectrum first

FMCW synthesis directly generates the Dirichlet range spectrum in native CUDA by default. The default is output_domain="spectrum" in both the typed FMCW spec and the flat radar configuration. Set output_domain="beat" only when a caller explicitly needs a synthesized time-domain beat signal.

The result carries named axes and an output-domain field. Downstream processing uses that metadata, so it does not apply an extra range FFT to a spectrum or omit the FFT for beat samples.

Main API

Radar is one flat immutable record in SI units. A sub-record appears only where a field has variants (the waveform, the antenna pattern, the motion sampling) or where several numbers are coupled (Noise, Agc, Adc); the array, the pose and the one-number receive stages are fields.

from witwin.radar import Motion, Noise, PointTargets, Radar

radar = Radar.from_dict(CONFIG, noise=Noise(figure=10.0), seed=20260727, look_at=(0.0, 0.0, -3.0))
targets = PointTargets(positions=[(0.0, 0.0, -3.0)], rcs=1.0)
result = radar.simulate(scene, targets, times=(0.0, 0.1, 0.2), los=True, reflections=1, motion=Motion.auto())

Radar.from_dict reads the flat FMCW configuration format examples and config files use, in its vendor units, and is the only place those units are read. Every other field is a keyword override in SI, so a receive chain and a pose attach in the same call. Radar.from_json loads the same mapping from a file. Keys outside the format are refused by name rather than stored.

The radar has four verbs, and they are two halves and their fusion:

  • radar.trace(scene, targets, times=...) runs the world half — world sampling, Channel epochs, round-trip composition, scattering and antenna weighting — and returns a Paths;
  • radar.echo(paths) runs the instrument half — waveform synthesis, the receive chain, the output domain and the processing axes — and returns a Result;
  • radar.simulate(...) fuses the two and stacks every frame;
  • radar.stream(...) fuses them and yields one frame at a time, so a sequence too long to hold as a stacked cube is still producible.

All four share one session loop and one synthesis route, so radar.echo(radar.trace(...)) is bit-identical to radar.simulate(...), asserted with torch.equal across every motion kind, both FMCW output domains, with and without a receive chain, and with oscillator phase noise.

The split buys reuse and costs retention. One trace can be echoed by several receive chains, seeds or output domains without re-solving the world, and a radar the rows do not describe is refused by name — including a phase-noise receiver against paths traced without ADC instants. Against that, a Paths holds every evaluated observation's rows, roughly twenty bytes per live row summed over every evaluated observation of every frame, so an ADC-refreshed sequence is expensive. simulate never pays that: a frame's observations stay a generator it drains one at a time. An adaptive trace retains its accepted partition rather than its schedule, so its retained row count is the probes and not the observations.

Radar.simulate(...) accepts a Core scene, a required PointTargets or StructureTargets record that says where the scatterers are and how strongly they scatter, the frame times, the propagation request as los and reflections, the in-frame resampling as motion, and the differentiation mode as grad. Radar.trace(...) takes the same arguments. simulate and echo both return Result, whose cube is organized as [frame, TX, RX, slow, fast], whose axis_names names those axes, and whose axes is the ProcessingAxes record every processing stage reads, built once from the waveform spec and the array that produced the cube.

Radar.stream(...) runs the same session and yields each frame as its own one-frame Result. Use it when the sequence is longer than the stacked cube can be held in device memory; the frames are bit-exact against simulate and peak allocation no longer scales with the frame count.

A radar is never edited in place: Radar.replace(...) and Radar.to(device) return a new one, and a radar retains nothing from a run, so a result is the only thing that can say which frame a diagnostic describes.

The root exports the whole happy path in sixteen names: Adc, Agc, Aspect, Fmcw, Frame, Motion, Noise, Ofdm, Paths, Pattern, PointTargets, Pulsed, Radar, Result, StructureTargets and processing. Advanced records stay importable from their owner modules.

Signal processing is exported through witwin.radar.processing; typed products include processing cubes, range profiles, Range-Doppler maps, beam cubes, detections, and point clouds. result.frame(i) returns the Frame that pairs one frame's cube with that metadata; its processing_cube(), range_profile(), range_doppler(), array() and points() are facades over that package and compute nothing themselves.

See docs/pipeline_guide.md for the full contract and examples/single_point.py for a maintained end-to-end example.

Tests and governance

pytest tests/
pytest tests/ --gpu
python ci/run_ci_tier.py quick

The quick tier includes canonical Ruff formatting/lint, exact-clone detection, architectural, public-surface, documentation, release-claim, workflow-reference, and compatibility-removal gates. Required-Channel workflows install the Channel extra, record its build fingerprint, and permit zero skips caused by a missing Channel runtime.

No benchmark, GPU result, wheel load, or remote workflow is claimed as executed merely because its command exists. Current performance evidence and outstanding measurements are documented in PERFORMANCE.md.

Examples

python -m examples.single_point
python -m examples.music_imaging
python -m examples.rgbd_range_doppler --input path/to/depths.npz

All maintained scene-driven examples require CUDA and Channel.

Documentation

  • Pipeline: docs/pipeline_guide.md
  • Development standard: docs/dev/standards/radar-adr-021-code-layout-comments-and-mathematical-ownership.md
  • Consolidation plan: docs/dev/plans/radar-concept-axis-layout-and-module-consolidation-plan.md
  • Governance inventory: docs/dev/audit/radar-governance-debt-and-drift-inventory.md
  • AD capability matrix: docs/dev/radar-ad-capability-matrix.md
  • AD tape/budget ledger: docs/dev/ad-tape-and-budget-ledger.md

License and citation

WiTwin Radar uses the WiTwin dual-license model. See the WiTwin licensing page. The simulator is derived from RF-Genesis; cite the RF-Genesis SenSys 2023 paper when that prior work is relevant.

Metadata

Release files for witwin-radar 0.4.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for witwin-radar 0.4.1
File Size Uploaded
witwin_radar-0.4.1.tar.gz 1.3 MB Details

Built distributions (wheels)

Table of built distributions (wheels) for witwin-radar 0.4.1
File Interpreter ABI Platform
witwin_radar-0.4.1-py3-none-win_amd64.whl Python 3 none Windows x86-64 Details
witwin_radar-0.4.1-py3-none-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl Python 3 none Linux glibc 2.24+ x86-64, Linux glibc 2.28+ x86-64 Details

Total release size: 5.9 MB

Release files / witwin_radar-0.4.1.tar.gz

Download URL witwin_radar-0.4.1.tar.gz
Size 1.3 MB
Tags Source
SHA-256 checksum
How to use checksums
8f5c4ceb6756526137b1cb40a317a7bc30706ef4a8b7ab7d2806fde41696cf8b
BLAKE2b-256 checksum
How to use checksums
baaf1d7bd98a6ef847c2b951dfe400ffeef010cf5571b05b04e0ede51d997776
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 20, 2026.

Transparency log

Release files / witwin_radar-0.4.1-py3-none-win_amd64.whl

Download URL witwin_radar-0.4.1-py3-none-win_amd64.whl
Size 2.2 MB
Tags Python 3 Windows x86-64
SHA-256 checksum
How to use checksums
69550d0627e7ef431c938cffa1d00805eeb5eb410491bd6f438ff463baa5e69b
BLAKE2b-256 checksum
How to use checksums
1736e45c77ae83518d3e8eeb707edbfa62db7c310a1136b8c3caaf6df4f0876d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 20, 2026.

Transparency log

Release files / witwin_radar-0.4.1-py3-none-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl

Download URL witwin_radar-0.4.1-py3-none-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl
Size 2.3 MB
Tags Linux glibc 2.24+ x86-64 Linux glibc 2.28+ x86-64 Python 3
SHA-256 checksum
How to use checksums
693466aef87f78b8f25664b9de2be73e2deb4be01648ad1e3a1836f13290bede
BLAKE2b-256 checksum
How to use checksums
a05c072bb0ec95f2f9a0e4b411a852adb649e84ebc0315816fe003cf85a09db6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 20, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.4.1 This release

3 release files

0.4.0

3 release files

0.3.0

3 release files

0.2.0

7 release files

0.1.0

2 release files

0.0.2

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page