Could a serious WSPR station need only one small program and one memorable command?

I built this especially for my friend Brad, K1TE. We both believe in practical P2P, point-to-point radio contacts, and we are working toward making them reliable across the breadth of planet Earth.

airspyhf-wsprd is a dedicated receiver for the Airspy HF+ Discovery. It tunes, aligns to the WSPR boundary, decodes, reports to WSPRnet, and repeats:

./airspyhf-wsprd -b 20m -c VU3CER -g MK68xm

No dial-frequency memorization, GUI, virtual audio cable, or Fortran runtime.

Loopback Demo

Install

On Debian, Ubuntu, or Raspberry Pi OS:

curl -fsSL https://raw.githubusercontent.com/kholia/airspyhf-wsprd/master/install.sh | sudo bash

The installer confirms the callsign and grid, builds and tests the software, enables automatic WSPRnet reporting, and installs a self-restarting systemd service. If libairspyhf-dev is unavailable, install the official airspyhf package from source.

For antenna and broader station notes, see my headless WSPR monitoring system and PA0FRI active antenna articles.

Why it might be the best

  • The decoder tracks the upstream wsprd core.
  • Strict -O3 builds retain safe floating-point behavior.
  • The 192 ksample/s default becomes the same filtered 375 sample/s decoder input as higher rates, with one quarter of the USB and host load of 768k.
  • A 32-block RAM queue and dedicated DSP worker keep processing away from the Airspy callback.
  • WSPRnet uploads are asynchronous and retry from a bounded RAM queue.
  • Clock health, callsigns, grids, HTTP replies, and decoder deadlines are checked.
  • The systemd service keeps decoder state and bounded logs in RAM, avoiding microSD writes during reception.

A Raspberry Pi 4 or newer is recommended. The load is small, so no powered USB hub is needed.

Tested with real RF

The bundled recording must produce all nine expected spots:

make test-wav

A second test uses an ESP32-S3-Zero and Si5351 with a 25 MHz TCXO. The generator stays silent until the host sends *tx* at an even UTC boundary:

make esp32-flash PORT=/dev/cu.usbmodemXXXX
make test-hw PORT=/dev/cu.usbmodemXXXX

The complete ESP32 to Si5351 to Airspy to decoder path worked:

Spot : 40.00 -2.00  14.097088  0  VU3CER MK68 10
Spot : 23.00 -1.00  14.097088  0  VU3CER MK68 10
user@rpi:~ $ sudo journalctl -u airspyhf-wsprd -b -o cat
Starting airspyhf-wsprd.service - Airspy HF+ WSPR receiver and reporter...
HF+ sample rates: 768000 384000 256000 192000 Hz
Starting airspyhf-wsprd (2026-08-22, 08:19z) - Version 0.5
  Callsign     : VU3CER
  Locator      : MK68xm
  Band         : 20m
  Dial freq.   : 14095600 Hz
  IQ center    : 14097100 Hz
  Rate         : 192000 Hz
  Decimation   : 512
  HF AGC       : yes
  AGC threshold: high
  Attenuation  : 0 dB (ignored while AGC is enabled)
  Preamp       : no
  WSPRnet      : upload enabled
  S/N          : <something>
Wait for time sync (start in 56 sec)
Clock is not synchronized; WSPR capture is paused
Started airspyhf-wsprd.service - Airspy HF+ WSPR receiver and reporter.
Clock synchronized; WSPR capture resumed
No spot [2026-08-22 08:24 UTC]
No spot [2026-08-22 08:26 UTC]
No spot [2026-08-22 08:28 UTC]
Spot : -25.00 0.20  14.097021  0  VU2ITI MK80 23
Spot : -24.00 0.20  14.097021  0  <VU2ITI> MK80EB 23
No spot [2026-08-22 08:34 UTC]
No spot [2026-08-22 08:36 UTC]
No spot [2026-08-22 08:38 UTC]

On-air WSPR demo

% curl http://rpi.local:8080/health
{
  "healthy": true,
  "receiver_state": "capturing",
  "mode": "FT8",
  "clock_synchronized": true,
  "decoder_busy": true,
  "callsign": "VU3CER",
  "grid": "MK68",
  "band": "20m",
  "dial_frequency_hz": 14074000,
  "band_hopping": true,
  "hop_interval_seconds": 120,
  "adaptive_hopping": true,
  "selected_bands": ["20m", "15m", "40m", "17m"],
  "exploration_slot": false,
  "reporting_enabled": true,
  "reporting_network": "PSK Reporter",
  "report_queue_depth": 28,
  "frames_decoded": 4754,
  "spots_decoded": 11775,
  "decoder_errors": 0,
  "last_decodes_ft8lib": 5,
  "last_decodes_jt9": 6,
  "total_decodes_ft8lib": 6794,
  "total_decodes_jt9": 11319,
  "frames_decoded_ft8lib": 4754,
  "frames_decoded_jt9": 4754,
  "average_decodes_ft8lib": 1.43,
  "average_decodes_jt9": 2.38,
  "airspy_overrun_samples": 0,
  "dsp_queue_overrun_samples": 0,
  "started_unix": 1787416848,
  "uptime_seconds": 71342,
  "last_decode_unix": 1787488176,
  "last_spot_unix": 1787488176,
  "version": "0.9"
}

My results with almost a wet noodle antenna:

On-air FT8 demo

In contrast, here is what the best parallel RX station in India looks like:

VU2CPL RX results

My station receives 116 countries and VU2CPL's station receives 141 countries in 24 hours or so!

For an Airspy HF+ owner wanting a reliable headless WSPR (plus FT8) receiver, this may be the easiest and best option yet.