VnutZ Domain
Copyright © 1996 - 2024 [Matthew Vea] - All Rights Reserved

2024-01-27
Featured Article

Direwolf on a Raspberry Pi with RTL-SDR

[index] [2,610 page views]
Tagged As: APRS, HAM Radio, How To, Raspberry Pi, and SDR

Introduction

One of the first things I experimented with after getting my HAM license was APRS (Automatic Packet Reporting System). It was created more than 30 years ago by Bob Bruninga, call sign WB4APR, as one of the few positive things out of the Naval Academy. A HAM can do lots of things with APRS like sending position, exchanging messages, providing weather reports, etc.

I found on long road trips that while my general route was picked up by digipeaters and IGates near large cities, there were huge swaths of travel where no APRS radios heard me. I wasn't even picked up by digipeaters. Fortunately, creating my own, portable solution was not hard and using a Raspberry Pi allows for a lot of flexibility using Direwolf. Not only could Direwolf handle digipeating for any drivers local to me with handhelds, but if I patched the Raspberry Pi's WiFi adapter into my phone, then I could provide mobile an IGate for the quiet areas I drove through.

Credit

This article is based primarily as a clone of my original article "Direwolf on a Raspberry Pi for Mobile APRS" but modified for using a simple RTL-SDR as a receive-only APRS station as opposed to a full RX/TX capable station.

The original article is based on the "APRS Digipeater/iGate with Direwolf (Entry Level)" article by KI5GTB and "Raspberry Pi based APRS Tracker/Digipeater/iGate" by VK3IL. Additionally, the Direwolf user manual expounds on all the configuration options that are possible and allowed me to easily adapt from KI5GTB's guide into a dynamic solution for mobile use.

Additional resources for the RTL2832U version came from "A Guide To Setting Up an APRS Receive Only IGate Using a Raspberry Pi and an RTL-SDR Dongle" by G6NHU. "Raspberry Pi SDR IGate" by WB2OSZ was also used.

Requisite Hardware

Not a lot of equipment is necessary for creating a mobile APRS station capable of serving as an IGate. For this application, I used a Raspberry Pi 4, a GPS dongle, and a simple RTL2832U Software Defined Radio (SDR). The better RTL2832U's often have an SMA antenna jack allowing you to optimize reception but they also typically come with an antenna that does fine. For the sake of this guide, the base installation of the Raspberry Pi will be assumed.

  • Raspberry Pi
  • RTL23823U RTL-SDR
  • GPS Dongle
  • Portable Battery
  • [recommended] USB Hub
Image linked from: Adafruit product website

Raspberry Pis are easily acquired from a variety of locations like Amazon or Adafruit. Running the "lite" distributions without the graphical interface reduces the processing demands and a Model 3+ or better Pi will be more than sufficient. Make sure to get a portable USB battery charger of some kind otherwise you'll have a lot of inert equipment that looks cool.

It's optional, but highly recommended, to get a USB hub that attaches devices vertically. Due to the compact nature of the Raspberry Pi, the plug ends of the Easy Digi and the USB sound card "bleed over" and block use of the ports above/below and to their sides. A vertical hub allows these devices to not interfere with one another.

RTL2832U from RTL-SDR

The RTL2832U was originally meant as a TV tuner but had the ability to receive a much wider frequency range than just broadcast television. RTL2832Us are easy to come by, but it is recommended to buy official units from RTL-SDR to avoid low quality knock-offs that underperform.

USB GPS Dongle

There are a variety of NMEA-0183 compatible GPS dongles available that plug directly into a USB port. Although the "thumbdrive form factor" UBlox units are extremely popular, there are two reasons not to use it. The first is the dongle's size may inhibit the installation of other USB plugs. The second is that if the Raspberry Pi is under a seat, for instance, it may struggle to receive the GPS signal. A UBlox GPS receiver on a cable like the VK-162 G-Mouse offers unobstructed plugs and the ability to place the antenna in a more optimal location using its magnetic mount. NOTE: Many of these GPS dongles also receive position data from the Russian GLONASS constellation of satellites as well.

Installing the GPS

