Raspberry Pi + RTL SDR dongle = APRS Rx only gate (Part Two – Decoder)

This is part two (here is part one) of the tutorial how to use Raspberry-Pi in APRS gateway.


In part one I explain how to install OS for Raspberry Pi and get RTL dongle up and running, which enables radio reception and listening received signal with FM demodulator on audio jack (simple FM receiver).  For next steps you will need HAM licence with own call sign. Mine is S54MTB.

Frequency calibration

The “RTL” dongle comes with 28.8MHz quartz crystal. Due to it’s low cost nature you should not expect high accuracy. To improve this some fine tuning is required.

There are two options. First is by using software kalibrate. It allows you to adjust the frequency offset due to oscillator tolerance when using other “rtl” software.

cd ~/rtl
sudo apt-get install libtool autoconf automake libfftw3-dev
git clone https://github.com/asdil12/kalibrate-rtl.git
cd kalibrate-rtl
git checkout arm_memory
sudo make install

Scan the GSM frequencies and find some strong base station. I found three GSM signals on frequencies 866.4MHz, 835.2MHz and 950.4MHz in my QTH on locator JN75OT. Run kal with proper GSM band selected:

kal -s GSM850
kal -s GSM900

The result of my receiver was +2.05ppm. I got equal result when plugged into PC and running SDR#:


Another way is to use some SDR application, tune your HAM rig to some legal frequency, switch to packet mode (like you are doing to tune the antenna with (A)TU) and check where your dongle receives the signal. Then calculate the ratio, subtract 1 and multiply with 1.000.000 to get “ppm”:

KALibration_value = ( RTL_receiving_frequency / Reference_frequency – 1 ) × 1000000 [ppm]


Install decoder and APRS iGate script

Go to your “rtl” directory and install the multimon decoder for received audio stream.

cd ~/rtl
sudo apt-get install qt4-qmake libpulse-dev libx11-dev
git clone https://github.com/EliasOenal/multimonNG.git
cd multimonNG
mkdir build
cd build
qmake ../multimon-ng.pro
sudo make install


Now plug your antenna for 2m into RTL dongle and test  APRS decoding. If there are no packets decoded, you may check with your rig sending some APRS packets. If you have “smart” phone you could use V2APRS. Just install the application to your phone, run it and generate some message. Plug the phone audio output to radio station input, tune to 144800 and enable VOX. Voila, in couple of minutes you have APRS generator. To avoid messing with live APRS network you could test it with dummy load and short wire plugged into your dongle inside your shack.

rtl_fm -f 144.800M -s 22050 - | multimon-ng -a AFSK1200 -A -t raw -

The output from the decoder should look something like this:

