Scene Composition¶
Scene composition draws one training example from a configured population. For
one sample_idx, it chooses which transmitters appear, when and where their
signals land, how strongly they arrive, and what metadata explains the draw.
In contract terms, composition realizes one concrete scene from a scenario distribution: how many emitters appear, which classes they use, where they land in time and frequency, how they overlap, and what power or signal-to-noise ratio (SNR) prior they follow. A prior is the configured distribution or target range that the composer samples before channel effects produce the final received signal.
Composition is independent of 3D geometry. A non-geometric scene can use a statistical propagation backend, meaning a library channel model between each transmitter and receiver with no loaded 3D asset. A geometric scene uses the same placement and orchestration model, plus Scene Geometry for SionnaRT ray tracing (RT).
What Composition Does¶
The composer turns a scene config, emitter pool, channel chain, and random number generator (RNG) seed into one populated scene-level Signal. The scene config is the typed Python object or YAML-derived settings for the population. The emitter pool is the set of waveform generators available for selection. The channel chain is the configured transmit, propagation, receive-capture, and receiver-hardware pipeline applied to selected signals. The important distinction is:
Config / preset: describes a dataset distribution. Fields can be fixed values, ranges, weights, or named distributions.
Scene plan: one concrete realization of that distribution for one
sample_idx.Scene signal: in-phase/quadrature (IQ) tensor plus metadata produced for this one draw.
In the diagram below, transmit (TX) steps happen before propagation, receive
(RX) steps happen after receiver summation, and baseband means complex samples
centered at 0 Hz with the carrier frequency carried as metadata.
component_signals[] is the list of per-emitter signal records preserved inside
the scene output.
flowchart TD
cfg["Scenario config<br/>fixed values + ranges + weights"]
pool["Emitter pool<br/>available waveform classes"]
seed["Seed<br/>global_seed + shard_id + sample_idx"]
plan["Realize scene plan<br/>count, classes, params, time, freq, power"]
gen["Generate components<br/>clean baseband Signal per emitter"]
chan["Apply pre-sum channels<br/>TX impairments → channel propagation"]
sum["Sum receivers<br/>one composite scene buffer per RX"]
post["Apply post-sum channels<br/>RX capture → RX hardware"]
out["Output scene Signal<br/>component_signals[] + realized metadata"]
cfg --> plan
pool --> plan
seed --> plan
plan --> gen --> chan --> sum --> post --> out
The phases are:
Phase |
What happens |
What becomes metadata |
|---|---|---|
Realize scene plan |
Choose emitter count, classes, per-emitter parameters, time/frequency placement, power or SNR target, device identity, optional position. |
Realized count, class names, parameter values, start samples, center offsets, target SNR or power, device IDs, positions. |
Generate components |
Call emitter backends for clean baseband waveforms. |
Per-emitter waveform metadata and source backend provenance. |
Apply pre-sum channels |
Apply TX impairments and channel propagation before receiver summation. |
Fingerprint values, propagation profile, path/channel realization IDs. |
Sum receivers |
Combine propagated components into one scene buffer per receiver. |
Overlap outcomes and component-to-scene alignment. |
Apply post-sum channels |
Apply RX capture, which mixes to the receiver frame, applies an intermediate-frequency (IF) filter, resamples, sums, and injects low-noise-amplifier (LNA) noise, then apply RX hardware, including analog-to-digital converter (ADC), phase-noise, IQ-imbalance, and automatic-gain-control (AGC) effects. |
Realized receiver state, noise floor, realized SNR. |
Output scene signal |
Preserve scene IQ and |
Ground truth consumed by labelers, storage, audits, and annotations. |
Composition owns orchestration and metadata. Emitters own waveform generation. Channels own RF impairments, propagation, RX capture, and RX hardware effects.
Use fixed config values when every record should share the same structure. Use ranges, weights, and distributions when the dataset should cover a population of possible scenes. Both modes use the same composer and the same deterministic seed flow.
Minimal Examples¶
Python examples use enum members for framework-owned closed choices. YAML can
still use enum string values because Pydantic, the validation library that turns
config dictionaries or YAML into typed SceneConfig objects, deserializes them
at the config boundary. Exact enum members live in
Reference / API / Enums.
These examples are contract skeletons. The emitter_pool and channel_chain
placeholders come from emitter and channel setup code, and the PyTorch
torch.Generator supplies a deterministic RNG for this one scene draw.
Sparse random scene:
import torch
from rfgen.config import SceneConfig
from rfgen.enums import (
DensityMode,
FrequencyPlacementStrategy,
SceneOverlapPolicy,
TimePlacementStrategy,
)
from rfgen.scene import DefaultSceneComposer
composer = DefaultSceneComposer()
scene = SceneConfig(
bandwidth_hz=20e6,
duration_s=0.020,
# Mean of 0.2 emitters per scene draw (before Poisson variation and min/max clipping).
density={"mode": DensityMode.POISSON, "poisson_rate": 0.2},
frequency_placement=FrequencyPlacementStrategy.IID_UNIFORM,
time_placement=TimePlacementStrategy.IID_UNIFORM,
)
signal = composer.build(
scene_cfg=scene,
emitter_pool=emitter_pool,
channel=channel_chain,
rng=torch.Generator().manual_seed(0),
)
Protocol-aware industrial, scientific, and medical (ISM) scene:
Protocol-aware means time or frequency placement follows protocol timing or band conventions instead of independent uniform draws.
composer = DefaultSceneComposer()
scene = SceneConfig(
bandwidth_hz=100e6,
duration_s=0.100,
density={"mode": DensityMode.POISSON, "poisson_rate": 1.5},
frequency_placement=FrequencyPlacementStrategy.REALISTIC_DENSITY,
frequency_placement_params={"taxonomy": "wifi-5ghz"},
time_placement=TimePlacementStrategy.EVENT_PERIODIC_BEACON,
time_placement_params={"period_s": 0.1024},
geometry={"overlap_policy": SceneOverlapPolicy.ALLOW},
)
signal = composer.build(
scene_cfg=scene,
emitter_pool=emitter_pool,
channel=channel_chain,
rng=torch.Generator().manual_seed(1),
)
Ray-traced scene composition still uses the same composer. Geometry appears in the scene and propagation configuration, not in a separate composition algorithm.
Available Implementations¶
DefaultSceneComposer is the only planned built-in composer. It coordinates heterogeneous emitter selection, time-frequency placement, channel routing, summing, and metadata preservation.
The composer interface is extensible: subclassing BaseSceneComposer is the path for a specialized scene-population engine, such as reproducing a specific radio-frequency machine learning (RFML) baseline or delegating population to an upstream system-level simulator, an external tool that decides which devices transmit when. This is not required for normal dataset generation. The contract that any composer must preserve is documented at BaseSceneComposer and on the Scene Composition Algorithm reference page.
The placement strategies are also extensible as standalone plugins:
Time placement: subclass BaseTimePlacement and register under the
rfgen.time_placemententry-point group.Frequency placement: subclass BaseFrequencyPlacement and register under the
rfgen.freq_placemententry-point group.
Custom placement strategies do not require a custom composer; they plug in by
name through SceneConfig.time_placement /
SceneConfig.frequency_placement, with constructor kwargs carried in
SceneConfig.time_placement_params /
SceneConfig.frequency_placement_params.
Python entry points are how installed packages advertise plugin classes to rfgen. The custom placement strategy how-to covers the registration workflow.
Why rfgen Owns Heterogeneous Composition¶
TorchSig-style RFML baselines are an important compatibility target, but rfgen’s scene composer has a broader job: heterogeneous, metadata-rich scenes that can feed detection, segmentation, captioning, reasoning, and downstream training workflows.
In this table, independent and identically distributed (i.i.d.) placement means each draw is sampled independently from the same configured distribution.
Need |
Benchmark-style mixture |
rfgen composer |
|---|---|---|
Carrier placement |
i.i.d. uniform over configured range |
i.i.d., stratified, clustered, ISM-realistic, forced-overlap |
Time placement |
i.i.d. start sample |
i.i.d. plus protocol-aware event timing |
Emitter count |
signal probabilities |
density-aware count, e.g. fixed, range, or Poisson-mean per scene |
Overlap handling |
scalar cochannel probability and retry loop |
explicit reject / allow / force policies with metadata |
Dense-scene failure mode |
retry exhaustion can truncate scenes |
truncation recorded in metadata; non-rejection policies available |
Multi-RX |
not the primary artifact shape |
optional multi-RX scenes |
Streaming |
in-memory generation |
ChunkedSignal for large captures |
The output remains compatible with downstream RFML tooling where possible: component_signals[] carries per-emitter metadata in the scene reference frame.
Placement Strategies¶
FrequencyPlacementStrategy selects how emitters are placed in frequency. The five shipped members and their ABCs are:
Member |
Class |
Description |
|---|---|---|
|
|
Uniform draw over |
|
|
Equal-width bin selection with optional per-bin weights. |
|
|
Draws from the per-family channel plan loaded through a |
|
|
Picks an anchor from |
|
|
With probability |
All five strategies use rejection sampling with _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.
TimePlacementStrategy selects how bursts are scheduled in time. The five shipped members are:
Member |
Class |
Description |
|---|---|---|
|
|
Uniform start-sample draw over |
|
|
Fixed-PRI pulse train with per-pulse |
|
|
Fixed-cadence schedule; phase chosen by |
|
|
Heavy-tailed Pareto on/off durations via |
|
|
Fixed-dwell FHSS schedule; dwell length drawn from a configured set via |
Source: Reference / Scene Composition Algorithm § Frequency placement strategies and § Time placement strategies define the local draw contracts and parameter tables. Protocol timing comes from standards and reference texts:
Wi-Fi timing uses IEEE 802.11-2020 target beacon transmission time (TBTT).
Bluetooth timing uses the Bluetooth Core Specification 5.4 for BLE advertising and Bluetooth hopping.
ADS-B timing uses ICAO Annex 10 Volume IV Chapter 3.1.2.8.1.1 for Mode S extended squitter.
Radar PRI scheduling uses Skolnik’s Radar Handbook.
Channel-plan defaults are grounded by per-band JSON files loaded via JsonChannelPlanSource. Shipped bands: wifi-2.4ghz (IEEE 802.11-2020 Table 17-9, non-overlapping channels 1/6/11 with higher weights), wifi-5ghz (IEEE 802.11-2020 Tables 17-12/17-13), ble (Bluetooth Core 5.4 Vol 6 Part B 1.4, advertising channels 0/12/39), lora-us915 (LoRa Alliance RP002-1.0.4 Table 2-3), lora-eu868 (RP002-1.0.4 Table 2-7), adsb-1090mhz (ICAO Annex 10 Volume IV).
Per-strategy parameters and validation rules live in Reference / Scene Composition Algorithm.
Power and SNR Policies¶
ScenePowerMode selects the power prior before channel realization. A relative SNR target asks the channel chain to reach a received signal-to-noise ratio after propagation and receiver noise. Absolute-power mode keeps transmit or receive power in physical units and lets Sionna, the delegated channel simulator, determine the received level. Members: UNIFORM_DB (controlled uniform ranges in decibels, dB, for classifier benchmarks), LOG_UNIFORM, LOG_NORMAL_HEAVY_TAIL (realistic mixtures), SIONNA_ABSOLUTE_POWER (absolute-power mode where Sionna propagation and receiver noise determine received power).
The realized SNR is determined after propagation and RX-capture LNA noise injection. Composition samples the prior or target; the channel chain realizes the actual received signal and noise floor.
Source: Reference / Scene Composition Algorithm § SNR and power defines the local prior and reconciliation modes. The receiver-noise operation is the T10 LNA-noise transformation documented in Channels / RX Capture, whose point-of-claim source is the thermal-noise relation used by the local noise-floor table.
Overlap Policies¶
Cochannel overlap means two emitters occupy the same time-frequency rectangle in
the same receiver band. SceneOverlapPolicy
selects how the composer handles that case. Members: REJECT, ALLOW
(probabilistic; per-draw probability lives on the
SceneOverlapPolicyConfig’s
p_overlap field), FORCE (deliberate cochannel collisions for stress tests).
Overlap decisions are metadata-bearing events. If rejection budgets are exhausted, the composer must record the realized count and truncation reason so downstream consumers know what was generated. The SceneOverlapPolicyConfig sub-model carries the full parameter surface (p_overlap, retry_budget, margin_hz, overlap_target_strategy).
Source: Reference / Scene Composition Algorithm § Overlap policy defines the local rectangle-overlap semantics, retry behavior, and TorchSig compatibility target. TorchSig’s signal type source is the upstream compatibility anchor for time-frequency rectangle metadata.
Multi-RX and Streaming¶
Multiple-receiver scenes produce IQ with a receiver axis in the tensor. Each receiver gets its own channel and front-end realization, meaning its own propagation result and receiver-side hardware state. Geometry-aware multi-RX uses the same RX positions and arrays consumed by SionnaRT; statistical multi-RX uses the selected statistical backend’s topology and correlation behavior. Output shape conventions live in Records, Receivers, and Assets.
Large scenes use ChunkedSignal once predicted IQ size exceeds the configured memory threshold. Chunking is part of the scene contract because long captures at high sample rates exceed memory quickly.
Open Questions¶
Measured density priors. Published measured density anchors for industrial, scientific, and medical (ISM) bands, Automatic Dependent Surveillance-Broadcast (ADS-B), LoRaWAN, Narrowband Internet of Things (NB-IoT), Long-Term Evolution for Machines (LTE-M), and vehicle-to-everything (V2X) coexistence need a separate verification pass.
3GPP scenario presets. Sionna-backed UMa, UMi, and RMa presets could bundle topology defaults, array defaults, BS/UE heights, and indoor/outdoor state.
Protocol coordination. Carrier-sense, cellular handover, coordinated multi-point, and retransmission behavior remain out of scope until mature upstream protocol simulators are selected.
Determinism¶
Same (global_seed, shard_id, sample_idx) produces the same scene composition. Per-emitter sub-seeds derive from (scene_seed, "emitter", emitter_idx). Per-RX sub-seeds derive from (scene_seed, "rx", rx_idx).
Determinism must not depend on Spark partition order, rejection-loop timing, or backend import order. Full seed-flow rules live in Reference / Determinism.
See Also¶
Scenes overview for the parent concept.
Scene Geometry for 3D scene assets consumed by SionnaRT.
Concepts / Channels for pre-sum and post-sum channel transformations.
Concepts / Emitters for the waveform generators populated into emitter slots.
Reference / Scene Composition Algorithm for the normative algorithm.
Reference / TorchSig Interop for benchmark compatibility.
Background / Sionna Stack for Sionna PHY and RT mapping.