rfgen.domains

The domain nursery: emitter recipes that encode a standard rather than a mechanism. A module belongs here when it is specific to one domain – communications or radar – and nothing outside that domain has asked for it. The moment a second domain wants a mechanism, that mechanism moves out to the stage package that owns it, which is what keeps this package a nursery rather than a second home for shared code.

The modulation machinery these recipes build on is domain-neutral and lives in rfgen.waveforms, together with the BaseEmitter contract every recipe here subclasses, the pool factory, and the built-in selector inventory that lists every emitter selector in one table.

Package

What it ships

rfgen.domains.comms

Cellular (LTE, 5G NR PUSCH), LoRa, ADS-B, the gr-OOT links (BLE, Wi-Fi, ZigBee), and the five drone links

rfgen.domains.radar

The deterministic chirp emitter, whose parameters are radar concepts rather than communications ones

Nothing in this package is imported eagerly by rfgen: a recipe that needs an optional extra raises BackendUnavailableError when it is instantiated, never at import time.

rfgen.domains.radar.chirp_emitter

ChirpRadarEmitter (from rfgen.domains.radar.chirp_emitter import ChirpRadarEmitter, ChirpRadarParams) synthesizes a deterministic linear-FM pulse train and is always available; SciPy is a runtime dependency rather than an extra. It is documented with the rest of the emitter catalog on the Waveforms page, and its scientific validation report is Chirp radar backend.

Do not confuse it with the TorchSig chirp adapter (rfgen.integrations.torchsig.emitters.torchsig_chirp), which is a randomized augmentation source rather than a deterministic radar waveform.

rfgen.domains.comms

The communications recipes: the LoRa dual backend, the 5G NR PUSCH emitter, and the five drone links. Each subsection below documents one family and names the module to import it from.

LoRa: dual backend

The LoRa family ships two backends behind one shared schema. Both accept ("lora",) as their only class label and share LoRaParams for the per-emission parameters; selection happens at scene-config time via the LoRaBackend enum ("gr-lora-sdr" or "lora-phy").

  • LoRaSdrEmitter is the default, backed by gr-lora_sdr (GNU Radio OOT; typically installed via conda or a system package manager, not PyPI). Behind rfgen[lora-sdr].

  • LoRaPHYEmitter is the pure-Python fallback, backed by the loraphy library. Behind rfgen[lora-phy].

Both subclass an internal _LoRaBase ABC that fixes family = EmitterFamily.IOT and supported_classes = ("lora",); per-emission knobs (spreading_factor, bandwidth_hz, coding_rate, payload_bytes) live in the shared LoRaParams model.

Because the public class label is the single value "lora", the scene composer treats LoRa as one family with backend-selectable internals.

NRPuschEmitter

Import with from rfgen.domains.comms.cellular import NRPuschEmitter, NRPuschParams. Wraps sionna.phy.nr.PUSCHTransmitter/PUSCHConfig directly: frequency-domain resource mapping, DMRS (pilot) insertion, transport-block encoding (LDPC + rate matching), scrambling, and OFDM modulation are all performed by PUSCHTransmitter. This is 3GPP-conformant by delegation: rfgen does not re-verify or re-implement 3GPP conformance itself, but inherits it from Sionna’s independent TS 38.211/TS 38.212 implementation. See the Signal Atlas comms-v1 Phase-2 physics validation for the evidence (a real DMRS at OFDM symbol index 2, a real sized transport block).

NRPuschParams:

Field

Type

Default

Constraint

n_rb

int

52

1 <= n_rb <= 275 (the full legal NR carrier grid, 3GPP TS38101-1 Table 5.3.2-1)

mcs

int

10

0 <= mcs <= 27, an index into 3GPP TS38214 Table 5.1.3.1-1

subcarrier_spacing_hz

int

30000

15000 <= subcarrier_spacing_hz <= 240000; must resolve to one of the NR numerologies (15/30/60/120/240 kHz)

occupied_bandwidth_hz

float | None

None

When set, > 0; overrides n_rb by rounding to the nearest resource-block count that best matches this bandwidth (see resource_blocks_for_bandwidth below)

allocation_fraction

float

1.0

0.0 < allocation_fraction <= 1.0. Fraction of the carrier grid actually allocated to the PUSCH, realized as PUSCHConfig.n_size_bwp = max(1, round(fraction * n_rb)), the 3GPP TS38214 frequency-domain resource allocation. 1.0 keeps the full-grid behavior. The realized allocation can differ from the rounded target by a few resource blocks, because transport-block validity is non-monotonic in the allocated count; the realized value is recorded in extras["nr_pusch_allocated_n_rb"]

