Your First Scene

Build a multi-emitter wideband scene end to end: pick emitters, pick a channel, set scene density, write joint labels, optionally annotate.

Warning

Pre-implementation. Commands and configs below are the target v0.5+ surface.

The scenario

Three coexisting emitters in a 100 MHz scene centered near the 2.4 GHz industrial, scientific, and medical (ISM) band:

  • Wi-Fi 11g orthogonal frequency-division multiplexing (OFDM) 20 MHz burst at 2.452 GHz

  • Bluetooth Low Energy (BLE) advertising Gaussian frequency-shift keying (GFSK) 1 Mbit/s frame at 2.402 GHz (advertising channel 37)

  • Barker-13 radar pulse train at 2.485 GHz, 1 ms pulse repetition interval (PRI)

The first emitter is a Wi-Fi burst, the second is a short Bluetooth advertising frame, and the third is a pulsed radar waveform whose pulses repeat every 1 ms.

Channel: tapped-delay-line C (TDL-C), the 3GPP urban non-line-of-sight (NLoS) profile, with 300 ns delay spread and 30 Hz Doppler. Delay spread controls multipath timing, Doppler controls motion-related frequency shift, and the scene has one receiver (RX). The run writes the full label suite plus caption and scene-report annotations.

The config

Read the YAML in two passes: first load reusable preset files, then override the few fields this example needs.

The config is composed by Hydra from named pieces under configs/<group>/. The defaults: list loads one published YAML per config group (emitter_zoo, channel, scene, label, annotator, storage), and the trailing _self_ entry tells Hydra to merge this file’s top-level keys after the defaults so they override specific fields. The emitter_zoo: block lower in the file therefore extends the named preset rather than redefining it; only the keys that appear here are overridden, and the rest of the preset stays as published. See Hydra’s defaults list for the full merge semantics.

# my_first_scene.yaml
defaults:
  - /emitter_zoo: heterogeneous_24ghz   # configs/emitter_zoo/heterogeneous_24ghz.yaml
  - /channel: sionna_phy_tdl_c          # configs/channel/sionna_phy_tdl_c.yaml
  - /scene: dense                       # configs/scene/dense.yaml
  - /label: joint                       # configs/label/joint.yaml
  - /annotator: caption_and_scene_report # configs/annotator/caption_and_scene_report.yaml
  - /storage: zarr_local                # configs/storage/zarr_local.yaml
  - _self_                              # apply this file's top-level keys after the defaults

run:
  num_samples: 1000
  shard_size: 100
  seed: 42

storage:
  path: ./out/first-scene

scene:
  sample_rate_hz: 2.0e8
  bandwidth_hz: 1.0e8
  center_hz: 2.45e9
  duration_s: 0.1
  density:
    mode: fixed
    min_emitters: 3
    max_emitters: 3

emitter_zoo:                            # overrides specific fields of the heterogeneous_24ghz preset above
  families:
    - family: comms
      classes: [wifi.11g.ofdm-20mhz]
      weight: 1.0
    - family: iot
      classes: [ble.gfsk.1m.adv]
      weight: 1.0
    - family: radar
      classes: [pulsed.barker.b13]
      weight: 1.0
      params:
        pri_us: 1000
        pulse_width_us: 13
        num_pulses: 100

This example captures 0.1 seconds per record at 200 MHz sample rate, keeps 100 MHz of bandwidth centered at 2.45 GHz, and fixes the scene at exactly three emitters.

Selected preset

Meaning

heterogeneous_24ghz

Emitter-zoo preset with Wi-Fi, Bluetooth, radar, and other 2.4 GHz family options.

sionna_phy_tdl_c

Channel preset using Sionna PHY’s TDL-C non-line-of-sight profile.

dense

Scene preset for drawing several emitters in one bandwidth window.

joint

Label preset that writes one coordinated label set across modalities.

caption_and_scene_report

Annotator preset that asks for a short caption and a structured scene report.

zarr_local

Storage preset that writes the canonical local Zarr store plus shard mirrors.

