Simulation pipeline
How Radar splits a simulation into a world half and an instrument half, and how trace, echo, simulate and stream use them.
This page explains the data flow from a Scene to a data cube, which package owns each step, and the four methods that run it. Read it first if you want to know where a given effect (a reflection, a noise stage, a window) enters the result.
Who owns what#
Radar sits on top of two other WiTwin packages and owns only the radar-specific steps.
| Package | Owns |
|---|---|
witwin.core (Core) | Scenes, geometry, materials, structure identity and motion (Scene, DynamicScene, trajectories, deformations) |
witwin.channel (Channel) | One-way electromagnetic propagation: line-of-sight and reflected paths between two endpoints |
witwin.radar (Radar) | The simulation session, round-trip path composition, target scattering, antenna patterns, the receiver chain, waveform synthesis and signal processing |
Radar never computes propagation itself. It asks Channel for the one-way legs from each transmitter to each scatterer and from each scatterer to each receiver, then joins them into round trips.
The eight steps of a frame#
For each frame time you pass in times, the session runs these steps:
- Sample the Core world at the waveform's observation instants inside the frame. How many instants depends on the motion sampling.
- Compile the Scene for Channel, or reuse the compiled epoch if nothing structural changed.
- Discover the one-way path topology, or re-evaluate the topology found earlier.
- Compose two-way round trips: transmitter to target to receiver, through the requested line-of-sight and reflection components.
- Weight each round trip by the target's scatter response and the antenna pattern gain.
- Synthesize the waveform (FMCW, OFDM or pulsed) from the composed rows in native CUDA, and add the TX-to-RX leakage tone if the radar declares one.
- Apply the receiver chain, if one is declared: conversion to volts, thermal and phase noise, LNA gain, IQ imbalance, AGC and ADC.
- Assemble the typed frame result and its processing axes.
Steps 1 to 5 are the world half: they depend on the Scene, the targets, the radar pose and the array, and on nothing else. Steps 6 to 8 are the instrument half: they depend on the waveform, the receiver chain and the output domain.
Four methods, two halves#
Radar has four methods that run these steps. They share one session loop and one synthesis route.
| Method | Runs | Returns | Use it when |
|---|---|---|---|
simulate(scene, targets, *, times, ...) | Both halves, per frame | One Result with every frame stacked | The default. The sequence fits in GPU memory |
stream(scene, targets, *, times, ...) | Both halves, per frame | An iterator of one-frame Results | The stacked cube would not fit in memory |
trace(scene, targets, *, times, ...) | World half only | Paths, the composed rows | You want to try several receivers, seeds or output domains on one world |
echo(paths) | Instrument half only | Result | You have a Paths from trace |
radar.echo(radar.trace(...)) is bit-identical to radar.simulate(...) with the same arguments. The difference is memory: simulate and stream drop each observation's rows as soon as they are synthesized, while Paths keeps all of them, about 20 bytes per live row per evaluated observation. See Reuse a trace with echo and Stream long sequences.
The propagation request#
Each call takes the propagation request as arguments, so the same radar can run with different settings:
los=Trueincludes the direct path from each antenna to each target.reflections=1is the maximum number of surface bounces on each one-way leg.0turns reflections off.- At least one component must be on:
los=False, reflections=0raisesValueError.
These arguments do not change the radar. A Radar is immutable and holds no run state, so a call that raises leaves nothing behind and there is nothing to reset between calls.
The result#
Result.cube is a complex tensor with the axes [frame, tx, rx, slow, fast]. Result.axis_names names them for the waveform, for example ('frame', 'tx', 'rx', 'chirp', 'range_bin') for the default FMCW output. Result.axes is the ProcessingAxes record that every processing function reads: the range and velocity axes in SI units, the phasor convention and the array layout. Result.frame(i) pairs one frame's cube with that record. See Signal processing chain.
The result also carries diagnostics about how it was produced: the Channel epoch of each frame, how many times the scene was compiled and the topology discovered, the motion sampling that ran, and whether the path set is known to be complete. The last_* fields hold the last frame's typed state, for example result.last_radar_paths.path_count.
Physical model and its limits#
- Propagation is quasi-static: Channel solves the geometry at each observation instant. Radar does not model retarded time or relativistic moving boundaries.
- Visibility changes are real discontinuities. Radar applies no smoothing or velocity clipping across a path appearing or disappearing.
- Radar requests only two Channel components: line of sight and specular reflection. "Complete" means complete within those components and the discovery policy, not every possible propagation effect.
- Radar places no scatterers on mesh geometry. Meshes reflect, and you declare scatterers with
PointTargetsorStructureTargets. See Scenes and targets.