QRP is amateur radio slang for 'low transmit power'. QRP digital modes such as FT8, JT9, JT65 and WSPR are modes designed to be transmit and received across the world on low transmit powers (although not everyone uses only low power). The special design of these modes allows even weak signals to be decodable by the receiving software. Released in 2017, FT8 has shown itself to now be the most popular mode by far with JT9 and JT65 taking a backseat. WSPR is also not as active as FT8, although WSPR is more of a beacon mode rather one used for making contacts.
Apart from being used by hams to make contacts, these weak signal modes are also valuable indicators of the current HF propagation conditions. Each packet contains information on the location of the transmitter, so you can see where and how far away the packet you've received comes from. You also don't need to be a ham to set up a monitoring station. As an SWL (shortwave listener), it can be quite interesting to simply see how far away you can receive from, and how many countries in the world you can 'collect' signals from.
This tutorial is inspired by dg0opk's videos and blog post on monitoring QRP with single board computers. We'll show you how to set up a super cheap QRP monitoring station using an RTL-SDR V3 and a Raspberry Pi 3. The total cost should be about US $56 ($21 for the RTL-SDR V3, and $35 for the Pi 3).
With this setup you'll be able to continuously monitor multiple modes within the same band simultaneously (e.g. monitor 20 meter FT8, JT65+JT9 and WSPR all on one dongle at the same time). The method for creating multiple channels in Linux may also be useful for other applications. If you happen to have an upconverter or a better SDR to dedicate to monitoring such as an SDRplay or an Airspy HF+, then this can substitute for the RTL-SDR V3 as well. The parts you'll need are as follows:
- RTL-SDR V3 (or upconverter, or other HF & Linux capable SDR)
- Raspberry Pi 3 (or other SBC with similar performance)
- Internet connection
- Band filter (optional but recommended)
- HF antenna (this could be as simple as a long wire)
Examples of QRP Receivers with an RTL-SDR
One week of #FT8 monitoring with the @rtlsdrblog V3 dongle using direct sampling on 30m with a 90ft longwire. 3151 unique calls, 2836 grids and 86 countries. ADIF from #pskreporter and mapped with @GridTracker . pic.twitter.com/1c826gDoce— Johnny (@cuoops) June 15, 2018
Setting up the Raspberry Pi 3
In this tutorial we use a Raspberry Pi 3, but any SBC (single board computer) with similar or better performance could probably be substituted. Here we'll first set up the Pi for remote desktop connections via RealVNC.
- First as usual for any Pi installation, burn a fresh copy of Raspbian Stretch (or newer if reading in the future) to an SD card, insert the SD card into the Pi, plug in a keyboard and mouse, HDMI monitor and power.
- Once you get to the desktop open a terminal and type in "sudo raspi-config". Here in the localisation settings change the timezone to UTC (localisation options -> time zones -> none of the above -> UTC), set up the keyboard for your locale, and enable VNC.
- In raspi-config also enable VNC under Interfacing Options.
- Using the Raspberry Pi RealVNC setup instructions, set up a RealVNC Viewer account, or if connecting over the local network, just write down your Pi's local IP by using "ifconfig" at the terminal. Note that after enabling VNC in the previous step you can access the RealVNC server settings to login in the top right of the Raspberry Pi taskbar.
- At this point if you like you can remove the HDMI monitor and connect to the Pi over VNC.
Install the following software on your Pi 3.
First we'll install the RTL-SDR drivers. We require the Keenerd drivers for the V3, as these are the only drivers that allow us to run the rtl_sdr software in direct sampling Q-branch mode, which is required for HF reception on the RTL-SDR V3.
sudo apt-get update sudo apt-get install libusb-1.0-0-dev git cmake -y git clone https://github.com/keenerd/rtl-sdr cd rtl-sdr/ mkdir build cd build cmake ../ -DINSTALL_UDEV_RULES=ON make sudo make install sudo cp ../rtl-sdr.rules /etc/udev/rules.d/ sudo ldconfig echo 'blacklist dvb_usb_rtl28xxu' | sudo tee – append /etc/modprobe.d/blacklist-dvb_usb_rtl28xxu.conf
Now reboot to apply the blacklist, and plug in your RTL-SDR.
PulseAudio & MPlayer
We'll need PulseAudio to create virtual audio cables and we'll also install mplayer for playing the audio.
sudo apt-get install pulseaudio pavucontrol mplayer -y
CSDR is a library of DSP functions that we'll use to set up a multi-channel receiver.
sudo apt-get install libfftw3-dev -y cd ~ git clone https://github.com/simonyiszk/csdr cd csdr
Before going any further, for the Pi 3 we recommend editing the Makefile and changing the PARAMS_NEON flags to the following. The Makefile can be opened with "sudo leafpad Makefile"
-march=armv8-a -mtune=cortex-a53 -mfpu=neon-fp-armv8.
Also under PARAMS_RASPI set:
We're not sure if it actually does anything, but the idea is that this should help optimize the code for the Pi 3 CPU. For any other SBC, you'll want to check what these settings should be for your architecture.
Close and save the file, then run:
make sudo make install
ncat is a TCP server that we'll use to help us set up a multi channel receiver.
sudo apt-get install nmap -y
We'll use Chrony to adjust the time offset required by these QRP modes. Configuration will be discussed later.
sudo apt-get install chrony -y
WSJT-X is the software that we'll use to decode FT8, JT9, JT65 and/or WSPR. To install it simply go to the WSJT-X page in Chrome, download the .deb file for the Raspberry Pi 3, and then double click on the downloaded file in the folder to begin the install process.
JTDX is another decoder that is derived from WSJT-X. Some say it decodes better than WSJT-X and has more features. However, we've found that JTDX uses significantly more CPU resources, so multichannel decoding with it on the Pi 3 is difficult. At the time of this tutorial post, there is no ready make .deb installation file for JTDX on the Pi 3 so it must be compiled manually.
The following compilation instructions are based on N0KEG's tutorial which now appears to be a little outdated.
# Install the prerequisites sudo apt-get install build-essential subversion git automake libtool libusb-dev gfortran gfortran-5 g++ g++-5 libusb-1.0-0-dev texinfo cmake asciidoc asciidoctor libqt5serialport5 libqt5serialport5-dev libfftw3-dev libqt5multimedia5 libqt5multimedia5-plugins libqt5multimediawidgets5 qtmultimedia5-dev libudev-dev pavucontrol wget # Enables multi-threaded compilation export MAKEFLAGS='-j 4' # Requires a temporarily enlarged swapfile to compile a large file sudo fallocate -l 2G /swapfile && sudo chmod 600 /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile # Download, compile and install the latest hamlib cd ~ mkdir ~/hamlib-prefix && cd ~/hamlib-prefix && git clone git://git.code.sf.net/u/bsomervi/hamlib src cd src git checkout integration ./bootstrap ./configure make sudo make install sudo ldconfig # Download an install JTDX cd~ wget https://www.release.jtdx.tech/Windows/Source%20code/src220.127.116.11.zip mkdir ~/jtdx-prefix && mkdir ~/jtdx-prefix/build && mkdir ~/jtdx-prefix/src unzip src18.104.22.168.zip mv wsjtx/* ~/jtdx-prefix/src/ cd ~/jtdx-prefix/build cmake -D CMAKE_PREFIX_PATH=~/hamlib-prefix ../src cmake – build . sudo cmake – build . – target install
Download the Raspberry Pi version of GridTracker from https://tagloomis.com/downloads using Chrome. This will allow you to visualize your QRP spots on a map on the Pi itself. It's optional, as you can use the PSKreporter.info site to do the same online.
cd ~/Downloads tar -xzf GridTracker-Linux-Arm-1.18.0604.tar.gz -C ~
Audio Piping Setup
We need to initially create virtual audio sinks for each frequency that you want to simultaneously monitor. The example below will set up a two virtual audio sinks that load on boot. To set up another, simply add more lines from Virtual 2 and and so on. First open the pulseaudio default.pa file:
sudo leafpad /etc/pulse/default.pa
Add the following lines to the end of the file:
load-module module-null-sink sink_name=Virtual0 sink_properties=device.description="Virtual0" load-module module-null-sink sink_name=Virtual1 sink_properties=device.description="Virtual1"
We also recommend disabling PulseAudio logging, as this seems to be a large user of CPU cycles.
sudo leafpad /etc/pulse/daemon.conf
Now find "log-level" and change it to "log-level = error". Remove the semi-colon on the log-level line too. Save and exit.
; log-target = auto log-level = error ; log-meta = no
You can now reload pulseaudio either by rebooting, or running "pulseaudio -k" at a command line.
Now in a terminal window run the command below to set up an RTL-SDR TCP server with ncat. In this example the center frequency is set to 14.1 MHz (14100000 Hz). Change this to whatever frequency band you want to monitor. There is a full list of various QRP bands available here. Just remember to offset the center frequency by a few hundred kHz from the actual signal frequency to help avoid the center DC spike.
rtl_sdr -s 1200000 -f 14100000 -D 2 - | csdr convert_u8_f | ncat -4l 4952 -k – send-only – allow 127.0.0.1
In the rtl_sdr command -s is the sample rate, -f is the center frequency and -D 2 sets the Q-branch direct sampling mode.
On ncat -4l sets the TCP IPv4 mode, 4952 is the port, -k allows for multiple connections to be made, – send-only ensures that the server only sends data and does not receive, and – allow 127.0.0.1 ensures that only local connections can be made.
In a second terminal window/tab run the command below to generate a SSB USB channel that monitors the 20M FT8 channel at 14.074 MHz. Note that the "(14100000-14074000)" part sets the monitoring frequency as "(center frequency - tuned frequency)". In this example we're monitoring 14.074 MHz which is the 20M FT8 frequency. If you are monitoring a different band, and used a different center frequency, then change the offset frequency here. The various csdr commands set up a USB SSB decoder. Further information about the use of csdr can be found on the csdr GitHub page https://github.com/simonyiszk/csdr. Note that we reduced the 'transition bandwidth' in the fir_decimate_cc command from 0.005 which was used in the csdr example on their GitHub page, down to 0.05. This reduces CPU usage at the expensive of possibly more interference, but in our case we noticed no problems.
ncat -v 127.0.0.1 4952 | csdr shift_addition_cc `python -c "print float(14100000-14074000)/1200000"` | csdr fir_decimate_cc 25 0.05 HAMMING | csdr bandpass_fir_fft_cc 0 0.5 0.05 | csdr realpart_cf | csdr agc_ff | csdr limit_ff | csdr convert_f_s16 | mplayer -nocache -rawaudio samplesize=2:channels=1:rate=48000 -demuxer rawaudio -
Open pavucontrol either by going to the Raspberry Pi Start Menu -> Sound & Video -> PulseAudio Volume control, or by simply typing "pavucontrol" in at the command line. Click on the Playback tab, and set MPlayer to use the "Virtual 0" audio sink.
Fixing the Time Delay
QRP modes (especially FT8) require the computer's clock to be accurate. This is because signals are expected to be transmitted and received at specific time intervals. Most people use NTP to synchronize their computer clocks to an accurate time. Raspbian automatically syncs to NTP by default if there is an internet connection.
However, the problem is that RTL_SDR, CSDR, and mplayer all running together create an unacceptable time delay from input to output of about 2 - 3 seconds on a Pi 3. Most of the delay comes from the buffering on mplayer which helps prevent underruns when the CPU spikes. WSJT-X expects the packets to be received in the correct time frame, and will not decode them if there are more than about +/- 2 seconds out.
To fix this we need to cheat the clock a bit, and set our system time to always be a few seconds in the past. Thanks to dg0opk for letting us know about this method which involves replacing Raspbians default NTP software with Chrony which is much more configurable. Chrony should have already been installed before in the previous section, installing it automatically disables NTP and activates Chrony. Open it's config file with the following:
sudo leafpad /etc/chrony/chrony.conf
On the first command, edit it to include an offset of about -2.5 seconds.
pool 2.debian.pool.ntp.org iburst offset -2.5
Save and close the config file, then restart the Chrony server with the following.
sudo invoke-rc.d chrony restart
Now open WSJ-T or JTDX and in File -> Settings -> Audio tab set the Soundcard Input to be "Virtual0.monitor".
In WSJT-X set the mode to FT8, and you should now be now decoding signals as they come in.
Alternatively you could have opened JTDX instead, using "jtdx" at the terminal. The interface for setting the input sound card is identical.
If you don't get any decodes, but are seeing signals in the spectrum, try playing with your time offset in Chrony. You should be able to see if the packets are arriving too early or too late in the WSJT-X waterfall. Once you start getting decodes, adjust the offset time accordingly to try and get the "DT" column values to as close to zero as possible. The DT column lets you know the time offset from perfect timing. E.g. -0.5 indicates that the packet was received 0.5 seconds earlier than expected.
Set up WSJT-X to Report to PSKReporter
WSJT-X can report your spots to pskreporter.info/pskmap, which is a site that aggregates QRP spots from all around the world. Here you can compare your receiver with nearby ones to see how good your antenna and setup is, and to show off how many countries you've received from.
To set it up simply enter your callsign and maidenhead grid details in the General settings of WSJT-X, and then place a check in "Enable PSK Reporter Spotting" under the Reporting tab.
If you're not a ham, you can still contribute to the site as an SWL (shortwave listener). Your callsign can be "hamprefix/SWL/city". There is an example given at https://pskreporter.info. You can find your countries hamprefix here. You can get your Maidenhead grid location from this calculator. Just use the first 4 characters it gives you.
Monitoring Multiple Channels
FT8 + JT9/JT65
Here we'll show how to add simultaneous monitoring for additional QRP modes like JT65, JT9 and WSPR. First we'll show how to set up dg0opk's method that was described in his YouTube video for monitoring just FT8, JT9 and JT65. Since the JT9 and JT65 bands are only a few kHz away from FT8, we can simply open a second instance of WSJT-X, get it to listen to the same audio as the FT8 decoder, and just expand the decoding bandwidth in WSJT-X.
- To do this first open a second instance of wsjt-x by typing in the terminal "wsjtx -r jt_decode".
- In WSJT-X advanced settings, increase the receiver bandwidth to 4500 Hz.
- Set the mode to JT9+J65, and in the waterfall window change the "JT65 2500 JT9" setting to "JT65 4500 JT9".
FT8 + JT9/JT65 + WSPR
Now to monitor WSPR, we'll need to open an actual second channel because the maximum bandwidth that WSJT-X can monitor is 6000 Hz, and WSPR is 28.1696 kHz above the FT8 frequency in the 20m band.
Open a second terminal window and run the ncat command again, making sure to change the tuned frequency. In the example below we change it to the 20m WSPR frequency of 14.0956 MHz.
ncat -v 127.0.0.1 4952 | csdr shift_addition_cc `python -c "print float(14100000-14095600)/1200000"` | csdr fir_decimate_cc 25 0.05 HAMMING | csdr bandpass_fir_fft_cc 0 0.5 0.05 | csdr realpart_cf | csdr agc_ff | csdr limit_ff | csdr convert_f_s16 | mplayer -nocache -rawaudio samplesize=2:channels=1:rate=48000 -demuxer rawaudio -
Open pavucontrol either by going to the Raspberry Pi Start Menu -> Sound & Video -> PulseAudio Volume control, or by simply typing "pavucontrol" in at the command line. Here set the new player to use the "Virtual 1" audio sink.
Open a third instance of wsjt-x with "wsjtx -r wspr". Then set the input soundcard to "Virtual1.montor" and the mode to WSPR. You should be now set up.
On a Raspberry Pi 3 we've been able to successfully open and run two channels and three decoding instances of WSJT-X. If using JTDX we can really only open one or two instances due to its high CPU usage.
GridTracker is a nice piece of software that will automatically plot your spots on a map. To use it after installing it using the optional instructions further up, just run ./GridTracker from the command line.
To set up WSJT-X for GridTracker simply go into the Reporting tab of the settings window, and enable "Accept UDP Requests".
Unfortunately it seems that running GridTracker with the two channels and three decoders may be a bit too much for the Raspberry Pi 3 B CPU to handle. If you start seeing missed decodes and buffer underruns in mplayer close GridTracker.
Tips and Lessons Learned
- We initially tried using sox play and aplay as the audio player. These didn't initially add any large latency, so adjusting the clock wasn't required. However, over time the players would start to display underruns every now and then. Over time the more it underran, the more the latency increased. We're not sure why. If you're running on faster hardware that never underruns, then this may be an easier solution.
- FT8 is definitely the most popular mode and we recommend starting with that. JT9 and JT65 are almost dead.
- Band filters can really help improve reception on the V3. You can find DIY kits from SV1AFN on eBay, or from qrp-labs.com. They require a bit of modification to add a connector however.
- We've had the receiver running stable for 3 days, but the long term stability of the system hasn't been tested yet. Although dg0opk has indicated that his similar setups have been running stable for over half a year.
- You must use an HF antenna. This might simply be a long length of wire (at least about 5 meters). If you're using the multipurpse dipole kit from our RTL-SDR V3 set, then you'll need to extend the length of the arms for HF using two lengths of wire. Simply clamp on about 5 meters or more wire to each leg of the dipole.
- We used a Raspberry Pi 3B. The newer 3B+ may offer improved performance. Overclocking the Pi may also help.