rtl_fm -f 144.800M -s 22050 - | multimon-ng -a AFSK1200 -A -t raw -
APRS: S54MTB-1>APDU28,WIDE1-1::S54MTB :Abc{81
APRS: S54MTB-1>APDU28,WIDE1-1::S54MTB :Testing testing 123{80

If you just want to listen to the APRS packets try with play instead of multimon-ng:

rtl_fm -f 144.800M -s 22050 - | play -r 24k -t raw -e s -b 16 -c 1 -V1 -

This was my testing bench (1: FT817 was FM Tx on 144800, 2: best antenna ever made with SWR 1:1 (holy grail of every ham), 3: APRS audio generator V2APRS on android phone, 4: Raspberry.Pi, 5: Receive antenna (has worse SWR than 1:1), 6: power supply for Raspberry Pi with 12V-to-USB cigarette lighter plug, 7: Icom H16-T tuned to 144800 (to check audio from FT817), powered from 10Ah LiPo pack, for loooong late night shifts):


And now the final step: Install the python APRS igate

cd ~/rtl
sudo apt-get install python-pkg-resources
git clone https://github.com/asdil12/pymultimonaprs.git
cd pymultimonaprs


Build the APRS software

sudo python setup.py build
sudo python setup.py install

Generate key for your callsign (WITHOUT ANY SUFFIX OR EXTENSION):

./keygen.py S54MTB
Key for S54MTB: 16600


Make a copy edit /etc/pymultimonaprs.json Insert proper callsign, (generated) passcode, Gateway and Lat/Lng

You can find your QTH Lat/Long with google maps. Just right click and select “what’s here”.

You can also calculate pass code via web page: http://www.frn-radio.de/?path=call

sudo cp /etc/pymultimonaprs.json /etc/pymultimonaprs.old
sudo nano /etc/pymultimonaprs.json

You can enter your call with extension in /etc/pymultimonaprs.json  and the pass code remains the same.

I used rotate.aprs.net:14580 for the gateway, but any other is OK.

Now you can test python multimon APRS:

sudo pymultimonaprs

The response should be something like this:

[2014-12-11 20:48:07] INFO: Starting pymultimonaprs
[2014-12-11 20:48:08] INFO: connecting...
[2014-12-11 20:48:08] INFO: connected
[2014-12-11 20:48:08] INFO: # aprsc 2.0.14-g28c5a6a
[2014-12-11 20:48:08] INFO: login S54MTB (PyMultimonAPRS 1.0.0)
[2014-12-11 20:48:08] INFO: # logresp S54MTB verified, server SIXTH

Now you should be able to see your igate on APRS map. Surf to aprs.fi and search for your call sign.

The command is active only for the current session. To make it permanent you should prepare startup script.

Create and edit startup script:

sudo nano /etc/init.d/pymultimonaprs

copy and paste this to editor:

# Provides: pymultimonaprs
# Required-Start: $all
# Required-Stop: $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: start/stop of pymultimonaprs
case "$1" in
 sudo pymultimonaprs --syslog &
 sudo killall pymultimonaprs
 echo "Usage: /etc/init.d/pymultimonaprs {start|stop}"
 exit 1
exit 0

Finally enable the script at boot:

sudo chmod +x /etc/init.d/pymultimonaprs
sudo update-rc.d pymultimonaprs defaults

To start the pymultimonaprs you can nuse the start command:

sudo /etc/init.d/pymultimonaprs start

You may now unplug the whole system and install it on permanent location. I will prepare some instructions for the antenna and simple “UPS” for the rAPRSberry-pi.




Here is little update for ELONICS tuner. I was testing the whole setup with local radio transmitter. The signal was strong enough to override the elonics central DC component in demodulated spectrum. Of course the lower signals from distant stations were not strong enough and stayed below this level. As the consequence, the whole iGate was deaf for all signals except extremely strong (5W, less than 100m away 🙂 !)

Then I tested the multimon-ng decoding with offset tuning mode enabled:

rtl_fm -f 144.788M -s 22050 -M fm -E offset - | multimon-ng -a AFSK1200 -A -t raw -

The decoded packets started to arrive immediately:

APRS: HG5CJW-12>APDI22,HG5PBD-2*,OE6XTR*,WIDE2*:_12212310c000s000g000t043h65
APRS: OK2KVM-2>APOT21,OE6XTR*,WIDE2*:>PHG3654/DIGI Krizanov 660 asl.
APRS: OK2KVM-2>APOT21,OE6XTR*,WIDE2*:!4925.10NS01603.12E# 13.7V 5C
APRS: 9A1DFG-1>APOT21,WIDE1,WIDE2-1:!4528.78N/01621.83E# 12.7V 18C Sisak Digi info: 9a1dfg@hamradio.hr
APRS: S51UL-6>APE4S1,S55YBO-2*,WIDE1:!4637.31N/01540.37E_315/000g000t025r000p000b10280h84PIC WS Pesnica pri MB, 265m asl
APRS: OE6XAR-11>APRSOE,OE6XVR-11*,WIDE1*,WIDE2:!4715.93N/01422.72E#D-Star Klosterneuburgerhuette,SysOP OE6POD
APRS: OE3PDB-13>APTW01,OE6XTR*,WIDE2*:!4815.17N/01605.95E_QRP
APRS: S55UMX-6>APE4S1,OE6XTR*,WIDE2:!4630.98N/01535.50E_359/000g000t039r000p000b09538h51PIC WS POHORJE 934m
APRS: HG1PNY-1>APDI24,OE6XTR*,WIDE2-1:=4734.92N101739.51E#PHG2630/A=001049DIXPRS 2.4.0-beta1 digi+igate sysop:HA1AV/HG2EBH qth:Lilahegy
APRS: S55YBO-2>APZ186,WIDE7-6:!4617.41N/01535.97E#PHG3460/APRS DIGI Mt. BOC-980 m ASL (UIDIGI1.8) 3
APRS: S57OEK-6>APE4S1,S59ACM-11*,WIDE2-1:!4604.09N/01358.10E_270/000g000t039r000p000b10296h66PIC
APRS: OE8XDR-11>APNW01,WIDE2-1:!4636.13N301340.18E# W3 DIGI DOBRATSCH 2166 m
APRS: OE8XHR-11>APRSOE,WIDE5-4:!4657.16N/01441.10E# APRSDIGI-Hohenwart 1818m NN Beacon2
APRS: HG5PGE-2>APNU19,OE6XTR*,WIDE2-1:!4740.56N101829.66E#PHG3730/A=002050 W1 UiDigi Gerecse
APRS: OE6XZG>APWS,OE6XTR*,WIDE2:@312325z4711.87N/01527.91E_292/004g000t031r000p000P000h50b10223v000/wetterstation Schoeckel

The solution was to add this offset tuning to pymultimonaprs script.

First you should make backup and then edit multimon.py:

cd ~/rtl/pymultimonaprs/pymultimonaprs/
cp multimon.py multimon.py-original
nano multimon.py

and change line

'-p', str(self.config['rtl']['ppm']), '$']), '-g', str(self.config['rtl']['gain']), '-'],


