Setting Up a Linux Horus Binary Receiving Station

The SF-HAB group is talking about doing a High-Altitude Balloon launch in the next few weeks, and asked if I had any payloads to fly. I thought it would be fun to refly all these radiosondes that I have collected.

Looking around a projects online for reprogramming radiosondes, I came across the the great work that Mark Jessop VK5QI was doing down in Australia. Mark and David Rowe created the Horus Binary protocol, which is a low-power 100 baud 4FSK modulation scheme specifically designed for high-altitude balloons. Mikael Nousiainen OH3BHX wrote the RS41ng project which implements the Horus Binary transmitter on a regular Vaisala RS41 radiosonde.

The next blog post will focus on reprogramming the RS41 radiosonde.

Hardware Setup

The hardware receiving the Horus is pretty much the same as receiving a radiosonde or AIS, except it's a different frequency so a different antenna must be used. While I list a Raspberry Pi 4 on the block diagram (running Debian 12 Bookworm), any Debian-based distribution can be used.

Horus hardware block diagram

The Mt. Carmel High School Amateur Radio Club (W6SUN) down in San Diego has launched many reprogrammed radiosondes. They already coordinated 431.05 MHz as the Horus Binary frequency in the Western United States with NARCC, so I'm just going to use that frequency.

Always attenuate the receiver when transmitting nearby. This is to protect the receiver, as any RTL-SDR dongle with the Rafael R820T chip has a maximum input of +10 dBm. I typically use 90 dB of attenuation, as free-space path loss at 10 km at 430 MHz is about 105 dB.

Adding receive attenuation for close-in reception

RTL-SDR: Disabling the Linux drivers

Before plugging an RTL-SDR dongle into a computer, it's always best to install the drivers, either from the Debian repository or locally built. Blacklisting the dvb_usb_rtl28xxu module is important, otherwise Linux will assume you're trying to watch TV with the RTL-SDR dongle.

For installing the drivers from the Debian repository:

sudo apt install rtl-sdr
echo "blacklist dvb_usb_rtl28xxu" | sudo tee /etc/modprobe.d/blacklist-rtlsdr.conf
sudo modprobe -r dvb_usb_rtl28xxu

For more up-to-date drivers that include Bias-Tee powering, build the rtl-sdr-blog drivers or the osmocom drivers. Make sure to remove any other RTL-SDR drivers on your system before building!

Once you have the drivers installed and kernel modules disabled and removed, run rtl_test and make sure the dongle is found and it's all working. Ctrl-C to stop.

$ rtl_test

Found 1 device(s):
  0:  Realtek, RTL2838UHIDIR, SN: 00000002

Using device 0: Generic RTL2832U OEM
Found Rafael Micro R820T tuner
Supported gain values (29): 0.0 0.9 1.4 2.7 3.7 7.7 8.7 12.5 14.4 15.7 16.6 19.7 20.7 22.9 25.4 28.0 29.7 32.8 33.8 36.4 37.2 38.6 40.2 42.1 43.4 43.9 44.5 48.0 49.6
[R82XX] PLL not locked!
Sampling at 2048000 S/s.

Info: This tool will continuously read from the device, and report if
samples get lost. If you observe no further output, everything is fine.

Reading samples in async mode...

Once you got the dongle working, you can proceed with the horusdemodlib installation.

horusdemodlib: Headless Docker Container (Recommended)

By far the easiest way to get started with receiving the Horus Binary modulation is to just download the pre-built Docker container from github and run that. This container is multi-architecture, built for linux/386, linux/arm64, linux/arm/v6, linux/arm/v7, and linux/arm64. Your docker client will download the appropriate architecture.

These directions are mostly stolen from here.

To install docker, I recommend using the convenience script. It figures out which OS you have, adds the correct package repositories, and installs everything.

curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh

You'll also need to add your user to the docker group with sudo usermod -aG docker $(whoami), then reboot. Make sure everything is working by running docker ps as your regular user (not sudo).

After docker is installed and running, download the docker compose file and user configuration files:

mkdir -p horusdocker
cd horusdocker
wget -O user.cfg https://raw.githubusercontent.com/projecthorus/horusdemodlib/master/user.cfg.example
wget -O user.env https://raw.githubusercontent.com/projecthorus/horusdemodlib/master/user.env.example
touch telemetry.log

Update the user.cfg file with your callsign, lat/lon, and any station comments you want.

Update the user.env file with the receive frequency. You can also change the RTL-SDR setting here, but the defaults should be fine.

Each decoded packet is saved inside the container to a file called telemetry.log. To see this file outside of the container, touch telemetry.log is required for the docker bind mount. Otherwise, docker will create a "telemetry.log" folder on the host and the container will fail to start.

Just to test things out: docker run -it --rm --device=/dev/bus/usb --env-file ~/horusdocker/user.env -v ~/horusdocker/user.cfg:/user.cfg:ro -v ~/horusdocker/telemetry.log:/telemetry.log --tmpfs /tmp ghcr.io/projecthorus/horusdemodlib:latest docker_single.sh

This will start the container in interactive mode, showing you what it's doing. Double check that it has access to the RTL-SDR dongle, the center frequency and callsign are correct, etc. Kill the container with Ctrl-C.

For a more permanent setup: docker run -d --name horusdemod --restart=unless-stopped --device=/dev/bus/usb --log-driver=local --env-file ~/horusdocker/user.env -v ~/horusdocker/user.cfg:/user.cfg:ro -v ~/horusdocker/telemetry.log:/telemetry.log --tmpfs /tmp ghcr.io/projecthorus/horusdemodlib:latest docker_single.sh

