Receiving APRS packets

Receiving and decoding of APRS packets is now implemented and it seems to work.

It is more tricky than sending packets but there are a fair amount of examples to look at. For instance the BeRTOS TNC implementation. Ok, here is how it is done: First, the audio signal from the receiver is fed to an ADC input on the Teensy 3. Since the ADC expect voltages in the range 0 – 3.3V, we set up a voltage divider that gives the input a +1.65V bias. It is important to get this bias right.

The ADC is sampled 9600 times per second, using a timer (adc_input.c). We use 8 bit sampling and we measure the value when there is no signal (should be close to 127) and subtract this from the sampled values. The AFSK detector (afsk_decode.c) multiplies each sample with a sample a half 1200Hz period earlier. For a perfect 1200Hz wave the resulting values will then always be negative and quite large. For a 2200 Hz wave it will shift between positive and negative values rather quickly. The resulting signal goes through a digital low pass filter and sampled 1200 times per second. To keep the detector in sync with the transmitter,  the phase is slightly adjusted when transitions are detected.  Then we get a stream of bits that represent mark=1=2200 Hz or space=0=1200Hz. From this stream we detect when there are changes from 0 to 1 or vice versa. Since APRS use NRZI encoding, a transition means 0 and no transition means 1 in the resulting stream of bits. The picture on top of this article shows a signal with 01111110 sequences (HDLC FLAG) along with the detected bits on the scope.

These bits goes to a HDLC decoder (hdlc_decode.c adapted from Polaric Tracker) that can recognize packets, do CRC checks, etc. The resulting packets can then be processed further by various applications. For testing, I just convert them to a readable text and display them in the command shell.

Now, how well does this work?  I tested with audio from the WA8LMF TNC test CD and was able to get a decent percentage of packets through.  Perfect signals results in 100% reception, but signals are usually not perfect. Tests on radio sometimes looked good, and sometimes not. I guess, one thing is that i just wired together the Teensy and the radio in a very ad hoc way. This arrangement seems to be prone to RF interference.  Mounting the modules on a proper PCB with shielding, etc. should probably be the next step now.  It is also possible to improve the software detector somewhat. For example, an alternative demodulation method is using FIR filters on 2200 and 1200 Hz. It may give somewhat better results.  My first attempt using this method didn’t succed,  but maybe later..


  1. Update: I now use FIR filters in demodulation. It splits the input stream of samples into two: One for the 1200Hz passband and one for the 2200Hz passband. Detection is done by subtracting one from the other (after some lowpass filtering) and checking if result is above or below zero. The result is further processed to detect transitions like before…

    A well known issue is that the level of the two tones may be different due to pre/de-emphasis of transmitters and receivers. One possible way to address this is to apply a simple ALC algorithm to each of the two sample steams.

    The ARM M4 CPU core of the Teensy has no floating point unit, but it has a set of DSP instructions that can execute FIR filtering more efficiently. A possible future project may be to try this.

Leave a Reply

Your email address will not be published. Required fields are marked *