'-p', str(self.config['rtl']['ppm']), '$']), '-g', str(self.config['rtl']['gain']), '-E offset -'],

then setup and install once again:

sudo python setup.py build
sudo python setup.py install

You can check the operation with

pymultimonaprs -v

you should see the decoded packets like this:

[2014-12-22 00:04:00] DEBUG: sending: HG2PKB-1>APDI22,WIDE1*,OE6XTR*,WIDE2*,qAR,S54MTB-11:=4702.77NS01739.41E#PHG4736/A=002132DIXPRS 2.2.5-beta1 sysop:HG2EBH qth:Kabhegy






Some useful links

APRS-IS (Automatic Packet Reporting System-Internet Service)

Raspberry Pi + SDR dongle = APRS iGate

rtl_fm guide

APRS Rx-only IGate with Raspberry Pi and DVB-T dongle


Google maps



  1. pa2ta says:

    Thank you for your tutorial, it was very usefull to me.

    I got it working with a RTL2838UHIDIR identifying itself as “Fitipower FC00013 Tuner”. This dongle works with- or without the “-E offset” parameter: when used it reports that the dongle is tuned to 144.800000 Hz, when this parameter is left out it says it is tuned to 145053575 Hz, but it is still receiving signals on 144.8MHz.

    At first multimon-ng did not decode any packet, but it started working after inserting “-p 41” to correct the offset found with “kal -s GSM850”.

    Next step: part 2 of your tutorial!

  2. samo petelinc says:

    vnesel sem lokacijo:

    “beacon”: {
    “lat”: 45.56680,
    “lng”: 15.30570,

    vztrasjno pa me postavlja – ikono na aprs.fi na:
    Location: 45°34.01′ N 15°18.34′ E – locator JN75PN66QA

    kakšna pomoč ??


  3. S53KS says:

    Ali mora multimon-ng zmeraj teči (ga zahteva pymultimonaprs) ali je multimon-ng samo v preizkusne namene?
    rtl_fm -f 144.800M -s 22050 – | multimon-ng -a AFSK1200 -A -t raw –

    Rad bi namreč nastavil, da namesto z RTL-SDR dela z zvočno kartico.

    • Mare says:

      multimon-ng je dekoder. Če hočeš dekodirat iz avdio signalov, uporabi namesto rtl_fm kakšen sox ali kaj podobnega. multimon-ng zahteva svoj format, tako da mora sox pretvorit zvok iz usb v 16 bitni signed (opcije -esigned-integer -b16)

Leave a Reply