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.

PackageOwns
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:

  1. Sample the Core world at the waveform's observation instants inside the frame. How many instants depends on the motion sampling.
  2. Compile the Scene for Channel, or reuse the compiled epoch if nothing structural changed.
  3. Discover the one-way path topology, or re-evaluate the topology found earlier.
  4. Compose two-way round trips: transmitter to target to receiver, through the requested line-of-sight and reflection components.
  5. Weight each round trip by the target's scatter response and the antenna pattern gain.
  6. 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.
  7. Apply the receiver chain, if one is declared: conversion to volts, thermal and phase noise, LNA gain, IQ imbalance, AGC and ADC.
  8. 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.

MethodRunsReturnsUse it when
simulate(scene, targets, *, times, ...)Both halves, per frameOne Result with every frame stackedThe default. The sequence fits in GPU memory
stream(scene, targets, *, times, ...)Both halves, per frameAn iterator of one-frame ResultsThe stacked cube would not fit in memory
trace(scene, targets, *, times, ...)World half onlyPaths, the composed rowsYou want to try several receivers, seeds or output domains on one world
echo(paths)Instrument half onlyResultYou 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=True includes the direct path from each antenna to each target.
  • reflections=1 is the maximum number of surface bounces on each one-way leg. 0 turns reflections off.
  • At least one component must be on: los=False, reflections=0 raises ValueError.

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 PointTargets or StructureTargets. See Scenes and targets.