The heterogeneous_24ghz preset is one of the published emitter_zoo config-group YAMLs; its full set of families and classes lives at configs/emitter_zoo/heterogeneous_24ghz.yaml. See the EmitterZooConfig schema for the structure each emitter_zoo YAML follows, and Scenario Presets for the larger top-level presets such as dense_urban_2_4ghz (dense 2.4 GHz urban ISM scene) and contested_ew_lband (electronic-warfare L-band scene). A top-level scenario preset composes an emitter_zoo group together with channel, scene, label, annotator, storage, and orchestration groups in one preset file.

Generate

rfgen generate --config-name my_first_scene

Output:

Note

Illustrative. Specific values below (config hash, byte counts, percentile SNRs, audit metrics, sample IDs) show the target output shape, not measured behavior. Numbers will be regenerated from real runs once the v0 implementation lands.

Resolved config (sha256:<digest>):
  ...

Generating 1000 samples in 10 shards (locally, 8 workers) ...
[████████████████████████] 1000/1000  [<elapsed>]

Cross-modality consistency: <pct> (<count>/1000)
Statistics audit: PASS
  density: 3.0 emitters/scene (target: 3.0)
  SNR p10/p50/p90: <value> / <value> / <value> dB
  delay-spread RMS: <value> ns (target: <target> ns)

Wrote ./out/first-scene/
  manifest.json       <size>
  samples.zarr/       <size>    (1000 samples, complex64 IQ + bbox + seg)
  shards/             <size>    (10 WebDataset tars)
  stats.json          <size>

Read this output as a quick health report. Signal-to-noise ratio (SNR) percentiles summarize received signal quality, IQ means complex in-phase / quadrature samples, bbox means time-frequency bounding boxes, seg means segmentation masks, and WebDataset tars are training shards mirrored from the canonical store.

Inspect

rfgen inspect ./out/first-scene sample --random --n 1
{
  "sample_id": "f9a3b7c2-...",
  "scene": {
    "bandwidth_hz": 1.0e8,
    "num_emitters": 3,
    ...
  },
  "bbox": [
    {
      "class_name": "comms.wifi.11g.ofdm-20mhz",
      "abs": {"start_sample": 1840000, "stop_sample": 2080000,
              "low_freq_hz": 2.442e9, "high_freq_hz": 2.462e9},
      "yolo": {"cx": 0.098, "cy": 0.56, "w": 0.012, "h": 0.20}
    },
    {
      "class_name": "iot.ble.gfsk.1m.adv",
      "abs": {"start_sample": 8400000, "stop_sample": 8476000, ...},
      ...
    },
    {
      "class_name": "radar.pulsed.barker.b13.pri-1ms",
      "abs": {"start_sample": 0, "stop_sample": 20000000, ...},
      ...
    }
  ],
  "text": {
    "caption": "Three coexisting emitters in 100 MHz around 2.45 GHz: a Wi-Fi 11g OFDM burst at channel 9, a BLE advertising frame on channel 37, and a Barker-13 radar pulse train at 2.485 GHz with 1 ms PRI.",
    "scene_report": "..."
  }
}

In the label output, abs gives absolute sample and hertz coordinates. yolo gives normalized center, width, and height coordinates for detector training. Generated annotations may use the domain vocabulary configured by the annotation template, such as OFDM or PRI.

Visualize

import matplotlib.pyplot as plt
import numpy as np
from rfgen.storage import open_store
from rfgen.viz import spectrogram, overlay_bboxes

with open_store("./out/first-scene") as store:
    sample = store.read_random(seed=0)

# Convert complex IQ samples into a time-frequency image.
spec = spectrogram(sample.iq, n_fft=1024, hop=256)
fig, ax = plt.subplots(figsize=(12, 6))
ax.imshow(spec, aspect="auto", origin="lower", cmap="viridis")

# Overlay ground-truth emitter boxes on the spectrogram.
overlay_bboxes(ax, sample.bboxes, sample.scene)

ax.set_title(sample.text["caption"])
plt.show()

You should see three rectangles annotating the three emitters in the spectrogram, with the caption rendered at the top.

What’s next

Scale up

Generate the full dense_urban_2_4ghz preset (1M samples) on Spark.

Generate a dense urban dataset
Sionna stack

How NVIDIA Sionna’s modules (RT, PHY) map to the framework’s layers.

Sionna stack
Add custom emitters

Write your own BaseEmitter for an unsupported waveform.

Add a custom emitter
Re-annotate

Add reasoning chains or contrastive pairs to an existing dataset.

Annotate an existing dataset