Just plug it in. Once attached, verify the device is seen by the Raspberry Pi. Generally, it can be found by looking at the devices for one named like ttyACM0, looking for it's activation in the system message queue, or by listing the attached USB devices.


pi@APRS:~ $ls /dev/ttyA*
/dev/ttyACM0

pi@APRS:~ $ lsusb
Bus 001 Device 004: ID 1546:01a7 U-Blox AG [u-blox 7]

pi@APRS:~ $ dmesg
[146215.390810] usb 1-2: new full-speed USB device number 4 using ohci-pci
[146215.930468] usb 1-2: New USB device found, idVendor=1546, idProduct=01a7, bcdDevice= 1.00
[146215.930476] usb 1-2: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[146215.930480] usb 1-2: Product: u-blox 7 - GPS/GNSS Receiver
[146215.930482] usb 1-2: Manufacturer: u-blox AG - www.u-blox.com
[146215.943836] cdc_acm 1-2:1.0: ttyACM0: USB ACM device

You can watch the NMEA-0183 strings scroll past by monitoring the output, usually on /dev/ttyACM0.


pi@APRS:~ $ sudo cat /dev/ttyACM0
$GPTXT,01,01,02,u-blox ag - www.u-blox.com*50

$GPTXT,01,01,02,HW  UBX-G70xx   00070000 FF7FFFFFo*69

$GPTXT,01,01,02,ROM CORE 1.00 (59842) Jun 27 2012 17:43:52*59

$GPTXT,01,01,02,PROTVER 14.00*1E

$GPTXT,01,01,02,ANTSUPERV=AC SD PDoS SR*20

$GPTXT,01,01,02,ANTSTATUS=OK*3B

$GPTXT,01,01,02,LLC FFFFFFFF-FFFFFFFF-FFFFFFFF-FFFFFFFF-FFFFFFFD*2C

$GPRMC,014346.00,A,3919.62422,N,09454.98659,W,0.098,,300422,,,D*60

$GPVTG,,T,,M,0.098,N,0.182,K,D*2C

NOTE: Sometimes the UBlox GPS dongles get "stuck" on a position fix. A few forum posts make note of the peculiar behavior where the module has a solid 2D or 3D fix on plenty of satellites and is updating time but not the position. This can often be resolved with a reset of the module using the ubxtool from the utilities included with gpsd. After performing a reset, many UBlox tinkerers advise to enable their binary protocol and disable the NMEA output.


pi@APRS:~ $ubxtool -p RESET; sleep 5; ubxtool -e BINARY; ubxtool -d NMEA
UBX-ACK-ACK:
  ACK to Class x06 (CFG) ID x09 (CFG)

... more status messages ...

Installing GPSd

The GPS service daemon makes everything extremely easy. Instead of having to manually interpret the NMEA-0183 strings or identify changed device names, the daemon basically handles everything. Downstream software that uses gpsd can make immediate use of location and timing data.


pi@APRS:~ $ sudo apt install gpsd gpsd-tools

The chrony daemon can handle getting the Raspberry Pi's clock synchronized properly. With an Internet connection, it will grab from NTP servers. With gpsd, it can set the clock from the GPS.


pi@APRS:~ $ sudo apt install chrony

Edit the /etc/chrony/chrony.conf file and add this line to the end in order to pull time from the GPS.


refclock SHM 0 offset 0.5 delay 0.2 refid NMEA

Finally, make sure both the chrony and gpsd services automatically run.


pi@APRS:~ $ sudo systemctl enable gpsd
Synchronizing state of gpsd.service with SysV service script with /lib/systemd/systemd-sysv-install.
Executing: /lib/systemd/systemd-sysv-install enable gpsd

pi@APRS:~ $ sudo systemctl enable  chrony
Synchronizing state of chrony.service with SysV service script with /lib/systemd/systemd-sysv-install.
Executing: /lib/systemd/systemd-sysv-install enable chrony
Created symlink /etc/systemd/system/chronyd.service → /lib/systemd/system/chrony.service.

pi@APRS:~ $ systemctl is-active gpsd
active

pi@APRS:~ $ systemctl is-active chrony
active