This will start the container in daemon mode.

  • To view logs docker logs -f horusdemod
  • To stop the container docker stop horusdemod
  • To start the container docker start horusdemod
  • To jump into the running container docker exec -it horusdemod bash

Note: The container environment variables (in user.env) are only read when you create the container the first time. So if you want to change the frequency or RTL-SDR settings, you'll need to delete the container with docker rm horusdemod, edit the user.env file, and recreate the container with long docker run -d command above.

There is also a dockerfile for building your own container, and be sure to read the docker guide. The dockerfile-generated container is also confusingly called ghcr.io/projecthorus/horusdemodlib.

Headless Native Setup (Alternate)

If you want to run this on bare metal, installation is pretty painless following the directions on the Github page. Since this is a headless version (no graphics), it's pretty much the same as the docker version.

After you've gotten the RTL-SDR all setup:

$ sudo apt-get install cmake build-essential libusb-1.0-0-dev git python3-venv python3-crcmod python3-requests python3-pip sox bc libatlas-base-dev libopenblas-dev
$ git clone https://github.com/projecthorus/horusdemodlib.git
$ cd horusdemodlib
$ mkdir build
$ cd build
$ cmake ..
$ make
$ sudo make install
$ ctest       (which should result in output ending in '100% tests passed')
$ cd ../
$ python3 -m venv venv
$ . venv/bin/activate
$ pip install -r requirements.txt
$ pip install horusdemodlib

You'll want to edit the user.cfg file with your callsign and lat/long. You'll also want to edit the start_rtlsdr.sh script with with center frequency and RTL-SDR settings, although the default ones work pretty well. This is the script that actually starts the receiver.

Use screen or tmux to keep hourusdemodlib running in the background after you log out or if your network connection dies.

pi@horus-demod:~/horusdemodlib $ screen
pi@horus-demod:~/horusdemodlib $ ./start_rtlsdr.sh 
Found horus_demod.
Found bc.
Entering venv.
Using SDR Centre Frequency: 432494000 Hz.
Using FSK estimation range: 1000 - 11000 Hz
Using AGC.
Setting estimator limits to 1000 to 11000 Hz.
Found 1 device(s):
  0:  Realtek, RTL2838UHIDIR, SN: 00000002

Using device 0: Generic RTL2832U OEM
Found Rafael Micro R820T tuner
Tuner gain set to automatic.
Tuned to 432878000 Hz.
Oversampling input by: 32x.
Oversampling output by: 1x.
Buffer size: 5.33ms
Allocating 15 zero-copy buffers
Sampling at 1536000 S/s.
Output at 48000 Hz.
2024-12-09 05:03:41,176 INFO: Opened log file telemetry.log.
2024-12-09 05:03:41,177 INFO: Attempting to download latest payload ID list from GitHub...
2024-12-09 05:03:41,389 INFO: Attempting to download latest custom field list from GitHub...
2024-12-09 05:03:41,582 INFO: Payload list contains 596 entries.
2024-12-09 05:03:41,582 INFO: Custom Field list contains 20 entries.
2024-12-09 05:03:41,585 INFO: Sondehub Amateur Uploader - Started Sondehub Amateur Uploader Thread.
2024-12-09 05:03:41,585 INFO: Using User Callsign: KF6ZEO-H2
2024-12-09 05:03:41,587 INFO: Started Horus Demod Uploader. Hit CTRL-C to exit.
2024-12-09 05:03:42,204 INFO: Sondehub Amateur Uploader - Uploaded station information to Sondehub.
2024-12-09 05:03:44,374 INFO: Received raw binary packet: C3020C000503295CFC164260DBF4C23F00000519A41700000000000000008226
2024-12-09 05:03:44,387 INFO: Decoded Binary Packet (SNR 19.3 dB): $$KF6ZEO,12,05:03:41,37.74644,-122.42847,63,0,5,25,3.22,0.23,0.0,0,0.0*C7F6

Use Ctrl-A then D to detach the screen session and keep it running in the background. Run screen -r to resume the session.

Graphical Horus-GUI

If you want a nice graphical way to look at the spectrum, use Horus-GUI. The installation directions on the Horus GUI wiki page are pretty good (and will probably change), so I won't repost them here. It requires horusdemodlib to be installed on the system (see above).

Installing Horus-GUI on Ubuntu 22.04 requires sudo apt install portaudio19-dev and then pip install pyaudio in the venv after exporting CFLAGS and LDFLAGS. Within the venv run python3 -m horus-gui.py.

Horus-GUI screenshot

Horus-GUI requires an audio stream as input, either from an audio soundcard (from a SSB receiver), or a UDP audio stream from a SDR source.

Getting Audio into Horus GUI: Installing SDR++

Since I don't want to hook up a 430 MHz hardware SSB receiver to my laptop via an audio cable, I decided to use SDR++ as the receiver. It can output audio on a UDP stream directly to Horus-GUI, but be aware that they must be on the same machine.

These directions are pretty detailed. Installing SDR++ is trivial, Ubuntu .deb packages are available on github. Just install the .deb and you're good to go.

SDR++ configuration:

  • Set the Source to RTL-SDR
  • Set the Radio to USB
  • Set the Sink to Network and click Start to send audio to Horus-GUI

SDR++ receiving Horus Binary signal

Horus-GUI receiving Horus Binary signal

See the next blog post for reprogramming a RS41 radiosonde to transmit with this Horus Binary v2 modulation.

links