Skip to content

Sionna RT digital twin

The boundary is deliberately simple:

OAI transmitter → external Sionna RT channel → OAI receiver

oai-python does not depend on Sionna and does not implement a channel model. The example alone installs sionna-rt, loads Sionna's built-in Munich scene around the Frauenkirche, traces one gNB/UE pair, converts the paths to SISO taps, applies them to copied time-domain IQ samples, and returns those samples to the OAI receiver.

The built-in Sionna RT Munich scene, traced gNB-to-UE paths, and the normalized downlink and uplink channel taps used by the example.

The image is generated by the example itself: red is the gNB at [8.5, 21.0, 27.0] metres, green is the UE at [45.0, 90.0, 1.5] metres, and the two plots are the exact normalized taps applied to the OAI IQ samples.

Run it

From a repository checkout with an installed oai-python wheel:

uv run --no-project --python 3.13 \
  --with 'sionna-rt==2.0.1' --with 'matplotlib>=3.8' \
  --with oai-python docs/examples/sionna_rt_channel.py \
  --plot sionna-munich-link.png

Expected output:

Sionna RT digital twin: downlink and uplink decoded
downlink transport block: b'\x00\x01\x02...'
uplink transport block: 384 bytes
visualization: /.../sionna-munich-link.png

The scene and channel are completely user-owned. Replace Munich with another built-in or Mitsuba XML scene, change the antenna positions, path-solver options, tap conversion, or the channel callable without changing the bindings. The explicit regular CPython selection is intentional: Sionna RT's Mitsuba dependency does not currently provide a free-threaded CPython wheel.

The application boundary

with NrPhyLink(
    downlink_channel=SionnaRtIqChannel(downlink_taps),
    uplink_channel=SionnaRtIqChannel(uplink_taps),
) as link:
    downlink = link.step(downlink_payload=payload)

The complete checked script is sionna_rt_channel.py. Sionna RT is not installed by oai-python and is not a package extra. It is not imported by any ordinary binding path.