Now confirm that the GPS is being interpreted properly using cgps utility. It may take awhile for the dongle to begin displaying location information if it does not have a recent cache of ephemeris data.


pi@APRS:~ $ cgps -s
┌───────────────────────────────────────────┐┌──────────────────Seen 19/Used  7┐
│ Time:        2022-04-30T01:49:43.000Z (18)││GNSS   PRN  Elev   Azim   SNR Use│
│ Latitude:         39.32718100 N           ││GP  5    5  33.0   56.0  33.0  Y │
│ Longitude:        94.91645280 W           ││GP 10   10  12.0  251.0  34.0  Y │
│ Alt (HAE, MSL):    731.138,    825.614 ft ││GP 13   13  45.0   69.0  32.0  Y │
│ Speed:             0.09 mph               ││GP 15   15  67.0  131.0  28.0  Y │
│ Track (true, var):     0.0,   1.7     deg ││GP 18   18  62.0  325.0  39.0  Y │
│ Climb:             0.00 ft/min            ││GP 23   23  41.0  262.0  30.0  Y │
│ Status:         3D FIX (1 secs)           ││GP 29   29  55.0  190.0  24.0  Y │
│ Long Err  (XDOP, EPX):  0.80, +/- 39.5 ft ││GP  2    2   6.0  115.0  21.0  N │
│ Lat Err   (YDOP, EPY):  1.21, +/- 59.3 ft ││GP 11   11   0.0  110.0  21.0  N │
│ Alt Err   (VDOP, EPV):  1.83, +/- 36.5 ft ││GP 16   16   9.0  307.0   0.0  N │
│ 2D Err    (HDOP, CEP):  1.45, +/- 27.8 ft ││GP 20   20   8.0   68.0   0.0  N │
│ 3D Err    (PDOP, SEP):  2.34, +/-  109 ft ││GP 24   24   3.0  150.0  25.0  N │
│ Time Err  (TDOP):       1.31              ││GP 30   30   0.0   37.0   0.0  N │
│ Geo Err   (GDOP):       2.68              ││SB133   46  32.0  227.0   0.0  N │
│ ECEF X, VX:    -423422.940 m   -0.010 m/s ││SB135   48  35.0  222.0   0.0  N │
│ ECEF Y, VY:   -4922385.560 m   -0.010 m/s ││SB138   51  43.0  199.0  32.0  N │
│ ECEF Z, VZ:    4020624.050 m    0.010 m/s ││QZ  1  193   n/a    0.0   0.0  N │
│ Speed Err (EPS):       +/-  1.0 mph       ││QZ  2  194   n/a    0.0   0.0  N │
│ Track Err (EPD):        n/a               ││QZ  3  195   n/a    0.0   0.0  N │
│ Time offset:            0.024121106 s     ││                                 │
│ Grid Square:            EM29nh08          ││                                 │
└───────────────────────────────────────────┘└─────────────────────────────────┘

Another handy trick for scripting is to look for the NMEA string $GPGGA which details the GPS fix status. Specifically, the field about the "Fix Quality" will indicate whether the location data is valid and useful which will be a value of 1 or 2.


pi@APRS:~ $ gpspipe --nmea --count 10 | grep -m 1 -i gpgga
$GPGGA,124049.00,3901.35298,N,07640.65124,W,1,04,2.09,30.2,M,-34.8,M,,*57

pi@APRS:~ $ gpspipe --nmea --count 10 | grep -m 1 -i gpgga | awk -F',' '{print $7}'
1

Installing the RTL2832U

Just plug it in. Once attached, verify the device is seen by the Raspberry Pi. Generally, it can be found by looking at the devices for one named like swradio0, looking for it's activation in the system message queue, or by listing the attached USB devices.


pi@APRS:~ $ls /dev/swradio*
/dev/swradio0

pi@APRS:~ $ lsusb
Bus 001 Device 007: ID 0bda:2838 Realtek Semiconductor Corp. RTL2838 DVB-T

