ADS-B Emitter Algorithm¶
Mode-S pulse-position modulation synthesis for ADS-B captures and synthetic scenes.
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.
2. ADS-B (Mode-S PPM)¶
Family. aviation.adsb. Carrier 1090 MHz; we synthesize at scene baseband and let the scene composer mix to scene-relative offset.
Modulation. Binary PPM at 1 Mbit/s. Each bit is 1 µs, split into two 0.5 µs half-bits:
1→ first half-bit high, second low0→ first half-bit low, second high
Frame structure.
Field |
Bits |
Notes |
|---|---|---|
Preamble |
8 µs (4 short pulses) |
Fixed pattern at samples 0, 1, 3.5, 4.5 µs |
Data |
56 (DF11) or 112 (DF17/DF18) bits |
Per format |
def generate_adsb(*, class_label, sample_rate, duration_s, params, rng):
"""
params:
df: int # downlink format: 11, 17, 18
message_bytes: bytes # if None, draw a plausible payload
cadence_hz: float = 1.0 # 1 Hz nominal
cadence_jitter_pct: float = 0.10
"""
bit_rate = 1e6 # 1 Mbit/s
samples_per_bit = sample_rate / bit_rate
samples_per_half = samples_per_bit / 2
# Build one frame
frame_bits = _build_mode_s_frame(params["df"], params.get("message_bytes"), rng)
# Preamble: pulses at 0, 1, 3.5, 4.5 µs (each 0.5 µs wide)
preamble = torch.zeros(int(round(8 * samples_per_bit)), dtype=torch.complex64)
for offset_us in [0, 1, 3.5, 4.5]:
start = int(round(offset_us * samples_per_bit))
end = start + int(round(samples_per_half))
preamble[start:end] = 1.0
# PPM data
n_data = len(frame_bits) * int(round(samples_per_bit))
data = torch.zeros(n_data, dtype=torch.complex64)
for i, bit in enumerate(frame_bits):
slot = int(round(i * samples_per_bit))
h = int(round(samples_per_half))
if bit == 1:
data[slot:slot + h] = 1.0
else:
data[slot + h:slot + 2*h] = 1.0
one_frame = torch.cat([preamble, data])
# Place frames at ~1 Hz cadence with jitter
iq = torch.zeros(int(round(sample_rate * duration_s)), dtype=torch.complex64)
cadence_s = 1.0 / params["cadence_hz"]
nominal_n = int(round(cadence_s * sample_rate))
pos = 0
while pos + len(one_frame) < len(iq):
iq[pos:pos + len(one_frame)] = one_frame
jitter = (torch.rand(1, generator=rng) - 0.5) * 2 * params["cadence_jitter_pct"]
pos += int(round(nominal_n * (1 + jitter)))
return Signal(iq=iq, metadata=SignalMetadata(bandwidth_hz=2.5e6, ...))
def _build_mode_s_frame(df: int, payload: bytes | None, rng) -> list[int]:
"""Construct DF11 (56-bit) or DF17/DF18 (112-bit) frame."""
if df == 11:
# DF11 (5-bit DF + 51-bit data + 24-bit CRC)
...
elif df == 17:
# DF17 ADS-B extended squitter
# 5-bit DF=17 + 3-bit CA + 24-bit ICAO + 56-bit ME + 24-bit CRC
...
# Use pyModeS for canonical bit-level encoding
Bandwidth. ~2.5 MHz occupied (RC-filtered PPM with rise time ~50 ns).
Reference dataset. Daytona ADS-B. Use pyModeS (GPL, lazy-imported) for canonical bit-level ME field encoding (airborne position, velocity, identification).
Cadence. 1 Hz per aircraft per message-type. Realistic distribution per aircraft: ~50% airborne_position, ~25% airborne_velocity, ~15% identification, ~10% surface_position.
See Also¶
Emitter Algorithms: overview and cross-family invariants.
Signal Catalog: browsable family list with parameter ranges.
References¶
The Mode-S frame layout, downlink-format codes, and PPM bit-encoding above follow the canonical aviation surveillance specs. The framework re-implements the synthesis path; payload encoding is delegated to pyModeS rather than re-derived.
RTCA DO-260B, Minimum Operational Performance Standards for 1090 MHz Extended Squitter Automatic Dependent Surveillance Broadcast (ADS-B) and Traffic Information Services Broadcast (TIS-B), RTCA Inc., 2009. (DF17 / DF18 extended-squitter ME field, 112-bit frame layout, 24-bit CRC)
ICAO Annex 10 Volume IV, Aeronautical Telecommunications: Surveillance and Collision Avoidance Systems, International Civil Aviation Organization, 5th ed., 2014. (Mode-S all-call DF11, 56-bit short squitter, 1090 MHz reply channel, 1 Mbit/s PPM bit encoding)
Junzi Sun. The 1090 Megahertz Riddle: A Guide to Decoding Mode S and ADS-B Signals, 2nd ed., 2021. https://mode-s.org/decode/. (Reference for the
pyModeSdecoder used for payload encoding cross-checks)