A geographic scene loads like a Sionna file scene. The returned object is a native
sionna.rt.Scene; arrays, radio devices, solvers, radio maps, rendering and preview
stay the upstream API.
# Sionna file scene
from sionna.rt import load_scene
scene = load_scene(filename)
# Geographic scene: same call, WGS84 coordinates
from openworld_radio_twin.rt import load_scene
scene = load_scene(latitude=52.3762, longitude=4.8993, radius_m=250)
Four environmental layers from open geodata, compiled into one metric frame with the receiver surface and the radio outputs.
Buildings from Overture and OpenStreetMap worldwide, or from 3DBAG, Berlin LoD2 and Boston BPDA models by region; bare-earth terrain from USGS 3DEP or Mapzen; water levelled per component; ground, vegetation, paved and water classes with ITU material proxies.
The scene is plain Mitsuba XML and PLY. Sionna RT solves radio maps on a terrain-following
measurement surface and channel responses with PathSolver. The explorer places
transmitters inside the compiled scene and drapes the result on its terrain; a CPU preview
shares the same output contract.
One scene expands into seeded cases with per-case transmitter layouts and solver settings. Datasets resume after interruption, extend deterministically, and record the source, release and resolution behind every array.
Controlled comparisons on the same scene, transmitter and seed. Only one layer changes at a time.
load_scene and shows it as an inset of the globe: scene terrain,
surface classes and buildings inside the dashed boundary, display terrain outside.
PathSolver outputs on OWRT scenes in
Bergen and Murray Hill: power-delay profiles, RMS delay spread and channel frequency responses.






One 1,500 m × 1,500 m scene, one 3.5 GHz sector transmitter, 1 m cells, 1.5 m receiver plane. PNGs keep one pixel per solver cell with transparent no-data; raw arrays ship alongside.
A CesiumJS world with display terrain everywhere. Drag a diagonal to define a scene; the compiled scene replaces the globe inside its square, with its own terrain, surfaces and buildings, and transmitters are placed inside it.
Overture and OSM extrusions worldwide; native LoD2 roofs from 3DBAG, Berlin and Boston selected automatically by location, or by explicit ID.
Bare-earth DEMs from USGS 3DEP and Mapzen Skadi with source, model and local elevation arrays kept apart; water levelled and recorded.
Non-overlapping ground, vegetation, paved and water meshes with documented ITU material proxies, each switchable at solve time.
Terrain-following measurement surfaces, native propagation controls, path solvers and rendering on the same scene. The CPU preview shares the same output contract.
Seeded expansion with PCG64, per-case provenance, resume after interruption, deterministic extension, and selective artifacts.
# install
git clone https://github.com/zhiheng-yang/openworld-radio-twin.git
cd openworld-radio-twin
./scripts/setup.sh
conda activate owrt
# interactive explorer
./scripts/run.sh # http://127.0.0.1:8765
# compile a reusable scene
owrt scene compile --lat 52.3762 --lon 4.8993 --radius 250 --output scenes/amsterdam
from openworld_radio_twin import rt as owrt
from sionna.rt import PlanarArray, RadioMapSolver, Transmitter
scene = owrt.load_scene(latitude=52.3762, longitude=4.8993, radius_m=250)
scene.tx_array = PlanarArray(num_rows=1, num_cols=1, pattern="iso", polarization="V")
scene.rx_array = PlanarArray(num_rows=1, num_cols=1, pattern="iso", polarization="V")
scene.add(Transmitter(name="tx", position=owrt.position(
scene, latitude=52.3770, longitude=4.9003, height_agl=25.0)))
radio_map = RadioMapSolver()(
scene, measurement_surface=owrt.measurement_surface(scene, cell_size=(5.0, 5.0)))
@software{yang2026openworldradiotwin,
author = {Zhiheng Yang},
title = {OpenWorld Radio Twin: Interactive World-Scale Wireless Digital Twins},
year = {2026},
version = {0.2.1},
url = {https://github.com/zhiheng-yang/openworld-radio-twin},
license = {Apache-2.0}
}