rfgen.emitters¶
The catalog of waveform generators. Every concrete class subclasses BaseEmitter and produces a baseband SignalMetadata-tagged IQ tensor.
Scientific validation
The emitter backends have been scientifically validated against published references. See the per-backend reports:
TorchSig comms backend: validated with documented limitations.
TorchSig FSK backend: validated.
TorchSig OFDM backend: validated with documented limitations.
TorchSig AM backend: validated with documented limitations.
TorchSig FM backend: validated with documented limitations.
TorchSig chirp backend: validated with documented limitations.
TorchSig tone backend: validated with documented limitations.
Chirp radar backend: validated with documented limitations.
Each report covers construct validity, mathematical correctness against cited equations, empirical comparison to published reference numbers, literature grounding, experimental methodology, operating envelope, and documented limitations.
Module summary¶
import torch
import rfgen.emitters as emitters
emitter = emitters.ChirpRadarEmitter() # always available (scipy is a runtime dep)
signal = emitter.generate(
class_label="lfm_chirp",
sample_rate=10_000_000,
duration_s=0.001,
f_offset_hz=0.0,
rng=torch.Generator().manual_seed(0),
)
Every emitter has the same surface (the generate keyword-only signature, the family and supported_classes ClassVars, schema() returning a Pydantic model). What differs is the synthesis math, the parameter schema, and the dependency tier.
Importing rfgen.emitters does NOT force-import Sionna, srsRAN, RadarSimPy, gr-lora_sdr, or any other optional dependency; missing-extra emitters raise BackendUnavailableError only when the class is actually instantiated.
Class index¶
Class |
Family |
|
Backend / extra |
Notes |
|---|---|---|---|---|
abstract |
abstract |
abc |
ABC; subclass to add a custom emitter |
|
|
|
|
1090 MHz Mode-S; Pass-1 stub ( |
|
|
|
GNU Radio OOT; |
BLE; Pass-1 stub |
|
|
|
|
LFM chirp; fully implemented |
|
|
|
srsRAN over ZMQ; |
LTE; Pass-1 stub |
|
|
|
|
LoRa fallback backend; Pass-1 stub |
|
|
|
|
Default LoRa backend; Pass-1 stub |
|
|
|
|
5G NR PUSCH; Pass-1 stub |
|
|
|
|
Pulsed radar; Pass-1 stub |
|
|
TorchSig AM signal builders |
|
AM-family (DSB-FC, DSB-SC, SSB-USB, SSB-LSB) |
|
|
TorchSig chirp signal builders |
|
LoRa-style chirp spread spectrum |
|
|
TorchSig constellation builders |
|
PSK / QAM / OOK / ASK constellations |
|
|
TorchSig FM builders |
|
NB-FM / WB-FM |
|
|
TorchSig FSK builders |
|
FSK / GFSK / MSK / GMSK |
|
|
TorchSig OFDM builders |
|
OFDM-{64…2048} sizes |
|
|
TorchSig tone builder |
|
CW tone |
|
|
|
GNU Radio OOT; |
802.11 a/g/p; Pass-1 stub |
|
|
|
GNU Radio OOT; |
802.15.4 OQPSK; Pass-1 stub |
Each emitter ships a matching *Params Pydantic v2 model (e.g. ChirpRadarParams, LoRaParams, LTEParams) returned from schema() and re-exported from rfgen.emitters.
For the conceptual map of supported families and roadmap items deferred to a future emitter expansion (drone family, GSM, captured playback, NR downlink, extended radar waveforms), see Signal Catalog. For synthesis algorithms, see Reference / Algorithms.
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). Behindrfgen[lora-sdr].LoRaPHYEmitter is the pure-Python fallback, backed by the
loraphylibrary. Behindrfgen[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. Backend selection happens at scene-config time via the LoRaBackend enum ("gr-lora-sdr" or "lora-phy").
Earlier drafts described a LoRaEmitter class with a multi-class supported_classes taxonomy of dotted strings (e.g. "iot.lora.css.sf7.bw125.cr-4-5"); the shipped framework keeps the SF/BW/CR knobs on LoRaParams and uses the single "lora" label so the scene composer treats LoRa as a single family with backend-selectable internals.
class rfgen.emitters.BaseEmitter¶
Produces IQ for one emitter on a shared time/frequency grid. Stateless across calls; all randomness comes from the supplied rng.
from abc import ABC, abstractmethod
from typing import ClassVar
import torch
from pydantic import BaseModel
class BaseEmitter(ABC):
"""One emitter family. Implementations are stateless; call producing IQ."""
family: ClassVar[EmitterFamily]
supported_classes: ClassVar[tuple[str, ...]]
@abstractmethod
def generate(
self,
*,
class_label: str,
sample_rate: float,
duration_s: float,
f_offset_hz: float,
rng: torch.Generator,
device_id: str | None = None,
params: BaseModel | None = None,
) -> Signal: ...
@abstractmethod
def schema(self) -> type[BaseModel]: ...
Contract¶
Output IQ shape MUST be
(2, int(sample_rate * duration_s)).Output IQ MUST be at baseband;
f_offset_hzis the desired offset from baseband DC, applied by the emitter (not the caller). Some emitters (LoRa CSS, FMCW radar) sweep across frequency intrinsically, so post-hoc mixing would alias the chirp slope.Returned
Signal.metadataMUST be self-consistent:start_samplewithin[0, int(sample_rate * duration_s)), frequency-extent fields within[-sample_rate/2, +sample_rate/2].Implementations MUST consume randomness only from
rngfor determinism under shard re-runs.paramsis a backend-specific Pydantic model (e.g.ChirpRadarParams); validated by the caller against the emitter’sschema()before calling.device_id, when set, is used by the fingerprint module to pick a deterministic CFO/IQ-imb/phase-noise profile per virtual device.
Class attributes¶
Attribute |
Type |
Description |
|---|---|---|
|
|
Top-level family tag, copied into every emitted |
|
|
Whitelist of |
Method: generate¶
Abstract method on BaseEmitter. Produces one emitter’s IQ plus its ground-truth metadata. See the full normative contract above.
Method: schema¶
Abstract method on BaseEmitter. Returns the Pydantic v2 model describing valid params for this emitter, used by the config validator and by rfgen inspect emitters --schema <family>.
Why stateless?
Spark workers spin emitters up per shard; statelessness lets us instantiate freely without worrying about reset between calls. State that should persist across emitters (per-device fingerprint parameters) lives in rfgen.device_fingerprint.DeviceRegistry, keyed by device_id.
Why is f_offset applied by the emitter, not the scene composer?
Some emitters (LoRa CSS, FMCW radar) sweep across frequency intrinsically; mixing them up after the fact would break their structure. Letting each emitter own its baseband-to-target translation keeps the composition layer agnostic.
Stub class anchors¶
The catalog rows above each follow the same template as ChirpRadarEmitter (the fully-implemented reference example). Most concrete emitters in this Layer 3 release ship as Pass-1 stubs: the constructor validates the lazy-import gate and pins the class attributes, but generate() raises EmitterError until the backend wiring lands in a future Pass-N. The TorchSig comms backends (TorchSigCommsEmitter, TorchSigFSKEmitter, TorchSigOFDMEmitter, TorchSigAMEmitter, TorchSigFMEmitter, TorchSigChirpEmitter, TorchSigToneEmitter) and ChirpRadarEmitter are fully implemented.
The anchor labels below give every shipped class a stable cross-reference target so concept pages, glossary entries, and how-to guides can use {ref} links per the STYLE.md code-span linking rule.
Per-protocol algorithm references:
TorchSig*Emitterfamily: TorchSig v2.1.x signal-builder docs upstream; no per-protocol page because the math lives upstream.
Anchor aliases (cross-reference compatibility)¶
The anchors below preserve cross-reference targets from earlier design drafts that named classes the shipped framework does not export. Each resolves to the class index above; readers looking for a class name that no longer exists will land on the index and see what shipped instead.
Earlier drafts described LoRaEmitter (now LoRaSdrEmitter + LoRaPHYEmitter, see LoRa: dual backend) and LTEDownlinkEmitter (the shipped class is LTEEmitter with supported_classes = ("lte_pusch", "lte_pdsch")). The drone family (OcuSyncShellEmitter, LightbridgeShellEmitter, FPVAnalogEmitter, MAVLinkTelemetryEmitter, CrossfireELRSEmitter), the extended radar waveforms (FMCWRadarEmitter, FrequencyHoppingRadarEmitter), GSMEmitter, NRDownlinkEmitter, and CapturedPlaybackEmitter are planned for a future emitter expansion and not yet implemented; see the Deferred for future emitter expansion section of the signal catalog for the canonical deferral list.