pi@APRS:~ $ dmesg
[    8.148607] rtl2832_sdr rtl2832_sdr.0.auto: Registered as swradio0
[    8.148655] rtl2832_sdr rtl2832_sdr.0.auto: Realtek RTL2832 SDR attached
[    8.148677] rtl2832_sdr rtl2832_sdr.0.auto: SDR API is still slightly experimental and functionality changes may follow
[    8.172828] Registered IR keymap rc-empty
[    8.176491] rc rc0: Realtek RTL2832U reference design as /devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb1/1-1/1-1.1/1-1.1.4/rc/rc0
[    8.177460] rc rc0: lirc_dev: driver dvb_usb_rtl28xxu registered at minor = 0, raw IR receiver, no transmitter
[    8.177672] input: Realtek RTL2832U reference design as /devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb1/1-1/1-1.1/1-1.1.4/rc/rc0/input1
[    8.185244] usb 1-1.1.4: dvb_usb_v2: schedule remote query interval to 200 msecs
[    8.195739] usb 1-1.1.4: dvb_usb_v2: 'Realtek RTL2832U reference design' successfully initialized and connected
[    8.196286] usbcore: registered new interface driver dvb_usb_rtl28xxu

Installing the RTL SDR Software

The rtl_fm package makes everything extremely easy. This package includes utilities to interact with and process radio frequencies received by the RTL2832U.


pi@APRS:~ $ sudo apt install rtl_fm

Installing Direwolf

There are two fundamental ways to install Direwolf. The first is directly from the source code repository and the second is from the package repository. For brevity, the output from these commands is omitted. In summary, the source is downloaded from github, configured via cmake, built with make and installed.


pi@APRS:~ $ git clone https://github.com/wb2osz/direwolf.git/
pi@APRS:~ $ cd direwolf
pi@APRS:~ $ mkdir build && cd build
pi@APRS:~ $ cmake ..
pi@APRS:~ $ make -j4
pi@APRS:~ $ sudo make install
pi@APRS:~ $ make install-conf

For the less sadist HAM radio operators, just install it from the package repository. It may not be the most bleeding edge version, but unless you are doing something extremely fancy, you won't need bleeding edge features.


pi@APRS:~ $ sudo apt install direwolf

Configuring Direwolf

When installing Direwolf from repositories, it often does not include a baseline configuration file. The following can be pasted into /etc/direwolf.conf to get started.


#############################################################
#                                                           #
#               Configuration file for Dire Wolf            #
#                                                           #
#                   Linux version                           #
#                                                           #
#############################################################

#############################################################
#                                                           #
#               FIRST AUDIO DEVICE PROPERTIES               #
#               (Channel 0 + 1 if in stereo)                #
#                                                           #
#############################################################

ADEVICE  null null

#############################################################
#                                                           #
#               GPS Device PROPERTIES                       #
#                                                           #
#############################################################

GPSD

#############################################################
#                                                           #
#               CHANNEL 0 PROPERTIES                        #
#                                                           #
#############################################################

CHANNEL 0
MYCALL KD3BUG-10

#############################################################
#                                                           #
#               BEACONING PROPERTIES                        #
#                                                           #
#############################################################


#############################################################
#                                                           #
#               DIGIPEATER PROPERTIES                       #
#                                                           #
#############################################################


#############################################################
#                                                           #
#               INTERNET GATEWAY                            #
#                                                           #
#############################################################

IGSERVER noam.aprs2.net
IGLOGIN KD3BUG-10 5-digit code

TBEACON SENDTO=IG EVERY=15 SYMBOL=R& COMMENT="Direwolf IGATE on RTL2832U"

#############################################################
#                                                           #
#                LOGGGING PROPERTIES                        #
#                                                           #
#############################################################

LOGDIR /var/log/direwolf/

What do all of these configurations mean?


ADEVICE  null null

This section relates to the audio interface and is normally used when connected via analog speaker/mic cables to a radio. Direwolf is a program that converts digital data into AFSK audio and therefore it needs to know where to send those sounds. However, with the RTL2832U, the audio is already digitized. The ADEVICE directive informs Direwolf of the card number and device number which in this case is null.


GPSD

