Skip to content

1.2 Raspberry Pi 'Headless' RX Guide

Mark Jessop edited this page Apr 11, 2026 · 33 revisions

Last updated: 2026-03-12 (Updated for horusdemodlib 0.6.x - Horus Binary v3 support)

This guide is intended to assist setting up a demodulator for a Horus Binary signal on a single-board computer like a Raspberry Pi, that may or may-not have a screen attached (and hence is 'headless'). Reception is performed using a RTLSDR - I recommend the RTLSDR v3 dongles.

I use this headless approach as the primary telemetry reception system in my HAB chase car.

1. Hardware Requirements

  • Ideally, a standalone single-board computer (Raspberry Pi 3, ODroid, etc...) to run the software. Other Linux machines will also work. We now require Python version of 3.9 or newer. Debian/Raspbian versions Bullseye or newer have at least this python version available. Debian Buster users will need to update.

  • A RTLSDR receiver.

    • A receiver with a Temperature Compensated Crystal Oscillator (TCXO) is not 100% critical as the demodulator can track a bit of drift, but it does help. The warm-up drift of the non-TCXO RTLSDRs can be significant enough that the wanted signal doesn't stay within the receiver passband. I've had great success with the RTL-SDR.com 'v3' dongles. The NooElec SMArt dongles (with the TCXO option) are also good. You can determine the ppm offset via a number of methods, such as kalibrate-rtl (if you have GSM signals nearby) or using LTE-Cell-Scanner, if you only have LTE around (i.e. Australia).
  • An antenna suitable for receiving on 434 MHz (a basic 1/4 wave monopole is usually good enough, or any other amateur 70cm antenna.

  • For optimal receive performance, a preamplifier and a band-pass filter, like this one. You can still achieve pretty good results without one.

2. Setup & Configuration

This section is intended to assist with setup under a fresh Raspbian installation. The instructions should be usable on other Debian-based systems.

For a good guide on setting up a 'headless' (no display) Raspberry Pi 2/3/Zero/ZeroW, look here.

2.1. Software Dependencies

We may require a few dependencies to be able to compile and use the new modem and uploader scripts. Under Ubuntu/Debian/Raspbian, you can install the required packages using:

sudo apt-get install cmake build-essential libusb-1.0-0-dev git python3-venv python3-pip python3-dev python3-cffi libffi-dev sox bc rtl-sdr

Under OSX, Macports or Homebrew should be able to provide the above packages, though they may be named slightly differently.

2.2 RTL-SDR udev rules

Installing the RTL-SDR utils via the system package manager doesn't seem to install the required udev rules which enable the pi user to access the RTLSDR. To install these, run:

sudo wget -O /etc/udev/rules.d/20-rtlsdr.rules https://raw.githubusercontent.com/osmocom/rtl-sdr/master/rtl-sdr.rules

Then, reboot the RPi.

2.2.1 RTL-SDR from source (usually not required)

There may be some rare cases where you need to compile rtl-sdr from source. We typically don't recommend this as the package manager versions of rtl-sdr work pretty well now. However, if you have a real need to do so, then the instructions are as follows:

git clone https://github.com/osmocom/rtl-sdr.git
cd rtl-sdr
mkdir build
cd build
cmake -DINSTALL_UDEV_RULES=ON -DDETACH_KERNEL_DRIVER=ON ../
sudo make install
sudo ldconfig

IMPORTANT NOTE: Make sure to remove any installations of rtl-sdr already on your system before compiling/installing from source. (i.e. sudo apt-get remove rtl-sdr librtlsdr0 librtlsdr-dev

2.2.2 RTL-DVB Kernel Module Blacklisting (also usually not required)

More recent versions of rtl_sdr will automatically detach kernel modules that turn the rtl-sdr into a TV tuner (wow, you can watch TV with these things?!). You should only need to do the following if you run into problems with the next step.

If you installed rtl-sdr from source as above, then a blacklist file should have already been installed, otherwise create a new module blacklist file using:

sudo nano /etc/modprobe.d/rtlsdr-blacklist.conf

and add the lines:

blacklist dvb_usb_rtl28xxu
blacklist rtl2832
blacklist rtl2830
blacklist dvb_usb_rtl2832u
blacklist dvb_usb_v2
blacklist dvb_core

Save the file (Ctrl+x, y), and reboot the Raspberry Pi.

2.2.3 - Check your RTLSDRs are working!

At this point it is worth checking that you can communicate with your RTLSDR, which can be achieved by running:

rtl_test

Which will output:

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...

Hit Ctrl+C to kill rtl_test. If you see lots of warnings such as lost at least <X> bytes, this indicates USB bandwidth issues, or some other issue with the RTLSDR. One or two lines are usually OK...

2.3.1. If updating an older (<0.5.0) horusdemodlib installation

If you are attempting to update a version of horusdemodlib older than 0.5.x, then you need to remove the old installation first:

cd ~/
rm -rf ~/horusdemodlib/
sudo rm /usr/local/bin/horus_demod

You will probably also need to install some additional dependencies. We suggest re-running the apt-get install command from further above.

If you're running a version of Debian/Raspbian older than bullseye, then it's time to update! We require Python 3.9 or newer, so that means Debian bullseye or newer. Horusdemodlib works fine on the latest Debian/Raspbian version (as of writing, Trixie).

2.3.2. Downloading this Repository

You can now grab this repository using git:

git clone https://github.com/projecthorus/horusdemodlib.git
cd horusdemodlib

Note that we won't actually be building horusdemodlib from source, however we do need some startup scripts from within the repository, and it's easier to just clone the entire thing!

2.4. Installing the horusdemodlib Python library

From within the horusdemodlib directory, create a python virtual environment running:

python3 -m venv venv
. venv/bin/activate

Then, install the horusdemodlib package (and it's dependencies):

pip install horusdemodlib

This should download and install the latest version of horusdemodlib (0.6.x) into your virtual environment

3. Configuration File Changes

Copy the example configuration file user.cfg.example to user.cfg, i.e.:

cp user.cfg.example user.cfg

The file user.cfg should then be modified to reflect the callsign you wish to use when uploading data to Habitat. Simply change the following section as appropriate:

[user]
# Your callsign -  used when uploading to the HabHub Tracker.
callsign = YOUR_CALL_HERE

# Your station latitude/longitude, which will show up on tracker.habhub.org.
# These values must be in Decimal Degree format.
# Leave the lat/lon at 0.0 if you do not wish your station plotted on the map,
# or if you are uploading your position via other means (i.e. using chasemapper)
station_lat = 0.0
station_lon = 0.0
# Radio/Antenna descriptions.
# An optional short description of your radio/antenna setup.
radio_comment = Your Radio Description Here
antenna_comment = Your Antenna Description Here

4. Startup Script Modification

There are a few receiver startup scripts available:

You will likely need to edit these scripts to change settings such as receive frequency, PPM offset, or Chasemapper output settings. The scripts are fairly well commented, and should be reasonably self-explanatory.

Once modified, you can start reception by running:

./start_rtlsdr.sh

This will produce output similar to the following:

Found horus_demod.
Found bc.
Entering venv.
Using SDR Centre Frequency: 434654000 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: 00000001

Using device 0: Generic RTL2832U OEM
Detached kernel driver
Found Rafael Micro R820T tuner
Tuner gain set to automatic.
Tuned to 435038000 Hz.
oversampling input by: 32x.
Oversampling output by: 1x.
Buffer size: 5.33ms
Create UDP thread
Created UDP thread
Main socket started! :-) Tuning enabled on UDP/6020
Allocating 15 zero-copy buffers
Sampling at 1536000 S/s.
Output at 48000 Hz.
2020-07-18 09:13:57,386 INFO: Attempting to download latest payload ID list from GitHub...
2020-07-18 09:13:57,910 INFO: Payload list contains 23 entries.
2020-07-18 09:13:57,910 INFO: Attempting to download latest custom field list from GitHub...
2020-07-18 09:13:58,495 INFO: Custom Field list contains 2 entries.
2020-07-18 09:13:58,496 INFO: Started Habitat Uploader Thread.
2020-07-18 09:13:58,496 INFO: Using User Callsign: YOUR_CALL_HERE
2020-07-18 09:13:58,497 INFO: Started Horus Demod Uploader. Hit CTRL-C to exit.
2020-07-18 09:13:58,997 WARNING: Listener position set to 0.0/0.0 - not uploading.
2020-07-18 09:14:01,672 INFO: Received raw binary packet: 010200173B300000000000000000000000001A971553
2020-07-18 09:14:01,695 ERROR: Horus UDP - Zero Latitude/Longitude, not sending.
2020-07-18 09:14:01,696 INFO: Decoded Binary Packet (SNR 10.7 dB): $$HORUSBINARY,2,23:59:48,0.00000,0.00000,0,0,0,26,2.96*3841
2020-07-18 09:14:03,431 INFO: Habitat - Uploaded sentence: $$HORUSBINARY,2,23:59:48,0.00000,0.00000,0,0,0,26,2.96*3841

Note that output from rtl_fm and the horus binary uploader are interleaved, which can be a bit confusing at first.

5. Startup on Boot.

Systemd-based startup

We have provided two systemd .service files, which allow starting up horusdemodlib as a systemd service.

  • horus_rx.service - Starts start_rtlsdr.sh
  • horus_rx_dual.service - Starts start_dual_4fsk.sh

For other startup scripts these services files can be modified fairly easily, replacing the script name within them to the one you want.

Copy the relevant file into your /etc/systemd/system/ directory using the command:

sudo cp horus_rx.service /etc/systemd/system/

If you are running as some other user than pi, then you will need to edit this file (e.g. sudo nano /etc/systemd/system/horus_rx.service) and change all instances of pi in the file to your own username.

You can then enable and start the service by running:

sudo systemctl enable horus_rx.service
sudo systemctl start horus_rx.service

(replacing horus_rx with horus_rx_dual if necessary)

You can check on the status of the service by running (using horus_rx_dual as an example here):

sudo systemctl status horus_rx_dual
● horus_rx_dual.service - horus_rx_dual
     Loaded: loaded (/etc/systemd/system/horus_rx_dual.service; enabled; preset: enabled)
     Active: active (running) since Sat 2024-12-28 05:16:57 UTC; 12min ago
   Main PID: 17033 (bash)
      Tasks: 22 (limit: 1582)
        CPU: 5min 13.736s
     CGroup: /system.slice/horus_rx_dual.service
             ├─17033 bash /home/pi/horusdemodlib/start_dual_4fsk.sh
             ├─17054 rtl_fm -M raw -F9 -d 0 -s 48000 -p 0 -f 434195000
             ├─17055 tee /dev/fd/63 /dev/fd/62
             ├─17056 bash /home/pi/horusdemodlib/start_dual_4fsk.sh
             ├─17057 bash /home/pi/horusdemodlib/start_dual_4fsk.sh
             ├─17058 ./build/src/horus_demod -q --stats=5 -g -m binary --fsk_lower=12500 --fsk_upper=1>
             ├─17059 python -m horusdemodlib.uploader --freq_hz 434195000
             ├─17061 ./build/src/horus_demod -q --stats=5 -g -m binary --fsk_lower=2500 --fsk_upper=75>
             └─17062 python -m horusdemodlib.uploader --freq_hz 434195000 --freq_target_hz 434200000

The log output can be viewed by running:

sudo journalctl -u horus_rx.service -f -n

To stop the service, run:

sudo systemctl stop horus_rx.service

You can also completely disable the service by then running:

sudo systemctl disable horus_rx.service

6. Updating

Note: If you are updating from a version prior to 0.5.x, please remove the old installation using the instructions above.

To update an existing horusdemodlib installation, you should be able to perform the following:

cd ~/horusdemodlib/
. venv/bin/activate
pip install -U horusdemodlib

This should result in the current [latest] version of horusdemodlib being installed into the virtual environment you set up as part of the installation steps. You will now need to re-start any decoders you might have running, e.g:

sudo systemctl restart horus_rx

Clone this wiki locally