Emitter Algorithms¶
Per-family synthesis pseudocode for emitters whose backends do not deliver a turnkey IQ modulator. Comms modulations are produced by a family of one-class-per-emitter wrappers around torchsig.signals.builders.*, listed in the table below; their algorithms live inside TorchSig and need no separate spec here.
rfgen emitter |
TorchSig backend |
Classes |
|---|---|---|
|
BPSK, QPSK, 8PSK, 16PSK, 16/32/64/256-QAM, OOK, 4/8-ASK |
|
|
2/4/8/16 {FSK, GFSK, MSK, GMSK} |
|
|
OFDM with 64, 72, 128, 180, 256, 300, 512, 600, 900, 1024, 1200, 2048 subcarriers |
|
|
DSB-FC, DSB-SC, SSB-USB, SSB-LSB |
|
|
NB-FM, WB-FM |
|
|
ChirpSS (LoRa-like), LFM data chirp |
|
|
CW |
Each TorchSig-backed emitter applies the same Nyquist guard (occupied bandwidth plus baseband shift fits inside [-fs/2, +fs/2]), the same digital frequency shift (multiplication by exp(j*2*pi*f_offset_hz*t)), and the same DC-subtraction pre-emit so the |mean| / |peak| < 1e-4 contract holds. The shared wrapper structure is documented inline in each module’s docstring.
This page is the implementation spec for Concepts / Emitters and Signal Catalog. Every algorithm below produces complex64 baseband IQ at the requested sample rate, with metadata.snr_db = +inf (the channel layer adds noise).
Note
Layer 3 shipped, Pass-1. The Layer 3 implementation (emitters,
device-fingerprint, tx-impairments, propagation, rx-frontend) landed
on branch rfgen-impl-2026-06-25-105955 (PR #94). The class names,
Pydantic schemas, and Transformation enum members referenced below
match the shipped surface; Pass-1 stubs (GNU Radio OOT emitters,
cellular emitters, Sionna propagation backends) construct cleanly and
raise an EmitterError or ChannelError tagged with
stage="pass1_stub" until backend wiring lands. See
Reference / rfgen.emitters and
Reference / rfgen.channels for the shipped
class roster.
Common shape contract¶
Every emitter:
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: dict | None = None) -> Signal:
# 1. Validate params via self.schema()
# 2. Synthesize at native rate
# 3. Resample to sample_rate via polyphase
# 4. Mix to f_offset_hz: iq * exp(j 2π f_offset n / sample_rate)
# 5. Return Signal(iq=..., metadata=SignalMetadata(...)) - iq shape (2, N), complex64-equivalent
Steps 3 and 4 are shared in BaseEmitter via .__call__, so subclasses focus on step 2.
Family pages¶
Use this page for the shared emitter contract and cross-family invariants. Use the family pages for waveform-specific synthesis details.
Family page |
Covers |
|---|---|
Finite pulsed, LFM, FMCW-saw, Barker-13, and Frank transmit waveforms. Frequency-hopping radar is outside the current contract. |
|
Mode-S PPM synthesis. |
|
LoRa CSS and BLE GFSK. |
|
IEEE 802.11 a/g/p and 802.15.4. |
|
OcuSync, Lightbridge, FPV video, MAVLink, Crossfire, and ExpressLRS shells. |
|
5G NR PUSCH, LTE via srsRAN, and GSM. |
|
SigMF and real-capture replay emitters. |
Cross-family invariants¶
Every emitter satisfies these invariants, enforced by BaseEmitter via .__call__:
IQ output shape
(2, int(sample_rate * duration_s))(channel-first real/imag).Dtype
float32(complex64-equivalent).metadata.snr_db = float("inf").metadata.source = "synthetic"(or"captured"for playback).Same
(rng, params)produces byte-identical IQ; no global state.Bandwidth reported within ±5 % of true 99 % occupancy.
See Also¶
Concepts / Emitters: conceptual overview
Reference / API Reference / BaseEmitter: ABC contract
Reference / Fingerprint Math: per-device hardware impairments applied after emitter output
Signal Catalog: browsable family list with parameter ranges
How-to / Add a custom emitter: step-by-step for new families