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.