This GPSD directive is quite simple and directs Direwolf to obtain location information from the GPS daemon configured earlier. If Direwolf was installed from the package repository, there should be no issues. There is a chance that compiling from source may not include the necessary gpsd interfaces. If errors arise, reference this guide "Adding a USB GPS Receiver to Direwolf to create an APRS Tracker" for recompilation.


CHANNEL 0
MYCALL KD3BUG-10

Normally this section handles the audio modem and PTT triggers for Direwolf to control the radio. An RTL2832U cannot transmit so none of those directives are relevant.

The MYCALL directive is set to your callsign and SSID. The SSID typically ranges from 0-15 where the numbers had legacy meanings from the early days of APRS. It is common for handhelds to use -7, mobiles to use -9, and IGates to use -10.


BEACONING PROPERTIES

This section is for Direwolf to transmit its own location and APRS messages via a connected radio. Once again, for an RTL2832U this is irrelevant since the device is receive-only.


DIGIPEATER PROPERTIES

Unless you are a wizard with Regular Expressions and really know what you're doing, utilize the default DIGIPEAT directive. This line will match APRS paths and repeat the received packets from your station. However, since the RTL2832U cannot transmit, you can't digipeat anyway so the directives are not necessary.


IGSERVER noam.aprs2.net
IGLOGIN KD3BUG-10 5-digit code

These two directives control Direwolf's access to the Internet APRS databases allowing it to serve as an IGate. Each user needs their own five digit access code which can be obtained from http://apps.magicbug.co.uk/passcode/. The first directive instructs Direwolf which of the various APRS regional servers to interact with. The second directive authenticates Direwolf's access.


TBEACON SENDTO=IG EVERY=15 SYMBOL=R& COMMENT="Direwolf IGATE on RTL2832U"

This TBEACON is different than what would have been used in the regular BEACONING PROPERTIES entry. It includes SENDTO=IG which instructs Direwolf to send items to the IGate instead of only out the radio interface. From the prior GPSD directive, this one will also utilize the GPS daemon to obtain geographic coordinates for advertising itself. The EVERY=15 transmits the station's information to the APRS-IS network every 15 minutes. The SYMBOL=R& utilizes the icon for a receive-only IGate.


LOGDIR /var/log/direwolf/

This final directive instructs Direwolf where to save it's activity log. By default, the files will be named in accordance with a YYYY-MM-DD.log convention. At this time, it's not possible to set the name to anything else but you can direct where to save them. Make sure the directory exists and you have write permissions to that path. You may have to adjust the permissions of this location for logging.


sudo chown USERNAME:USERNAME -R /var/log/direwolf

Configuring Wireless Connection

Your IGate will be useless if the Raspberry Pi is not connected to the Internet. Fortunately, you only need to add one or two entries to the /etc/wpa_supplicant/wpa_supplicant.conf file to allow the Raspberry Pi to connect to available WiFi stations. Add lines similar to the following but tailor them to match your own mobile hotspot.


network={
        ssid="KD3BUG iPhone"
        key_mgmt=WPA-PSK
        psk="NotMyRealPasswordSilly"
}

It makes sense to add an additional set of lines for the Raspberry Pi to connect to any open WiFi. Setting the key_mgmnt=NONE directive matches non-WEP, non-WPA SSIDs. The priority=-999 directive just makes it the last one used in case a preferred access point, like your mobile hotspot, is available.


network={
        key_mgmt=NONE
        priority=-999
}

Running Direwolf

With the hardware installed and the configuration file prepared, it's time to run Direwolf. Using the -c directive instructs Direwolf which configuration file to load. This can be handy for having multiple configurations for different use cases - just transmitting, just digipeating, just IGating, etc. The -t 0 directive tells the application not to use fancy colors in output which can help to avoid the Raspberry Pi's display getting funky.

The rtl_fm application tunes the RTL2832U to the APRS frequency with the -f 144.39M directive. It's digitized output is sent to STDOUT from the trailing - and is passed directly into Direwolf as STDIN via a pipe.

