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

TorchSigCommsEmitter

signals.builders.constellation.constellation_modulator

BPSK, QPSK, 8PSK, 16PSK, 16/32/64/256-QAM, OOK, 4/8-ASK

TorchSigFSKEmitter

signals.builders.fsk.fsk_modulator

2/4/8/16 {FSK, GFSK, MSK, GMSK}

TorchSigOFDMEmitter

signals.builders.ofdm.ofdm_modulator

OFDM with 64, 72, 128, 180, 256, 300, 512, 600, 900, 1024, 1200, 2048 subcarriers

TorchSigAMEmitter

signals.builders.am.am_modulator

DSB-FC, DSB-SC, SSB-USB, SSB-LSB

TorchSigFMEmitter

signals.builders.fm.fm_modulator

NB-FM, WB-FM

TorchSigChirpEmitter

signals.builders.chirpss.chirpss_modulator, signals.builders.lfm.lfm_modulator

ChirpSS (LoRa-like), LFM data chirp

TorchSigToneEmitter

signals.builders.tone.tone_modulator

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

Radar

Finite pulsed, LFM, FMCW-saw, Barker-13, and Frank transmit waveforms. Frequency-hopping radar is outside the current contract.

ADS-B

Mode-S PPM synthesis.

LoRa and BLE

LoRa CSS and BLE GFSK.

Wi-Fi and Zigbee

IEEE 802.11 a/g/p and 802.15.4.

Drone

OcuSync, Lightbridge, FPV video, MAVLink, Crossfire, and ExpressLRS shells.

Cellular

5G NR PUSCH, LTE via srsRAN, and GSM.

Capture replay

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