Native protocols
Rafe reimplements its radio protocols natively (clean-room, from published
standards and functional facts) instead of shelling out to external decoders.
External binaries (fldigi, WSJT-X, dsd-fme, SatDump, AIS-catcher, …) are kept only
as optional fallbacks/oracles for the edges the native code doesn't cover. See
NOTICE.md for the clean-room provenance of each.
Supported (native)
Weak-signal / WSJT-X family
| Protocol |
Module |
Notes |
| FT8, FT4 |
radio/ftx/ |
8-/4-FSK, LDPC(174,91), CRC-14, Costas sync |
| JT65, JT9, JT4 |
radio/jtx/ |
classic weak-signal MFSK/RS |
| Q65 |
radio/jtx/q65.py |
65-tone, tolerant of Doppler/scatter |
| FST4, FST4W |
radio/jtx/fst4.py |
LF/MF weak-signal + WSPR-style beacon |
| WSPR |
radio/wsprx/ |
K=32 conv + beacon message |
HF keyboard / chat digital
| Protocol |
Module |
Notes |
| RTTY (Baudot) |
radio/rtty.py |
45.45 baud, 170 Hz shift |
| PSK31 / PSK63 / PSK125 |
radio/psk.py, pskvaricode.py |
BPSK/QPSK + varicode |
| Olivia |
radio/olivia.py |
MFSK, Walsh/FEC |
| DominoEX |
radio/domino.py |
incremental-frequency MFSK |
| THOR |
radio/thor.py |
MFSK + FEC (robust) |
| Throb / ThrobX |
radio/throb.py |
tone-pair MFSK |
| MFSK16 / MFSK32 |
radio/mfsk.py |
MFSK + conv FEC |
| Feld Hell |
radio/feld.py |
on/off Hellschreiber raster |
| JS8 (normal/fast/turbo/slow) |
radio/js8/, js8_mode.py |
FT8-derived PHY + JS8 message layer |
| MT63 |
radio/mt63.py |
64-tone Walsh-spread DBPSK OFDM + interleave |
HF ARQ / data-link
| Protocol |
Module |
Notes |
| ARDOP (modem + ARQ + TNC) |
radio/ardop_native.py, ardop_arq.py, ardop_tnc.py |
4FSK/PSK/QAM, RS FEC, ardopcf host interface |
| Mercury |
radio/mercury/ |
OFDM + LDPC + our own ARQ data-link |
Digital voice
| Protocol |
Module |
Notes |
| M17 |
radio/m17.py, m17_voice.py |
4-FSK, Golay/conv/interleave, Codec2 voice |
| P25 Phase 1 (C4FM) |
radio/p25/, radio/imbe.py, radio/mbe.py |
framing + IMBE FEC + native IMBE/MBE vocoder (no mbelib) |
| DMR (Tier I/II) |
radio/dmr.py, radio/ambe.py |
4FSK bursts, BPTC(196,96), LC/CSBK, voice superframe + native AMBE+2 |
| D-STAR (DV) |
radio/dstar.py, radio/ambe.py |
GMSK 4800, coded radio header, voice + slow data, native full-rate AMBE |
| YSF (System Fusion) |
radio/ysf.py, radio/ambe.py |
C4FM, FICH, DN-mode DCH/VCH frames + native AMBE+2 |
| NXDN |
radio/nxdn.py, radio/ambe.py |
2400/4800-sym 4FSK, LICH/SACCH/FACCH1, VCALL + native AMBE+2 |
| FreeDV |
radio/freedv_native.py, freedv_voice.py |
via libcodec2 (LGPL) |
| Codec2 |
radio/codec2_native.py |
via libcodec2 (LGPL) |
| RVQ-voice |
radio/rvqvoice*.py |
original low-bitrate source-filter + RVQ vocoder |
Paging / packet / maritime
| Protocol |
Module |
Notes |
| POCSAG |
radio/pocsag.py |
512/1200/2400 bps FSK pager |
| APRS / AX.25 (RX + TX) |
radio/aprs.py, aprstx.py |
1200 bps AFSK, HDLC |
| NAVTEX / SITOR-B (CCIR 476) |
radio/navtex.py |
FEC-B time-diversity |
| Marine DSC (ITU-R M.493) |
radio/dsc.py |
10-bit symbols + B-count check, DX/RX time diversity, MMSI/format/EOS/ECC, FSK + AFSK |
| CW / Morse |
radio/cwdecoder.py |
decode + encode |
Images / fax
| Protocol |
Module |
Notes |
| WEFAX / radiofax |
radio/wefax.py |
HF weather-fax IOC576 |
| HamDRM / digital SSTV |
radio/hamdrm/ |
amateur DRM (EasyPal-compatible) |
| SSTV |
(sstv library) |
Scottie/Martin/Robot — via a Python lib, not clean-room |
Broadcast / wideband
| Protocol |
Module |
Notes |
| RDS |
radio/rds.py |
FM broadcast data, 57 kHz subcarrier |
| DAB / DAB+ |
radio/dab/ |
Mode I OFDM, RS, MP2/HE-AACv2 (linked codecs) |
| DVB-S / DVB-S2 (DATV) |
radio/datv/ |
ETSI EN 300 421 / 302 307-1, LDPC |
Satellite / space (CCSDS)
| Protocol |
Module |
Notes |
| CCSDS TM core |
radio/sat/ccsds.py |
r=1/2 K=7 Viterbi, RS(255,223) dual basis, PN, CADU |
| NOAA APT |
radio/sat/apt.py |
2400 Hz AM subcarrier → image (FM audio) |
| Meteor-M LRPT |
radio/sat/lrpt.py |
QPSK → CCSDS → Meteor JPEG image |
| LRIT / HRIT (xRIT) |
radio/sat/xrit.py |
GOES/Meteosat file layer, JPEG via Pillow |
| NOAA HRPT |
radio/sat/hrpt.py |
Manchester, 10-bit AVHRR de-interleave |
| MetOp / FengYun AHRPT |
radio/sat/ahrpt.py |
AVHRR over the CCSDS core |
| Inmarsat-C STD-C / EGC |
radio/inmarsat.py |
BPSK 1200, r=1/2 K=7 Viterbi, interleave/descramble, unique-word sync, SafetyNET/MSI EGC parse |
| Inmarsat AERO / ACARS |
radio/acars.py |
ACARS message layer (ARINC-618) + AERO framing over the Inmarsat-C FEC |
| VHF ACARS |
radio/acars.py |
2400 bps AM-MSK PHY (1200/2400 Hz), char odd-parity, SYN SYN SOH sync |
Tracking / sensing / trunking
| Protocol |
Module |
Notes |
| AIS (marine) |
radio/aisdecode.py |
GMSK, NRZI, AX.25-like |
| ADS-B Mode S |
radio/adsb.py |
DF17, CRC-24, CPR position |
| VDL Mode 2 |
radio/vdl2.py, radio/acars.py |
D8PSK 10500, RS(255,249), LFSR scramble, AVLC + ACARS |
| ISM / rtl_433 devices |
radio/rtl433/ |
config-driven OOK/FSK sensor decode |
| LoRa (CSS) |
radio/loracss.py, lorasdr/ |
chirp spread spectrum + Semtech PHY |
| TETRA (downlink signalling) |
radio/tetra/ |
π/4-DQPSK, RCPC/Viterbi, clear signalling only |
Radio control / transport
| Protocol |
Module |
Notes |
| RS-BA1 (Icom network) |
radio/civ.py, control.py, passcode.py |
UDP transport, CI-V, credential scrambling |
Remaining candidates (not yet native)
| Protocol |
Currently |
Feasibility |
| dPMR (digital voice) |
dsd-fme/mbelib |
Feasible — shares NXDN's 2400-sym 4FSK PHY (now parameterised) and the native AMBE+2 codec; only its ETSI TS 102 658 framing remains. The last standing candidate. |
On the AMBE/IMBE vocoder
The digital-voice modes were previously flagged "voice gated" by the AMBE patent.
That is not a technical barrier (mbelib is a clean-room software MBE decoder) and
in this project's context it is not a practical legal one either: IMBE (P25 Phase 1)
patents have expired, the UK/EU do not grant patents on "programs as such", and the
project is non-commercial. A native MBE vocoder (radio/mbe.py) therefore
replaces the mbelib IMBE->PCM step, making P25 and the DMR/D-STAR/YSF/NXDN family
fully native. AMBE+2 (some in-force patents) is the only lingering edge.
Out of scope (not wire protocols)
- whisper.cpp — speech-recognition model (transcription), not a protocol
- Pat — Winlink email/BBS client app (ARDOP itself is native)
- ffmpeg (DATV video codec), Leaflet / OSM tiles (maps) — libraries
Implementation log
Remaining candidates are implemented one at a time, each on its own worktree; as
each lands it moves from the candidates table to Supported.
- VDL Mode 2 —
radio/vdl2.py, test_vdl2 (8). ICAO Annex 10 Vol III: a
Gray-coded differential 8-PSK modem at 10500 sym/s (RRC shared with tetra,
widened to 8 phases; a one-step slip is a single-bit error), RS(255,249) over
GF(0x11D) (6 parity, corrects 3 byte errors -- BM/Chien/Forney ported from
sat/rs.py), a 15-bit LFSR scrambler, a Golay(24,12)-protected length header,
and the AVLC data-link (HDLC bit-stuffing + CRC-16/X.25 reused from
aisdecode, 24-bit ICAO addresses). ACARS rides in an AVLC information field
and is handed to the existing acars.parse. A full burst -- ACARS message ->
AVLC -> RS/scramble -> D8PSK IQ and back -- round-trips clean and through
30 dB complex noise. Training symbols, scrambler seed, header bit layout and
RS symbol ordering are the on-air interop checks. dPMR is now the only
remaining candidate.
- VHF ACARS PHY —
radio/acars.py, test_acars (+5). The 2400 bps AM-MSK
front end for the existing ARINC-618 message layer: phase-continuous
1200/2400 Hz tone modem, per-character odd parity, and the pre-key + '+' '*'
SYN SYN SOH air frame, with the 16-bit BCS sent raw (not parity-bearing) and
DEL terminating. A message round-trips through the modem clean and recovers
through 30 dB noise + an arbitrary sample offset; the BCS surfaces a
mid-message corruption. Only VDL2's D8PSK + AVLC now remains in the
candidates table. Exact pre-key/bit-sync values and mark/space polarity are
the on-air interop checks.
- NXDN —
radio/nxdn.py, test_nxdn (7). The NXDN common air interface
over the p25 4FSK modem with its symbol rate parameterised (2400 sym/s for
the 6.25 kHz 4800 bps channel, 4800 for 9600; both tested). The 384-bit
frame: 20-bit FSW + LICH (7+parity bits sent twice) + SACCH (26 info +
CRC-6, K=5 conv punctured 72->60 -- the shared ysf Viterbi gained erasure
support) + either 4 x 72-bit AMBE+2 channel frames or duplicated 144-bit
FACCH1 (80 info + CRC-12, punctured 192->144) carrying VCALL/TX_RELEASE
with 16-bit unit IDs. A call round-trips clean at both rates and at 30 dB
SNR. FSW value, puncture patterns and LICH functional-type encodings are
the on-air interop checks. dPMR now needs only its ETSI framing on this PHY.
- YSF —
radio/ysf.py, test_ysf (6). Yaesu System Fusion over the shared
4800-sym C4FM modem: the 960-bit / 100 ms frame (40-bit sync 0xD471C9634D +
200-bit FICH + 720-bit payload), the published FICH chain (32 info bits +
CRC-16 -> 4x Golay(24,12) -> rate-1/2 K=5 trellis-terminated conv -> 20x10
interleave), and DN mode (V/D type 2): five DCH/VCH pairs per frame with the
20-char CSD (dest+source callsigns, CRC-16 + K=5 conv + 20x18 interleave +
PN whitening) and 5 x 72-bit AMBE+2 channel frames. A header/comm/terminator
call round-trips clean and at 30 dB SNR. Interleave orderings, whitening
seed, K=5 polynomials and YSF's exact V/D2 voice protection are the on-air
interop refinements.
- D-STAR —
radio/dstar.py, test_dstar (7). JARL DV mode: a GMSK-4800
modem on FM-discriminator audio (Gaussian pulse shared with aisdecode), the
660-bit radio header (39 bytes of flags + RPT2/RPT1/UR/MY callsigns, CRC,
rate-1/2 K=3 (7,5) conv + 12x55 interleave + x^7+x^4+1 scramble), and the
96-bit voice frame stream: 72-bit full-rate AMBE (radio/ambe.py) + 24-bit
slow data with the 0x55 2D 16 sync every 21st frame and the 20-char TX
message in 0x70 4F 93-scrambled type-0x4x blocks. Header survives 8 bit
errors (Viterbi + CRC), full stream round-trips clean and at 30 dB SNR.
Interleave geometry, LFSR seed, sync/termination patterns and the in-frame
AMBE bit interleave are the on-air interop checks.
- DMR —
radio/dmr.py, test_dmr (8). ETSI TS 102 361 Tier I/II: the 264-bit
burst geometry + the four 48-bit sync words over the shared 4800-sym C4FM modem
(p25/demod reused unchanged), BPTC(196,96) (Hamming(15,11) rows x new
Hamming(13,9) columns in p25/fec, 181-step interleave), Golay(20,8) slot type,
full LC with RS(12,9) parity under the 0x969696/0x999999 masks, CSBK with the
0xA5A5 CRC mask, and the A-F voice superframe: 3 x 72-bit AMBE+2 channel frames
per burst with the 72-bit LC embedded across B-E under EMB. A full call (header
- superframe + terminator) round-trips through C4FM audio clean and at 30 dB
SNR. The EMB (16,7) generator, embedded-LC/CACH interleaves and RS(12,9)
generator constants are the on-air interop checks.
-
AMBE parameter codecs — radio/ambe.py, test_ambe (7). The DV-family
voice frames onto the native MBE vocoder, as imbe.py did for P25: the
half-rate AMBE+2 49-bit parameter frame (DMR/NXDN/YSF-DN) and the D-STAR
full-rate 48-bit frame, each carried as a 72-bit channel frame -- Golay(24,12)
over the most-sensitive bits + a PN-scrambled Golay(23,12) (hr) / Golay(24,12)
(fr) + unprotected tail. Parameters round-trip (f0 <5%, amp corr >0.7, V/UV
0.8) and the channel frames correct 3+3 injected errors. DVSI's exact VQ
tables, PN seed and inter-frame prediction remain for bit-exact on-air audio.
-
Marine DSC — radio/dsc.py, test_dsc (8). ITU-R M.493 10-bit symbol codec
- B-count check, DX/RX time-diversity error correction, message build/parse
(format/MMSI/category/self-ID/EOS/ECC), FSK + AFSK modem. Round-trips through an
AFSK audio channel clean and under noise; time diversity recovers fully-corrupt
DX copies. On-air interop (exact M.493 bit ordering / phasing) is the next check.
- ACARS + Inmarsat AERO —
radio/acars.py, test_acars (6). The ARINC-618
ACARS message layer (mode/registration/label/block-id/sequence/flight/text + the
CRC block-check) shared across VHF/VDL/satellite, plus AERO framing that reuses
the Inmarsat-C r=1/2 Viterbi + interleave/descramble. Messages round-trip and
recover through 3%% bit errors; BCS rejects tampering. The VHF-ACARS MSK PHY and
the AERO burst timing are the interop steps.
- IMBE parameter codec -> native P25 voice —
radio/imbe.py, test_imbe (5).
The 88-bit IMBE frame <-> (f0, per-band V/UV, harmonic amplitudes): the
fundamental/harmonic-count relation (w0=4pi/(b0+39.5), L via the 0.9254 band
limit) is spec-accurate; amplitudes are gain + differential-log quantised,
bit-allocated by L. Wired through p25.voice's Golay/Hamming/PN FEC and the MBE
vocoder, an 88-bit frame -> PCM runs fully native (p25 Vocoder.decode, no
mbelib). f0 within 5%, amplitude corr 0.7-0.98 (finer at low L), V/UV ~95%,
surviving 8 channel bit errors. The exact IMBE amplitude tables + inter-frame
DCT prediction remain for bit-exact on-air audio.
- Native MBE vocoder —
radio/mbe.py, test_mbe (7). The Multi-Band Excitation
analysis + synthesis (pitch f0, per-harmonic voiced/unvoiced, harmonic
amplitudes; sinusoid + shaped-noise synthesis) -- the IMBE/AMBE step formerly on
mbelib. f0 within 5%, amplitude corr >0.95, V/UV recovered incl. mixed, envelope
corr >0.97. Unblocks fully-native P25 voice (its 88-bit IMBE frame maps to these
params) and the DMR/D-STAR/YSF/NXDN family; the per-codec quantisation mapping is
the wiring step.
- MT63 —
radio/mt63.py, test_mt63 (6). 64-carrier DBPSK OFDM with Walsh(64)
symbol spreading + block interleave; text round-trips clean and through noise up
to 0.6x signal power. Exact fldigi interop (bandwidth/tone spacing, interleave
geometry, the secondary channel) is the refinement.
- Inmarsat-C STD-C / EGC —
radio/inmarsat.py, test_inmarsat (7). BPSK-1200
chain reusing the sat r=1/2 K=7 Viterbi + BPSK receiver; descrambler, block
interleaver, unique-word frame sync, and the EGC (SafetyNET/MSI) message layer.
Recovers the message clean, through noise+offset, and from raw BPSK IQ (180-deg
ambiguity resolved via the unique word). AERO/ACARS remains. Scrambler/
interleaver/UW constants need on-air validation.