For Direwolf to handle the digital stream from the RTL2832U, it needs the -r 24000 and -D 1 directives. These tell the application to sample at 24Khz and divide the first channel by 1 which will match the output from the RTL2832U. Lastly, the directives -dgitm include debugging information in Direwolf's output stream to show GPS activity, IGate updates, beacons, and observed stations. The output from Direwolf is more informative than what it logs which makes it handy to direct it to STDOUT via the trailing - which can be redirected into a file (I typically choose /var/lib/direwolf.log).


pi@APRS:~ $ sudo rtl_fm -f 144.39M - | direwolf -t 0 -c /etc/direwolf.conf -r 24000 -D 1 -dgitm -

Scripting Direwolf to Self-Heal

Of course, for a mobile application, you're unlikely to have a keyboard and monitor attached to the Raspberry Pi inside your vehicle. I put together a script to only run Direwolf if all of the requisite components were ready - e.g. a RTL2832U present, a GPS fix, network connectivity confirmed, etc. This script is run every five minutes from a cronjob to restore it in the event of a crash so that no interaction is necessary while driving.


#! /bin/bash

varFLAG=false

## check network access ##
echo "[.] TESTING network connectivity"
if ping -c 1 noam.aprs2.net | grep "bytes.from"; then
  echo "[*] network connectivity	OK"
else
  echo "[!] network connectivity	FAIL"
  varFLAG=true
fi

## validate name resolution
echo "[.] TESTING name resolution"
nslookup noam.aprs2.net
if tail -150 /var/log/direwolf.log | grep "Temporary failure in name resolution"; then
  echo "[!] name resolution		FAIL"
  varFLAG=true
else
  echo "[*] name resolution		OK"
fi

## validate RTL-SDR
echo "[.] TESTING RTL2832U"
if pgrep rtl_fm; then
  echo "[*] RTL2832U in use		OK"
elif ls /dev | grep "swradio0" || lsmod | grep -i "rtl2832_sdr"; then
  echo "[*] RTL2832U detected		OK"
else
  echo "[!] RTL2832U missing		FAIL"
  varFLAG=true
fi

## validate GPS lock
echo "[.] TESTING GPS lock"
gpspipe --nmea --count 5
varGPS=`gpspipe --nmea --count 50 | grep -i gpgga | awk -F',' '{print $7}' | egrep -m 1 "1|2"`
if [ "$varGPS" = "1" ] || [ "$varGPS" = "2" ]; then
  echo "[*] GPS lock			OK"
else
  echo "[!] GPS lock			FAIL"
  varFLAG=true
fi

## terminate errant Direwolf
if [ $varFLAG = true ]; then
  echo "[!] terminating Direwolf due to erros"
  if pgrep direwolf; then
    sudo kill -9 `pgrep direwolf`
    sudo kill -9 `pgrep rtl_fm`
  fi
fi

if ps -A | pgrep direwolf; then
  echo "[*] direwolf running		OK"
elif [ $varFLAG = false ]; then
  echo "[!] direwolf missing		FAIL"
  touch /var/log/direwolf.log
  sudo rtl_fm -f 144.39M - | direwolf -t 0 -c /etc/direwolf.conf -r 24000 -D 1 -dgitm - >> /var/log/direwolf.log &
fi
You may have to adjust the permissions of this location for logging.

sudo touch /var/log/direwolf.log
sudo chown USERNAME:USERNAME /var/log/direwolf.log

Lastly, the line to add via crontab -e to keep Direwolf running:


*/5 * * * * /home/pi/auto_aprs.sh
@reboot     ubxtool -p RESET; sleep 5; ubxtool -e BINARY; ubxtool -d NMEA

Conclusion

This guide should get you 99% to your end solution. After setting everything up, I put everything into a Pelican 1200 case to hold the equipment and batteries. Now the unit is easily transportable and can be setup anywhere. After a quick GPS fix and tethering to a mobile phone, the APRS station provides coverage on the go.

de KD3BUG



More site content that might interest you:

Rubber Ducks are in fact highly sensitive instruments of science.


Try your hand at fate and use the site's continuously updating statistical analysis of the MegaMillions and PowerBall lotteries to choose "smarter" number. Remember, you don't have to win the jackpot to win money from the lottery!


Tired of social media sites mining all your data? Try a private, auto-deleting message bulletin board.


paypal coinbase marcus