ACARS (VHF air band)
GopherTrunk decodes ACARS — the Aircraft Communications Addressing and
Reporting System, the plain-old VHF data link aircraft and ground stations
use on 131.550, 131.525, 130.025, 129.125, 131.125 MHz and friends. It runs
off a scanner.conventional AM channel, the same way MDC1200 and
FleetSync hang off an FM channel: no SDR has to be dedicated to it.
Added in response to #1231.
Configuration
storage:
path: ./gophertrunk.db # the decoded blocks live in the SQLite log
scanner:
conventional:
- label: "ACARS primary"
frequency_hz: 131550000
mode: am # required: ACARS rides an AM carrier
decoders: [acars]
acars is accepted only on a mode: am channel. The AM channel’s
carrier-to-noise squelch (squelch_cn_db, default 12 dB) opens the channel;
while a block is being framed the scanner holds the channel (up to 1 s — a
full ACARS block is ~0.9 s) so a hop cannot cut it off.
Decoded blocks land in:
- the
acars_logSQLite table (swept byretention.log_days), GET /api/v1/acars/messages?limit=N(503 withoutstorage.path),- the ACARS panel of the web console (
web.tabs.acars: falsehides it), - the
acars.messageevent on the SSE / WebSocket stream.
Each row carries the aircraft registration, the flight id and message number
(downlinks), the label, block id, the text, the channel it came from, and
whether the block check validated — fixed N when N bits had to be
repaired first.
Air interface
| Modulation | 2400 bit/s MSK on audio tones 1200 / 2400 Hz, amplitude-modulated onto the carrier |
| Line code | 2400 Hz when a data bit equals the previous one, 1200 Hz when it changes |
| Characters | 7-bit ASCII, LSB first, odd parity in bit 7 |
| Block | pre-key (≥16 × 0xFF) · + * SYN SYN · SOH · mode · address (7) · ack · label (2) · block id · STX · text (≤220) · ETX/ETB · BCS (2) · DEL |
| Block check | CRC-16/KERMIT (reflected 0x1021, init 0) over mode … suffix including parity bits; the two BCS bytes, low first, make it zero |
| Downlink text | message number (4) + flight id (6) + body |
The layout, parity sense, bit order, block check and line code were read from
acarsdec (GPL — read for protocol
facts only, nothing ported) and then confirmed in both directions: acarsdec
decodes GopherTrunk’s synthesised audio (and of the four possible
tone/line-code mappings it decodes only the one above), and GopherTrunk
decodes the seven real-air messages in acarsdec’s own test recording to the
same fields acarsdec prints. Literal on-air blocks from that recording pin
the parser (internal/radio/acars/acars_test.go).
Pipeline
channel IQ (~48 kHz, from the scanner's data front end)
→ AM envelope |x| (no carrier recovery needed:
ppm / tuning offset costs nothing)
→ resample to 19.2 kHz (8 samples/bit)
→ coherent MSK demodulator:
mix by the 1800 Hz centre → low-pass
timing: Gardner loop on the one-bit phase difference
data: absolute phase at each bit boundary (BPSK after de-rotation)
→ framer: 48-bit sync hunt (either polarity) → characters → suffix → BCS
→ block check, with repair of up to three single-bit character errors
(located by parity) or one adjacent bit pair, only when the repair is
unique and the result is printable ACARS
Why coherent: the tones encode whether a bit changed, so a receiver that decides tones has to integrate them, and one wrong tone then inverts the rest of the block. On acarsdec’s recording a tone-deciding front end decoded 5 of 7 messages; the coherent detector decodes all 7, and with white noise added it decodes as many or more messages than acarsdec at every level tried.
Verifying a capture
TestACARSReplay replays a recording through the production front end and
prints every block:
# AM-demodulated audio (rtl_fm -M am, any rate, any channel count)
GT_ACARS_AUDIO=capture.wav go test ./cmd/gophertrunk -run TestACARSReplay -v
# raw IQ (wav/flac container, or headerless f32 / cs16)
GT_ACARS_IQ=capture.cs16 GT_ACARS_FORMAT=cs16 GT_ACARS_RATE=2400000 \
GT_ACARS_TUNE_HZ=0 go test ./cmd/gophertrunk -run TestACARSReplay -v
GT_ACARS_EXPECT=N turns the report into an assertion of at least N
CRC-valid blocks.
Known limits
- An ACARS block on a scan-list channel also opens an ordinary AM “call” (the channel’s carrier is up), so short data-burst recordings appear in the call history beside the decoded messages.
- Multi-block messages (ETB) are logged block by block; they are not reassembled.
- Labels are shown raw; no label-specific payload decoding (positions, OOOI times, …) is done.
- VDL Mode 2 (the 31.5 kbit/s D8PSK successor) is a different air interface and is not decoded.