❌

Normal view

There are new articles available, click to refresh the page.
Before yesterdayAI6YM

Simultaneous Multi-Band FT8 Receive

By: Justin
6 July 2024 at 22:41

The availability and performance of Software Defined Radios (SDRs) today is incredible. Equally incredible is how poorly us Hams make use of the capabilities at our disposal.

A single HackRF One can simultaneously receive a 20MHz wide slice of spectrum anywhere from 1MHz to 6GHz. That means that a single device can simultaneously receive every amateur band from 20 meters to 10 meters or from 160 meters all the way to 15 meters!

In this post I’ll show you how to receive and decode FT8 on multiple bands simultaneously. These techniques can be expanded to receive WSPR, JT9, and every other digital mode in the received bands. Your CPU is the limit.

Hardware

HackRF One or another SDR with a high sample rate and the capability to receive HF.

Any old PC or Single Board Computer (SBC) – I’m using an old laptop with a quad-core i7 running at 3.5GHz. Nearly anything with a CPU is acceptable. My recommendation for SBCs is currently the Orange Pi 5 Pro, it’s a good balance of cost vs performance but it might not get you all 5 bands like my old i7 will.

Multi-Band Antenna – I’m using a PAC-12 from QRP Labs. It is not the best choice for this application since it needs to be tuned for each individual band, but it is a great POTA antenna and I already had it hooked up. A good fan dipole or trapped EFHW would be better.

Software

Linux Mint – Most of this will work on any OS that can run the rest of the software but the audio routing bits are specific to Linux & PulseAudio.

PulseAudio – This is already installed with most modern Linux distributions, including Mint. If you’re running another audio server, or an OS without PulseAudio, you get to figure out routing audio between GNU Radio and WSJT-X.

GNU Radio – Manages the SDR, tuning, and demodulating SSB.

WSJT-X – Decodes FT8 from the audio produced by GNU Radio.

Source

All of the code and configuration I used is on GitHub. Please feel free to adapt or build on this work (see the LICENSE file for details).

References & Gratitude

Many thanks to Mark Smith, N6MTS – Mark’s March 2021 QSO Today presentation provided the guiding light for this work. I’m especially grateful that he included links to source material.

Additional inspiration was taken from Corey Koval, K3CPK – Corey’s Automate FT8 GitHub project showed me the way through some interesting bugs.

Thanks to Tushar Jain for uploading an SSB signal capture to their EE304P GitHub repo. All other sources for that file seem to have been scrubbed from the internet.

Problem Outline

The process of extracting useful data from a 20MHz wide chunk of raw RF is a bit daunting but it can be broken down into small steps.

  1. Tune to a Target Signal – 20MHz is too much data to process, the target signals are ~3kHz.
  2. Demodulate SSB – FT8 (and most digital modes) are decoded from audio which has been demodulated from a Single Side Band (SSB) signal.
  3. Decode FT8 – Our demodulated audio stream has to make it to something that can decode FT8.
  4. Multiband Operation – We need multiple parallel instances of the decoding software playing nicely together. Multiple audio streams need to be routed correctly, labelled, and ideally we should have the ability to listen in to each band.

Tuning to a Target Signal

I chose to split tuning into two steps. First, tuning to a specific Amateur band (or at least, a good part of it) and second, tuning in to the specific frequency used for FT8 on that band. The workload to filter and decimate the 20MHz sample of raw RF is very high so splitting this work up allows decoding other digital modes in the future without adding a lot of load to the CPU.

Band Tuning

The Frequency Xlating FIR Filter block.

Allow me to introduce the Frequency Xlating FIR Filter, this is a stock block provided by GNU Radio that provides frequency shifting, filtering, and (optionally) decimation in one step. It’s a bit more CPU efficient than executing each of those steps separately.-

My goal here is to reduce the massive 20MHz sample rate to something more manageable for each amateur band, center the target band in the output, and filter out neighboring signals. I chose to take every band to a 400kHz slice. That slice is large enough to cover the entire CW, digital, and beacon ranges of each HF band. Using the same slice for each band also makes the math easier downstream.

This hier block calculates and executes the filter necessary from a few inputs. The most important bits are the input center frequency, the output center frequency, and the output bandwidth.

This filter was a frequent source of bugs; signals being shifted incorrectly, aliasing, massive energy loss, and more. Configuring the taps incorrectly was usually the problem (the documentation explains essentially NOTHING about that function) but the C++ reference for the firdes class cleared things up.

For a bandpass filter, which is what we need for isolating an Amateur band, firdes.complex_band_pass takes the gain, input sample rate, the low cutoff relative to the center frequency, the high cutoff relative to the center frequency, and the transition bandwidth. Let’s go over each of those.

  • gain is the 1. No need to amplify or reduce the target signals.
  • input sample rate is just the sample rate coming into the block.
  • low cutoff is negative, half of the bandwidth in Hz desired from the bandpass filter.
  • high cutoff is positive, also half the bandwidth desired.
  • transition bandwidth is up to you, smaller values make for greater rejection of signals adjacent to your bandpass but that comes at a high CPU cost. My block defaults to 2kHz but my CPU struggled to keep up, even on a single band. More on that later.

The result is exactly as described, the large chunk of raw RF is taken down to a manageable slice covering the target band.

SSB Tuning

With an amateur band isolated into a manageable chunk it’s time to select a single SSB signal. This is just another Frequency Xlating FIR Filter but on a much more manageable quantity of data. The logic is wrapped up in another hier block for easy reuse.