Not every (n_rb, mcs) pair is a constructible NR transport block: Sionna’s LDPC base-graph selection (3GPP TS38212) rejects some combinations near the BG1/BG2 code-rate crossover. NRPuschEmitter.generate() catches Sionna’s ValueError in both cases and re-raises EmitterError with the offending parameters attached; when occupied_bandwidth_hz is set, a bounded ±8-RB search finds the nearest constructible neighbor before raising. signal.metadata.extras records nr_pusch_n_rb, nr_pusch_mcs, nr_pusch_subcarrier_spacing_hz, nr_pusch_tb_size_bits (the real, achieved transport-block size in bits), nr_pusch_num_slots_generated, nr_pusch_native_sample_rate_hz, nr_pusch_channel_n_rb, nr_pusch_allocation_fraction, nr_pusch_allocated_n_rb, and, when occupied_bandwidth_hz is set, nr_pusch_occupied_bandwidth_bounds_hz.

Requested duration versus slot duration. generate() transmits as many consecutive Sionna slots as needed to cover duration_s, then resamples and trims or zero-pads the concatenated waveform to exactly round(sample_rate * duration_s) samples. When a caller’s fixed generation window is shorter than one full NR slot (which lasts 1 / (2 ** numerology) milliseconds; longer at 15 kHz, shorter at 240 kHz), the returned record can end mid-slot, potentially cutting off part or all of the single-symbol DMRS this section describes as real and standards-conformant. This is a window-length-versus-slot-duration interaction of the caller’s own generation configuration, not a property of NRPuschEmitter itself; request a duration_s covering at least one full slot (resource_grid.ofdm_symbol_duration * resource_grid.num_ofdm_symbols, both read from the constructed PUSCHTransmitter.resource_grid) when the downstream use of a record depends on an intact DMRS.

Drone signal emitters

The drone EmitterFamily members. Each is synthesized clean-room from a published specification (never from copyleft reference code) and is pure NumPy/torch, so no optional extra is required. They were added to support the use_cases/signal-atlas/cuas-v1 counter-UAS dataset, but are core mechanism (no dataset-specific values) per the Core/Defaults/Use-case placement rule; their measured-vs-published fidelity evidence, real-capture comparison, and scope-bounded limitations live in the cuas-v1 emitter reports and retained pre-graph-migration composition report (use_cases/signal-atlas/cuas-v1/docs/report/cuas-v1-validation.pdf and use_cases/signal-atlas/cuas-v1/docs/validation/), not duplicated here. The composition report is prominently marked historical because its former thermal-noise graph does not validate the current aggregate-AWGN track.

DroneIdEmitter (from rfgen.domains.comms.drone_id import DroneIdEmitter, DroneIdParams) synthesizes one DJI DroneID broadcast frame: an LTE-derived OFDM grid (15 kHz subcarrier spacing, 1024-point FFT, 601 occupied subcarriers with a nulled DC), two Zadoff-Chu synchronization symbols (roots 600 and 147), and QPSK data subcarriers carrying a representative (non-decodable) payload. frame_variant selects the 9-symbol or 8-symbol frame layout.

FhssRcLinkEmitter (from rfgen.domains.comms.drone_fhss_rc import FhssRcLinkEmitter, FhssRcLinkParams) synthesizes a frequency-hopping RC control-link burst sequence from register-verified presets (protocol: frsky_d8, frsky_d16, dsmx, afhds2a), each self-mixed onto its hop channel within the requested bandwidth.

AnalogFpvVideoEmitter (from rfgen.domains.comms.drone_fpv_video import AnalogFpvVideoEmitter, AnalogFpvVideoParams) frequency-modulates a synthetic composite-video baseband (video_standard: ntsc or pal) with the ITU-R BT.470-7 color subcarrier and line rate.

RemoteIdEmitter (from rfgen.domains.comms.drone_remote_id import RemoteIdEmitter, RemoteIdParams) synthesizes an ASTM F3411 Remote ID broadcast over its Bluetooth Low Energy advertising transport only (GFSK bursts on the three advertising channels); Wi-Fi/NaN Remote ID is not modeled.

OcuSyncSurrogateEmitter (from rfgen.domains.comms.drone_ocusync_surrogate import OcuSyncSurrogateEmitter, OcuSyncSurrogateParams) synthesizes a duty-cycled wideband OFDM burst (bandwidth_mode_hz: 10 or 20 MHz) as an explicitly non-protocol-faithful surrogate for DJI’s proprietary, encrypted OcuSync link; metadata.extras records protocol_faithful: False.

See Also

  • Waveforms: the BaseEmitter contract, the domain-neutral modulations, the built-in selector inventory, and the waveform evidence primitive.

  • Emitters: the concept page for choosing an emitter family.