Architecture
xng is a Rust workspace of 25 crates behind one binary. The whole design follows from one decision: capture once, then fan one normalized message out to everything.
The pipeline
Every mode runs the same stages. The capture is shared; the message at the end is identical regardless of which mode produced it.
SDR (Soapy / native) · IQ file · stdin
│
▼ Capture ──► Channelizer (polyphase, one DDC per channel)
▼ DemodCore (MSK · D8PSK · PSK · GMSK · BPSK · PPM · DQPSK)
▼ FrameCore (AVLC · MPDU/LPDU · SU · HDLC · Mode S · …)
▼ AppLayer (ACARS · ADS-C · CPDLC · MIAM)
▼ Normalized Message
│
▼ message bus ──► broadcast fan-out
├─ asf-2.0 (gRPC / QUIC) ├─ Prometheus
├─ Airframes feed ├─ SBS / Beast / NMEA / MQTT
├─ console / JSON / JSONL └─ web dashboard + data APIWhy capture sharing is the whole game
A single SDR capture feeds many channels, and modes whose bands overlap can share one capture entirely — ACARS and VDL2 both live in 136–137 MHz; Aero and STD-C both sit in 1537–1547 MHz. Where the old tools needed one dongle per decoder, xng needs one dongle per band.
Most fixed-channel modes run a digital down-converter per channel. Iridium is the exception: it runs a wideband FFT burst detector across the entire capture, parallelized across cores — which is why --decode-threads matters most there.
The per-mode decode contract
Each mode is a crate. Adding a mode means adding a crate that satisfies a decode contract — and the runtime, outputs, stats, TUI, and feeding all keep working with zero changes. There is no runtime plugin system; modes are compiled in. The closest extension point for outside code is xng extern, which wraps an external decoder’s output onto the same bus.
Workspace layout
25 crates: five shared cores plus one crate per mode, with the CLI, runtime, outputs, TUI, and web dashboard in the binary.
Core infrastructure
xng-typesThe normalized message model shared by everything.
xng-dspChannelizer, DDC, FIR/NCO, Viterbi, Reed-Solomon, CRCs, scramblers, interleavers, sync/AGC.
xng-sdrSoapySDR + native Airspy backends + IQ-file sources, with sample-rate negotiation.
xng-acarsThe ACARS application layer (ARINC 618/620/622, ADS-C, CPDLC, MIAM) — ported from MIT libacars.
xng-protoThe asf-2.0 protobuf schema (prost/tonic) and conversions.
Decode cores
One xng-mode-* crate per signal — acars, vdl2,hfdl, aero, stdc, iridium, ais,adsb, uat, sonde, navtex, flex, and the rest. Each ships a spec-faithful modulator for loopback tests, vendored validation fixtures, and a PROVENANCE.md documenting exactly where its knowledge came from.
Tech & provenance
Rust (edition 2021) on tokio, with clap (CLI), ratatui (TUI), prost + tonic (gRPC), quinn (QUIC), rustls (TLS), rustfft, and rayon. Decode cores are clean-room from public ICAO, ARINC, ITU-R and ETSI specs, or ported from permissively-licensed projects (libacars, JAERO, iridium-toolkit) with attribution; GPL projects are used as fact references only. Sourcing is documented in docs/REFERENCES.md and each crate’s PROVENANCE.md.
Performance
Every mode runs real-time on Pi-class hardware with --demod-effort live. On Apple M-series, measured throughput runs many times real-time — VDL2 around 85×, HFDL 283×, AIS 8.6×; Mode S 16.6× live. Every count-gated benchmark is enforced by a CI regression gate on each PR, so a change that drops frames does not merge.
For the practical side of all this, see Recipes.