The parameters here allow selecting a specific SSB signal; the interesting bits are the USB / LSB switch and the tuning frequency.

Hams think of frequency in terms of the dial on their radio but an SSB signal is actually above (Upper Side Band) or below (Lower Side Band) the tuning frequency. Our demodulation logic is going to expect the signal to be centered.

The center frequency here is calculated based on the tuning frequency and whether the target signal is USB or LSB. The bandpass filter limits the signal to the desired audio bandwidth. The transition bandwidth of the filter is a compromise at 800Hz, if I was using this for voice signals I’d probably want a tighter filter.

The waterfall below shows an SSB signal on an FT8 frequency after tuning. The content is centered, adjacent signals are strongly attenuated but there is still some out of band leakage.

SSB Demodulation

If you’re expecting a detailed explanation of SSB demodulation mathematics you’re about to be disappointed.

This hier block implements the weaver method for SSB demodulation. I went on a deep dive to understand how it works; I ended up with a headache and a renewed appreciation for people that study advanced mathematics. It’s an interesting subject if you enjoy studying such things.

There’s not much to say about this block. It takes in a tuned, filtered RF signal and it outputs an audio signal at the same sample rate. I chose to sample at 8kHz to allow a bit of head room over the roughly 3kHz audio being demodulated. Remember audio bandwidth is half of the input RF sample rate.

The waterfall below shows the audio content extracted from the FT8 signal shown previously.

Testing

I had to test this demodulation with a known signal, something I could listen to and hear any off-center tuning or inversion (making the wrong LSB / USB choice). Digital signals and the weak audio I could find on the band aren’t great for that. The official GNU Radio documentation provides a captured signal for testing but the link is broken. I eventually found the file and it’s included in my GitHub repo.

The test file is centered at 50.247MHz (that took a while to figure out) and sampled at 256kHz. I built this simple flow to resample it for compatibility with my blocks, tune and demodulate the 50.3MHz LSB signal, and route the audio for playback.

I’m going to make this look easy by showing you the end result and not the hours of debugging my own idiocy. Getting these waterfalls was frustrating and it required a lot of time. If you’re struggling with an SDR problem remember that we take a lot of what our radios do for granted, you’re looking at my result NOT my process.

FT8 Workflow & Decoding

Let’s put these pieces together for a real signal. It’s not much different from the demodulation test workflow. Receiving from the HackRF is one simple block, all I’ve done is make the amplifiers and gain variables accessible from the QT GUI.

That’s RF reception, band tuning, ssb tuning & demodulation, and audio playback. That’s a radio, maybe not a great one, but it’s a radio. If this was a rig sitting in the shack we would connect the audio input and output to our PC, open up WSJT-X, and we’d have FT8 monitoring.

So let’s do just that! PulseAudio has a concept of null sinks and monitors. A null sink is just a virtual audio device any application can play out audio to. A monitor is virtual audio device any application can record audio from, one is automatically created for every real or virtual sink.

$ pactl load-module module-null-sink sink_name=vsink0

One command is all it takes to create the audio devices and routing we need. Now just set GNU Radio and WSJT-X to use the null sink and its monitor.

Start the workflow on your band of choice (I tuned in 20m, because there was a lot of activity) and you’ve got signals.

It would be nice to be able to listen in for debugging and tuning purposes. For that we need a loopback.

$ pactl load-module module-loopback source='vsink0.monitor'

Multiband FT8 Workflow

One band down, how much harder could five be?

All the concepts here are the same. We’re receiving a wider sample of RF (20MHz), and tuning into 5 separate bands, routing each band to an SSB Tune & Demod block, and then resampling the audio for consumption by WSJT-X.

Audio Routing

Each band has its own audio sink, monitor, and loopback. They are all named and I’ve added descriptions which are visible using GUI tools. I added one additional β€œnull” sink which is the default output for the loopbacks. That means that by default we aren’t hearing the audio from every band at once but we can listen in using tools like PulseAudio Volume Control (pavucontrol).

By putting this configuration in the PulseAudio config directory (/etc/pulse/default.pa.d) the configuration will persist through reboots and service restarts. My full config file is in the GitHub repo.

WSJT-X

WSJT-X expects to be a singleton, i.e. it will only allow one instance of itself to be running at a time. For most users that’s a good thing but for us it’s a problem.

We can create additional β€œRigs”, separate config files for WSJT-X, and spawn multiple instances from the command line.

$ wsjtx --rig-name=20mFT8

Each one needs to be individually configured to use the audio device intended for the band it is monitoring. Once configured they can all be started in parallel. My, mostly correct, configuration files are in the GitHub repo. These are stored in ~/.config on Linux systems.

Only 17m and 20m show any decodes because my antenna was tuned for 20m for this screenshot. One day I’ll build a better multi-band antenna.

Obviously this is not a low intensity task for the PC. I’m running this on a 7 year old laptop with a quad-core i7 processor and it’s barely hanging on with all the waterfalls disabled. I managed to squeeze in room for one WSJT-X waterfall at a time.

Wrap Up

That’s it for now. Five parallel FT8 monitors from a single SDR, running on a single machine. There’s some room for improvement and I still need to get transmitting functional.

Before I sign off I owe one last bit of gratitude to the ancient CPU in this old laptop.

73, AI6YM

❌
❌