libacarsd • C • real-time DSP

The decoder was not “just an FFT”.

ACARSd’s production decoder used a lightweight, empirical strategy: repeated passes over PCM audio, multiple timing code tables, message validation and automatic reordering of the strategies that worked best on a particular receiver chain.

The exact frame signature

*<SYN><SYN><SOH>

The recovered libacarsd2 source preserves the preamble literally as {'*',0x16,0x16,0x01}. Hex 0x16 is SYN and 0x01 is SOH. A second accepted preamble contains four SYN characters before SOH.

Measure transitions, not spectra.

The production path walks the PCM stream sample by sample and observes meaningful changes in signal direction. The number of samples between those transitions becomes a cheap proxy for the signal’s timing structure.

That avoids performing a Fourier transform on every analysis window. For early-2000s hardware, the difference mattered: the same computer could decode several receiver channels in real time.

if (signal changes direction) {
    halfwave_length = samples_between_changes;
    chkContent(..., halfwave_length, ...);
}

Why this was practical

Analog scanner audio is messy. Levels, filters, sound cards and noise differ. ACARSd therefore did not demand one mathematically perfect waveform. It tried several interpretations cheaply and let message structure and CRC decide which one was credible.

LOW CPUREAL AUDIOMULTI-PASS

Ten code tables. Several passes. One message.

The library maintains multiple threshold tables. Each table represents a slightly different set of timing decisions. A second dimension — the decoder pass — changes the sensitivity used to recognize a transition in the PCM signal.

DIMENSION A

Code table

Defines acceptable timing ranges for warm-up/synchronization and the interpretation of pulse lengths during message decoding.

DIMENSION B

Decoder pass

Changes the threshold at which an amplitude change is considered significant, helping with quiet, distorted or noisy recordings.

VALIDATION

ACARS structure + CRC

Candidate frames are checked against synchronization, field plausibility, parity and CRC. The winning decode is selected by confidence rather than by signal guess alone.

Adaptive heuristic

The receiver taught ACARSd which table to try first.

Successful tables incremented per-table counters. acarsd_sorttables() then reordered the tables so the combinations that worked best on a particular station were tried earlier.

Lib->acarsd_utable[codeT]++;
...
acarsd_sorttables(Lib);

This is not machine learning in the modern sense. It is a very effective adaptive heuristic: optimize the search order using the observed success rate of the real installation.

SOURCE FINDING

“Resorting codetables to save CPU time”

The source itself explains the goal. Accuracy was gained by trying alternatives; speed was recovered by learning which alternatives usually succeeded first.

The development process looked like a modern regression corpus.

When ANAD decoded audio that ACARSd missed, a special build saved the failed sound. Those recordings became real-world examples against which the decoder could be improved. The library also contains a “learn” path that sweeps parameter combinations and records successful settings.

decode_buffer_learn(...)
    vary timing parameters
    decode the same captured audio
    if successful:
        write combination to /tmp/success.txt
        update successful min/max ranges

The famous contemporary comparison documented on acarsd.org: “ANAD 137 — ACARSd 169 in one hour.”

FFT experiments

FFT code exists — but it does not appear to be the production secret.

The RCS archive contains FFT routines and functions such as ACARS_Decoder_FFT(). Their surrounding code contains debug-oriented behavior and experimental scaffolding. The mature production path is the code-table/multi-pass decoder.

This matches the project story: FFTW was suggested early, but the successful system came from empirically understanding the actual waveform and building a decoder around what worked.

ENGINEERING LESSON

Optimize for the real problem

A theoretically elegant approach is not automatically the most robust approach on inexpensive receivers, analog audio paths and early-2000s CPUs. ACARSd traded a little redundant work for fault tolerance.

TWO IN ONE was almost absurdly simple.

Stereo audio is interleaved L/R. ACARSd changed the stride to two samples: pass one started on the left channel, pass two on the right. No costly buffer split was needed.

// conceptual form
forward = 2;
decode(buffer + 0);  // L L L L ...
decode(buffer + 1);  // R R R R ...

That efficiency is one reason the architecture scaled from one receiver to two, then to several sound cards and eventually eight scanners.

Next: how those decoders became a network →

Why is there FFT code in the archive?

Because Dr. Armin Zundel, KjM's boss at INLINE and a mathematician, was quite convinced that a Fast Fourier Transform was the proper solution. Prof. Kay Berkling also helped with the FFT experiments. The April 2003 README already records both contributions — and adds the dry parenthesis that “acarsd is still running without it”. Those experiments survived in the source tree. In practice, however, FFT never became the heart of the production decoder. The empirical multi-pass/code-table approach won because it worked better for the real signals and hardware they were testing.

The mathematician proposed FFT. The baker kept testing real radio signals. The code tables won.

Balatonföldvár • real traffic as a test bench

A good place to write an ACARS decoder.

Much of ACARSd was developed in Balatonföldvár, Hungary, roughly between Budapest and Zagreb. Busy European air routes crossed the wider region, so real ACARS traffic was usually available without waiting hours for a useful transmission. That made everyday development unusually practical: change the code, restart the decoder and test against live radio.

The project's 2003 frequency list documented 131.725 MHz as the primary European channel and 131.525 MHz as the European secondary channel, alongside regional and worldwide alternatives.

2008 • FIRST PERSONAL STEREO TEST

Two receivers, one stereo sound card.

ACARSd's stereo mode already existed before 2008. That year a friend gave KjM a tiny airband receiver with a small antenna. Tuned to the European secondary frequency beside the main receiver, it finally let him test the feature himself under real conditions.

It worked immediately.

“Stereo” did not mean stereo listening: the left and right sound-card channels acted as two independent decoder inputs, allowing two receiver frequencies to be decoded at the same time.

Frequency2003 ACARSd description
131.550 MHzPrimary channel worldwide
131.125 MHzAdditional channel for USA
131.450 MHzPrimary channel for Japan
131.475 MHzAir Canada company channel
131.525 MHzEuropean secondary
131.725 MHzPrimary channel in Europe
131.850 MHzNew European frequency

Source note: frequency labels above reproduce the surviving 2003 ACARSd frequency list; the Balatonföldvár and 2008 two-receiver test story is KjM's 2026 recollection.

What was inside those decoded frames? ACARS was an operational messaging system, not simply a position beacon. The recovered archive includes routes, clearances, maintenance, performance and operational messages. See “What exactly was ACARS?” →