Scenes¶
The scene layer is the planner for one generated scene: one generated example that can contain multiple emitters and one or more receivers. Each scene yields receiver IQ, in-phase / quadrature complex baseband samples, plus component metadata. After labeling, that output becomes one or more Record objects depending on the RX axis, the receive-antenna or per-receiver dimension of the output tensor: one record per RX antenna by default, or one joint record per scene with the RX axis preserved. See Records, Receivers, and Assets for the multi-RX axis story.
A scene decides:
how many emitters are present,
which emitter classes are selected,
where each emitter sits in time and frequency,
what power or signal-to-noise ratio (SNR) target each emitter is configured against, sampled from a configured distribution,
which receiver or receiver array observes the scene,
whether physical 3D geometry is needed.
The output is one scene-level Signal;
per-emitter metadata lives on component_signals[], a list of per-emitter
Signal objects. Each entry represents one
planned emission and is indexed in the same order as the realized scene plan.
Scene Configs Become Scene Plans¶
At the architecture level, configs describe dataset distributions and records store concrete realizations. The scene layer is where that distinction becomes visible.
To realize a scene is to draw concrete values for every random field, producing one specific scene from the distribution the config describes. A SceneConfig is a Pydantic config that declares either fixed values or distributions for each scene field. It can be fully fixed, fully stochastic, or a mix:
Scene config field |
If fixed |
If stochastic |
|---|---|---|
Emitter count |
Every scene has the same number of emitter slots. |
Each scene draws a count from fixed-range, uniform, or Poisson density settings defined by the scene-composition contract. |
Emitter pool |
Every slot can name a specific class. |
Slots choose classes from configured weights. |
Placement |
Start sample and carrier offset can be pinned. |
Time and frequency strategies draw positions and may retry for overlap policy. |
Power/SNR |
Each emitter uses one configured target. |
Targets are drawn from a configured distribution and realized after the channel chain. The low-noise-amplifier (LNA) noise transformation T10 in Group.RX_CAPTURE sets the realized noise floor. |
Channel realization |
A fixed backend and seed can reproduce one path. |
Per-emitter and per-RX sub-seeds produce independent channel realizations (concrete draws from the channel’s random effects, such as fading or multipath; each receiver and each emitter sees its own independent draw). |
The composer turns those settings into a scene plan: a concrete list of
emitter slots with chosen classes, exact parameters, start samples, frequency
offsets, power targets, device identities, and optional TX/RX positions. The
scene-level Signal and its
component_signals[] carry the realized plan forward to labels, storage, and
audits.
Default Composer¶
DefaultSceneComposer is the primary scene composer. It plans emitter slots, calls emitter backends (the per-protocol waveform-generation classes such as Wi-Fi, LTE, BLE, radar, or captured playback; see Concepts / Emitters), routes component signals through channel transformations, sums propagated components per receiver, and emits scene-level metadata. It does not implement waveform synthesis or RF propagation physics.
The composer delegates per-emitter placement to two pluggable strategy ABCs. Each strategy is selected by a StrEnum member named on SceneConfig; the worked Python form appears in the Minimal Example below.
BaseTimePlacement controls start-sample scheduling for each emitter. Shipped strategies live in TimePlacementStrategy:
IID_UNIFORM(uniform draw over scene duration),EVENT_RADAR_PRI(fixed-PRI pulse train with per-pulse Gaussian jitter),EVENT_PERIODIC_BEACON(fixed-cadence schedule covering Wi-Fi TBTT, BLE advertising, ADS-B squitter, and cellular SSB),EVENT_BURST(heavy-tailed Pareto on/off durations), andEVENT_FHSS_HOP(fixed-dwell frequency-hopping schedule).BaseFrequencyPlacement controls carrier frequency selection. Shipped strategies live in FrequencyPlacementStrategy:
IID_UNIFORM(uniform draw over scene bandwidth),STRATIFIED(equal-width bin selection with optional per-bin weights),REALISTIC_DENSITY/ISM_REALISTIC(draws from per-family channel grids for Wi-Fi, BLE, LoRa, and ADS-B),CLUSTERED(anchor-and-jitter draws), andFORCED_OVERLAP(forces overlap with a previously-placed emitter’s occupied band).
All five frequency strategies use rejection sampling with a fixed budget of 64 retries (_MAX_RETRIES = 64). On exhaustion the strategy raises PlacementError naming the strategy, the exhausted retry budget, and the configured min_spacing_hz. There is no nearest-feasible fallback.
Source: the exact rfgen strategy contract is in Reference / Scene Composition Algorithm § Frequency placement strategies and § Time placement strategies. Protocol timing anchors use IEEE 802.11-2020 for Wi-Fi target beacon transmission time (TBTT), the Bluetooth Core Specification 5.4 for Bluetooth Low Energy (BLE) advertising and Bluetooth hopping, ICAO Annex 10 Volume IV for ADS-B Mode S, and Skolnik’s Radar Handbook for radar pulse-repetition-interval (PRI) scheduling. Channel-plan anchors are sourced from the per-band JSON files loaded through JsonChannelPlanSource; each channel plan cites its spec section (IEEE 802.11-2020, Bluetooth Core 5.4, LoRa Alliance RP002-1.0.4, ICAO Annex 10).
To swap among shipped strategies, set SceneConfig.time_placement and
SceneConfig.frequency_placement to the relevant enum members, then place any
strategy-specific kwargs in SceneConfig.time_placement_params and
SceneConfig.frequency_placement_params; no subclass is required. To add a
strategy not in the enums, subclass the relevant ABC and register the class
under the rfgen.time_placement or rfgen.freq_placement Python entry-point
group. Python packages declare plugins via entry points. See How-to / Add a
custom placement strategy for the
registration boilerplate. The DefaultSceneComposer resolves the strategy name from SceneConfig at build time. See Scene Composition § Available Implementations for the registration contract.
Minimal Example¶
This skeleton exercises both placement strategies with shipped members:
EVENT_PERIODIC_BEACON for protocol-aware time scheduling and REALISTIC_DENSITY for a 2.4
GHz Wi-Fi / Bluetooth Low Energy carrier prior. The emitter_pool and
channel_chain objects come from the emitter and channel setup for the dataset;
see Concepts / Emitters and Concepts /
Channels. Two further worked examples, sparse i.i.d. uniform and
multi-strategy 100 MHz ISM scene with overlap policy, live in Scene Composition
§ Minimal Examples.
The snippet is a skeleton: emitter_pool, channel_chain, and rng are constructed earlier in the program; see Concepts / Emitters for emitter pools and Concepts / Channels for channel chains. density.poisson_rate=0.4 sets the mean emitter count per scene draw directly (mean 0.4), so each Poisson sample draws roughly 0 or 1 emitter regardless of scene bandwidth.
import torch
from rfgen.config import SceneConfig
from rfgen.enums import (
DensityMode,
FrequencyPlacementStrategy,
TimePlacementStrategy,
)
from rfgen.scene import DefaultSceneComposer
composer = DefaultSceneComposer()
scene_signal = composer.build(
scene_cfg=SceneConfig(
sample_rate_hz=20_000_000,
duration_s=0.020,
bandwidth_hz=20_000_000,
# Mean of 0.4 emitters per scene draw (independent of scene bandwidth).
density={"mode": DensityMode.POISSON, "poisson_rate": 0.4},
frequency_placement=FrequencyPlacementStrategy.REALISTIC_DENSITY,
frequency_placement_params={"taxonomy": "wifi-2.4ghz"},
time_placement=TimePlacementStrategy.EVENT_PERIODIC_BEACON,
time_placement_params={"period_s": 0.1024},
),
emitter_pool=emitter_pool,
channel=channel_chain,
rng=torch.Generator().manual_seed(0),
)
# scene_signal.iq has shape (n_rx_antennas, n_samples); here (2, 400_000).
assert scene_signal.iq.shape == (2, 400_000)
for component in scene_signal.component_signals:
print(
component.metadata.class_name,
component.metadata.start_sample,
component.metadata.realized_carrier_hz,
component.metadata.snr_db,
)
Composition and Geometry¶
Scenes have two separable concerns:
Concern |
What it covers |
When it matters |
|---|---|---|
Scene-plan realization, emitter count, class choice, time-frequency placement, SNR/power priors, overlap, multi-RX, streaming |
Always |
|
3D assets, materials, antenna arrays, TX/RX positions for Sionna RT |
Only for site-specific ray tracing |
Statistical scenes do not need 3D geometry. They use composition plus a statistical propagation backend such as SionnaUMa (3rd Generation Partnership Project, or 3GPP, urban macro), SionnaUMi (3GPP urban micro), SionnaRMa (3GPP rural macro), SionnaTDL (tapped-delay-line fading), or SionnaCDL (clustered-delay-line fading). The scenario-name acronyms come from 3GPP TR 38.901; see Concepts / Channels for the full backend table.
Geometric scenes use the same composition output, then add site-specific geometry consumed by SionnaRT, a ray-tracing (RT) propagation backend.
Compatibility¶
Geometry and propagation backend are compatibility-locked. The scene composer cross-validates the propagation backend against the presence of a 3D site geometry block at construction time. Mismatches fail validation rather than silently falling back.
Propagation backend |
Requires 3D geometry |
Behavior if mismatched |
|---|---|---|
Yes |
Missing geometry is invalid. |
|
Statistical Sionna backends |
No |
Providing site geometry is invalid unless the backend explicitly declares support. |
TorchSigImpairments (an adapter that applies the TorchSig open-source radio-frequency machine-learning library’s impairment models for benchmark compatibility) |
No |
Same as statistical backends. |
Each propagation class declares its geometry contract in the channels API. The composer reads that declaration and validates it against the scene config. Exact exception classes, error messages, and config fields belong in Reference / rfgen.channels and Reference / Config Schema.
Coordinate Frames¶
TX and RX positions matter only when the propagation backend consumes geometry. Statistical Sionna backends use ad-hoc topology coordinates internally; the ray-tracing backend reads coordinates from a loaded 3D scene asset. The two are not interchangeable and live in different frames:
Backend family |
Position frame |
Where positions live |
|---|---|---|
Statistical |
Topology frame: ad-hoc UE (user equipment, e.g. a phone) and BS (base station, e.g. a cell tower) positions in meters as defined by the selected statistical scenario |
Scene context positions passed to the channel-propagation transformation |
Geometric |
Asset frame: meters in the loaded 3D scene’s coordinate system |
Same scene context fields; the loaded asset defines the origin and axis convention |
The two frames are not interchangeable. A position correct in topology coordinates may be wildly off in the loaded 3D asset’s coordinates (Mitsuba is the rendering library Sionna RT uses for ray tracing; the asset is typically a Mitsuba .xml file). The composer enforces this:
Positions in scene context carry an implicit frame determined by the chosen propagation backend.
Switching from a statistical backend to a ray-tracing backend without re-coordinating positions fails validation. The validator checks position presence jointly with the backend’s geometry contract.
Open question
Today’s scene context carries the position fields tx_positions_m and
rx_positions_m without an explicit coordinate-frame tag. Scene metadata
similarly notes “scene-frame meters” without specifying which scene frame
applies. Disambiguating topology frame from asset frame requires either
frame-tagged position fields or a single position field with frame implied by
the chosen propagation backend plus validation.
Ownership Boundary¶
The scene composer coordinates mature tools and framework records. It does not own waveform or RF physics:
Responsibility |
Owner |
|---|---|
Waveform synthesis |
|
TX impairments, propagation, RX capture, RX hardware (the four channel groups) |
|
Emitter count, class choice, time-frequency placement, overlap, scene-level metadata |
Scene composer |
3D site assets for ray tracing |
|
Bounding boxes and masks |
|
Text annotations |
Scene composition is custom because heterogeneous wideband dataset composition is the framework’s core job. The custom part is orchestration: selecting and placing emitters, driving existing emitter/channel backends, summing propagated components, and preserving metadata. The composer must not reimplement emitter waveforms or RF channel physics.
Composer Flow¶
The default composer runs six grouped stages. The channel-group acronyms (TX impairments, RX capture, RX hardware) and their per-transformation contents are defined in Concepts / Channels:
Plan: realize a concrete scene plan from fixed values, ranges, weights, and distributions; assign per-emitter time, frequency, power/SNR, device identity, and optional position.
Generate: produce per-emitter baseband waveforms via emitter backends. Baseband means complex-valued IQ samples centered at frequency 0 before channel impairments or carrier shift.
Pre-sum channel: apply TX impairments and channel propagation to each component (one emitter’s contribution to one receiver) along its path. A Sionna backend may vectorize many TX/RX paths in one call, but the semantics remain per component before summing.
Sum: combine propagated component IQ into one scene buffer per receiver.
Post-sum channel: apply RX capture (mix to baseband, intermediate-frequency filter, resample, sum, low-noise-amplifier noise) and RX hardware (analog-to-digital conversion, phase noise, IQ imbalance, automatic gain control) to each receiver’s composite IQ. See Concepts / Channels for the per-transformation pages that expand each acronym.
Output: emit the scene-level Signal with
component_signals[].
flowchart TD
cfg["SceneConfig + emitter pool + channel chain"]
plan["Plan<br/>per-emitter time, freq, power, position"]
gen["Generate<br/>per-emitter baseband waveforms"]
perchan["Pre-sum channel<br/>TX impairments → channel propagation"]
sum["Sum<br/>combine propagated components per receiver"]
scene_chan["Post-sum channel<br/>RX capture → RX hardware"]
out["Scene-level Signal<br/>with component_signals[]"]
cfg --> plan --> gen --> perchan --> sum --> scene_chan --> out
Statistical scenes and geometric scenes share this flow. Geometry changes the propagation backend’s input: a statistical backend reads scene topology; a ray-tracing backend reads a 3D scene asset. See Scene Geometry.
The full normative algorithm lives in Reference / Scene Composition Algorithm.
Determinism¶
Same (global_seed, shard_id, sample_idx) produces the same scene population and placement. Per-emitter and per-RX sub-seeds derive from the scene seed so that distributed generation remains reproducible. Full seed-flow rules live in Reference / Determinism.
Sionna RT scenes are deterministic up to backend and device limits (for example, GPU floating-point reductions may not be bit-exact across runs). When exact reproducibility matters, use deterministic Sionna/PyTorch settings and record Sionna, PyTorch, CUDA, and scene-asset versions in metadata.
Placing signals in a scene¶
Placement decides where each emitter sits in time (start sample) and frequency (carrier). The framework ships 11 strategies: 5 frequency-placement strategies (uniform, stratified, realistic-density, clustered, forced-overlap) and 6 time-placement strategies (uniform, radar PRI, periodic beacon, heavy-tailed burst, self-exciting burst, FHSS hop). Both are selected from SceneConfig.time_placement and SceneConfig.frequency_placement, typed as TimePlacementStrategy and FrequencyPlacementStrategy.
Recipes for common scene types (mixed ISM 2.4 GHz, radar-only, cellular, generic) live in the Placement Strategy Selection Guide, which also cites the standards or peer-reviewed sources behind each shipped strategy and lists the evidence tier per strategy.
User-defined strategies register via the rfgen.time_placement or rfgen.freq_placement Python entry-point groups. The composer resolves plugin strategies through the plugin registry at construction time, so third-party strategies work without modifying framework code.
See Also¶
Scene Composition for placement strategies, SNR/power priors, overlap policies, multi-RX, and streaming.
Scene Geometry for 3D site assets when ray-traced propagation is in use.
Compatibility for the geometry/backend locking rules and fail-fast behavior.
Reference /
rfgen.scenefor the scene API.Reference / Scene Composition Algorithm for the full algorithm contract.
Background / Design Decisions § Channel pipeline for the rationale behind per-emitter carriers and per-receiver sample rates that the scene composer assigns.
Background / Design Decisions § ABC Pluggability for the rationale behind concrete-via-ABC composition that scenes use to swap composers and placement strategies.
Concepts / Channels for the channel chain driven by the composer.
Concepts / Labels for how
component_signals[]becomes bounding boxes and masks.