Introduction
After nearly one year of kernel crashes, WiFi driver panics, and instability, this configuration has proven rock-solid for 24/7 SDR server operation on the Orange Pi Zero 3 (Allwinner H618 SoC).
The design goal: a compact, low-power, fully headless SDR server accessible entirely over the network — including from an Android smartphone, with no desktop PC required for day-to-day operation.
Hardware
Main Board & Power
| Component | Details |
|---|---|
| SBC | Orange Pi Zero 3 — Allwinner H618, quad-core Cortex-A53 @ 1.5 GHz |
| RAM | 1.5 GB LPDDR4 |
| Network | Gigabit Ethernet — WiFi completely disabled |
| Power | Official Raspberry Pi 5V/3A USB-C adapter |
| USB | SDR dongles plug directly into the Orange Pi Zero 3 USB hat connector — no external hub |
| Enclosure | Custom box — SBC, RF switch, filters, all cabling inside |
SDR Receivers
| Device | Frequency | Role | HF Capability |
|---|---|---|---|
| RTL-SDR Blog V4 | 500 kHz – 1766 MHz | General purpose RX | Built-in HF upconverter |
| RTL-SDR (ADS-B) | 1090 MHz | ADS-B aircraft tracking | — |
| Airspy Discovery HF+ | 0.5 kHz – 31 MHz + VHF | HF + VHF high dynamic range | Native HF, best-in-class |
RF Switch — HMC241 (Analog Devices GaAs SP4T)
The Airspy Discovery HF+ is connected through an HMC241 GaAs MMIC SP4T switch, allowing SpyServer to select between 4 antennas via 2 GPIO pins.
| Spec | Value |
|---|---|
| Part | HMC241ALP3E — Analog Devices / Hittite |
| Type | GaAs MMIC SP4T Non-Reflective Switch |
| Frequency | DC to 4 GHz |
| Insertion Loss | 0.7 dB @ 2 GHz |
| Isolation | 43 dB @ 2 GHz |
| Control | 2-pin TTL/CMOS — integrated 2:4 decoder on chip |
| Supply | 3V–5V — powered from Orange Pi 3.3V GPIO pin |
GPIO Truth Table — Antenna Selection
| CTRL_A | CTRL_B | Port | Antenna |
|---|---|---|---|
| 0 | 0 | RF1 | Quadrifilar helix 145 MHz — self-built, LEO satellites, circularly polarized |
| 1 | 0 | RF2 | V-dipole 137 MHz — NOAA/Meteor weather satellites |
| 0 | 1 | RF3 | YouLoop — passive magnetic loop for HF |
| 1 | 1 | RF4 | 16.5m random wire — broadband HF |
Antennas
| Antenna | Band | Notes |
|---|---|---|
| Quadrifilar Helix (QFH) | 145 MHz | Self-built — circularly polarized, excellent for LEO satellite passes |
| V-dipole | 137 MHz | NOAA/Meteor-M weather satellite image reception |
| YouLoop | HF | Noise-cancelling passive magnetic loop — excellent in urban environments |
| 16.5m random wire | HF broadband | Wide HF coverage via 9:1 unun + toroid choke |
| ADS-B antenna | 1090 MHz | Direct to RTL-SDR ADS-B dongle |
| Discone | VHF/UHF broadband | Direct to RTL-SDR Blog V4 |
RF Filtering & Chokes
- LPF30 MHz Low-Pass FilterProtects HF from VHF/UHF overload
- FILTERFM Notch FilterEliminates 88-108 MHz FM overload
- CHOKEToroid ChokesCommon-mode suppression on coax
- CHOKESpiral Cable ChokesFeed point common-mode suppression
The Critical Choice — Legacy Kernel
| Branch | Package | Result |
|---|---|---|
| current | linux-image-current-sunxi64 | CRASH within hours |
| legacy ✓ | linux-image-legacy-sunxi64 | STABLE — days of uptime |
Install Legacy Kernel
sudo apt install linux-image-legacy-sunxi64 linux-dtb-legacy-sunxi64
sudo reboot
uname -r # Expected: 6.6.75-legacy-sunxi64
SDR Software Stack
| Software | Protocol / Port | SDR Device | Client |
|---|---|---|---|
| OpenWebRX+ | Web — :8073 | Airspy HF+ / RTL-SDR V4 | Any browser |
| SpyServer | TCP — :5432 | Airspy HF+ via RF switch | SDR++Brown, SDRAngel |
| rtl_tcp (V4) | TCP — :1234 | RTL-SDR Blog V4 | SDR++Brown, SDRAngel, GQRX |
| rtl_tcp (ADS-B) | TCP — :1235 | RTL-SDR ADS-B dongle | dump1090, Virtual Radar |
| Radiosonde auto_rx | Web — custom port | RTL-SDR V4 | Any browser |
Package Repositories
| Software | Source |
|---|---|
| OpenWebRX+ | repo.openwebrx.de/debian/ bookworm main |
| OpenWebRX Plus (luarvique) | luarvique.github.io/ppa/trixie ./ |
| RTL-SDR Blog V4 drivers | RTL-SDR Blog official repo — rtl-sdr.com (NOT Debian repo) |
| SpyServer | airspy.com/download/ — arm64 binary, manual install |
| Radiosonde auto_rx | github.com/projecthorus/radiosonde_auto_rx |
| SoapySDR / Airspy HF+ | apt install soapysdr-module-airspyhf (then apply library fix — Section 6) |
Supported Decoders — 30+
All decoders run simultaneously on the same hardware. Grouped by band and protocol type:
HF Decoders
VHF / Marine / Aviation
Digital Voice
IoT & Sensors
Satellite & Radiosonde
SDR++Brown Exclusive
Critical System Tweaks
1 — RTL-SDR DVB Module Blacklist
Linux loads DVB-T drivers for RTL-SDR dongles by default. Blacklist them:
blacklist dvb_usb_rtl28xxu
blacklist rtl2832
blacklist rtl2830
install dvb_usb_rtl28xxu /bin/true
install rtl2832 /bin/true
sudo update-initramfs -u && sudo reboot
2 — Disable WiFi & Bluetooth (aw859a)
The H618 WiFi driver causes kernel instability. Mask and blacklist completely:
sudo systemctl mask aw859a-wifi.service
sudo systemctl mask aw859a-bluetooth.service
# Add to /etc/modprobe.d/wifi-blacklist.conf:
blacklist sprdwl_ng
blacklist uwe5622_bsp_sdio
blacklist panfrost
3 — CPU Governor: schedutil
Default 'performance' governor keeps CPU at max. schedutil scales dynamically. Temperature dropped ~8-10°C.
echo schedutil | sudo tee /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
sudo sed -i 's/^GOVERNOR=.*/GOVERNOR=schedutil/' /etc/default/cpufrequtils
sudo sed -i 's/^ENABLE=false/ENABLE=true/' /etc/default/cpufrequtils
4 — USB Autosuspend (Critical for SDR Dongles)
Without this, USB dongles enter sleep and become unresponsive. Add to /boot/armbianEnv.txt extraargs:
usbcore.autosuspend=-1
5 — Disable Unnecessary Services
sudo systemctl disable --now exim4
sudo truncate -s 0 /var/log/exim4/paniclog
sudo systemctl mask systemd-networkd-wait-online
sudo systemctl mask systemd-networkd-wait-online@.service
sudo systemctl mask alsa-utils.service
sudo systemctl mask fake-hwclock.service
6 — SoapySDR / SpyServer Library Conflict
# Copy working version from /usr/local to system path
sudo cp /usr/local/lib/SoapySDR/modules0.8/libairspyhfSupport.so \
/usr/lib/SoapySDR/modules0.8/libairspyhfSupport.so
# Lock BOTH files — apt can never overwrite them
sudo chattr +i /usr/lib/SoapySDR/modules0.8/libairspyhfSupport.so
sudo chattr +i /usr/local/lib/SoapySDR/modules0.8/libairspyhfSupport.so
# Verify — look for 'i' in the output
lsattr /usr/lib/SoapySDR/modules0.8/libairspyhfSupport.so
# Expected: ----i---------e------- .../libairspyhfSupport.so
To update the library manually in the future:
sudo chattr -i /usr/lib/SoapySDR/modules0.8/libairspyhfSupport.so
# ... make changes, then re-lock:
sudo chattr +i /usr/lib/SoapySDR/modules0.8/libairspyhfSupport.so
RF Switch GPIO Control
The HMC241 is controlled by two GPIO pins. A Python script selects the active antenna — callable from SpyServer or the SSH Button Android app.
Python GPIO Control
import OPi.GPIO as GPIO
GPIO.setmode(GPIO.SUNXI)
CTRL_A = 'PA6' # Adjust to your wiring
CTRL_B = 'PA7' # Adjust to your wiring
GPIO.setup(CTRL_A, GPIO.OUT)
GPIO.setup(CTRL_B, GPIO.OUT)
def select_antenna(port): # port = 1, 2, 3 or 4
GPIO.output(CTRL_A, port & 1)
GPIO.output(CTRL_B, (port >> 1) & 1)
SpyServer Config
bind_host = 0.0.0.0
bind_port = 5432
list_in_directory = 1
Full Android Control — No PC Required
Complete operation from an Android smartphone. Every function of the SDR server is accessible without touching a desktop computer.
Installation Guide — Step by Step
1 — Flash Armbian Trixie Image
Download the Armbian 13 (Trixie) image for Orange Pi Zero 3 from armbian.com. Choose the Debian Trixie variant — not Ubuntu.
# Write to SD card (replace /dev/sdX with your card)
dd if=Armbian_26.x_Orangepizero3_trixie_*.img of=/dev/sdX bs=4M status=progress
sync
2 — Install Legacy Kernel + DTB (CRITICAL)
# Install legacy kernel AND DTB together — never one without the other
sudo apt update
sudo apt install linux-image-legacy-sunxi64 linux-dtb-legacy-sunxi64
# Remove current kernel to prevent accidental boot into wrong kernel
sudo apt remove linux-image-current-sunxi64
# Reboot into legacy kernel
sudo reboot
# Verify after reboot
uname -r
# Expected: 6.6.75-legacy-sunxi64
⚠ After first boot, immediately disable WiFi and BT drivers. → See Section 06, Tweak 2: Disable WiFi & Bluetooth
⚠ Set USB autosuspend and CPU governor. → See Section 06, Tweaks 3 & 4
⚠ Disable unnecessary services. → See Section 06, Tweak 5
3 — Verify Armbian Sources (Trixie)
Armbian Trixie repo is pre-configured in the image. Verify it is correct:
cat /etc/apt/sources.list.d/armbian.sources
# Should show: Suites: trixie
4 — RTL-SDR Blog V4 Drivers
Do NOT use the Debian repo rtl-sdr package — install from RTL-SDR Blog source for V4 support:
⚠ After installing, apply the DVB blacklist. → See Section 06, Tweak 1: RTL-SDR DVB Module Blacklist
sudo apt install libusb-1.0-0-dev git cmake
git clone https://github.com/rtlsdrblog/rtl-sdr-blog
cd rtl-sdr-blog && mkdir build && cd build
cmake ../ -DINSTALL_UDEV_RULES=ON
make && sudo make install
sudo cp ../rtl-sdr.rules /etc/udev/rules.d/
sudo ldconfig
sudo udevadm control --reload-rules && sudo udevadm trigger
# Add user to plugdev group
sudo usermod -aG plugdev $USER
5 — SoapySDR + Airspy HF+ Support
⚠ After installing, immediately apply the library lock. → See Section 06, Tweak 6: SoapySDR / SpyServer Library Conflict
sudo apt install soapysdr-tools libsoapysdr-dev soapy-connector
sudo apt install soapysdr-module-airspyhf
# Verify Airspy HF+ is detected:
SoapySDRUtil --find
# Should show: airspyhf
6 — OpenWebRX+ Installation
# Add OpenWebRX repo
wget -O - https://repo.openwebrx.de/debian/key.gpg | sudo apt-key add -
echo "deb https://repo.openwebrx.de/debian/ bookworm main" | \
sudo tee /etc/apt/sources.list.d/openwebrx.list
# Add OpenWebRX Plus (luarvique fork — more decoders)
wget -O - https://luarvique.github.io/ppa/openwebrx-plus.gpg | \
sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/openwebrx-plus.gpg
echo "deb [signed-by=/etc/apt/trusted.gpg.d/openwebrx-plus.gpg] \
https://luarvique.github.io/ppa/trixie ./" | \
sudo tee /etc/apt/sources.list.d/openwebrx-plus.list
sudo apt update && sudo apt install openwebrx
7 — OpenWebRX+ External Decoder Plugins
These are the external decoder tools OpenWebRX+ uses in the background. Install all for full decoder support:
sudo apt install \
dump1090-fa-minimal \
dump978-fa-minimal \
dumphfdl \
dumpvdl2 \
acarsdec \
libacars2 \
multimon-ng \
redsea \
csdr-skimmer \
sonde-decoders \
faad \
direwolf
| Package | Decodes |
|---|---|
| dump1090-fa-minimal | ADS-B 1090 MHz — aircraft position |
| dump978-fa-minimal | UAT 978 MHz — US aircraft traffic |
| dumphfdl | HFDL — aircraft position over HF |
| dumpvdl2 | VDL Mode 2 — aircraft VHF datalink |
| acarsdec + libacars2 | ACARS — aircraft communications |
| multimon-ng | POCSAG, FLEX, DTMF, EAS pagers |
| redsea | RDS — FM broadcast station data |
| csdr-skimmer | CW skimmer — full-band Morse decode |
| sonde-decoders | RS41, DFM, M10 radiosondes |
| faad | DAB / AAC digital radio |
| direwolf | AIS, APRS packet decoder |
8 — SpyServer Installation
⚠ Make sure the SoapySDR library fix is applied BEFORE starting SpyServer. → See Section 06, Tweak 6
# Create directory and download binary
mkdir ~/spyserver_arm64 && cd ~/spyserver_arm64
wget https://airspy.com/downloads/spyserver-arm64.tgz
tar xvf spyserver-arm64.tgz
chmod +x spyserver
# Create config file
cat > spyserver.config << 'EOF'
bind_host = 0.0.0.0
bind_port = 5432
list_in_directory = 1
owner_name =
owner_email =
EOF
# Create systemd service
sudo tee /etc/systemd/system/spyserver.service << 'EOF'
[Unit]
Description=SpyServer for Airspy HF+
After=NetworkManager-wait-online.service
[Service]
User=sdruser
WorkingDirectory=/home/sdruser/spyserver_arm64
ExecStart=/home/sdruser/spyserver_arm64/spyserver \
/home/sdruser/spyserver_arm64/spyserver.config
StandardOutput=append:/home/sdruser/spyserver_arm64/spyserver.log
StandardError=append:/home/sdruser/spyserver_arm64/spyserver.err
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable spyserver
9 — rtl_tcp Services
Two separate rtl_tcp instances — one for RTL-SDR V4 (general), one for ADS-B. They conflict with radiosonde since all use the RTL-SDR hardware.
# RTL-SDR V4 service (device index 1)
sudo tee /etc/systemd/system/rtl_tcp.service << 'EOF'
[Unit]
Description=RTL-SDR TCP Server
After=network.target
Conflicts=radiosonde.service
[Service]
Type=simple
ExecStart=/usr/local/bin/rtl_tcp -a 0.0.0.0 -p 8091 -d 1 -s 2048000 -b 30
User=sdruser
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
EOF
# ADS-B RTL-SDR service (device index 0)
sudo tee /etc/systemd/system/rtl_tcp_adsb.service << 'EOF'
[Unit]
Description=RTL-SDR ADSB TCP Server
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/rtl_tcp -a 0.0.0.0 -p 8092 -d 0 -s 2048000 -b 30
User=sdruser
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
10 — Radiosonde auto_rx Installation
cd ~
git clone https://github.com/projecthorus/radiosonde_auto_rx
cd radiosonde_auto_rx
# Create Python virtualenv
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
deactivate
# Create systemd service
sudo tee /etc/systemd/system/radiosonde.service << 'EOF'
[Unit]
Description=Radiosonde Auto RX
After=network.target
Conflicts=rtl_tcp.service
[Service]
Type=simple
User=sdruser
WorkingDirectory=/home/sdruser/radiosonde_auto_rx/auto_rx
ExecStart=/home/sdruser/radiosonde_auto_rx/venv/bin/python3 auto_rx.py
Restart=no
TimeoutStopSec=10
KillMode=control-group
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
11 — RF Switch Script (rf_setter.py)
Uses gpioset from libgpiod — no Python GPIO library needed. GPIO pins: gpiochip0, pins 72 and 79.
cat > /home/sdruser/rf_setter.py << 'EOF'
import sys
import subprocess
def menu():
print("\n=== RF SWITCH MENU ===")
print("1. Quadrifilar Helix 145 MHz")
print("2. 9:1 Unun (Random Wire HF)")
print("3. V-Dipole 137 MHz")
print("4. YouLoop (HF)")
print("5. OWon (OpenWebRX mode)")
print("6. OWoff (SpyServer mode)")
print("======================")
return input("Select: ").strip()
if len(sys.argv) < 2:
secim = menu()
else:
secim = sys.argv[1].strip()
komut = []
rf_ismi = ""
if secim in ("1", "0"):
komut = ["gpioset", "-t", "0", "-c", "gpiochip0", "72=0", "79=0"]
rf_ismi = "Quadrifilar Helix"
elif secim == "2":
komut = ["gpioset", "-t", "0", "-c", "gpiochip0", "72=1", "79=0"]
rf_ismi = "9:1 Unun (Random Wire)"
elif secim == "3":
komut = ["gpioset", "-t", "0", "-c", "gpiochip0", "72=0", "79=1"]
rf_ismi = "V-Dipole 137 MHz"
elif secim == "4":
komut = ["gpioset", "-t", "0", "-c", "gpiochip0", "72=1", "79=1"]
rf_ismi = "YouLoop"
elif secim == "5":
komut = ["bash", "/home/sdruser/OWon.sh"]
rf_ismi = "OpenWebRX mode activated"
elif secim == "6":
komut = ["bash", "/home/sdruser/OWoff.sh"]
rf_ismi = "SpyServer mode activated"
else:
print("Invalid selection!")
sys.exit(1)
try:
subprocess.run(komut, check=True)
print(f"OK: {rf_ismi}")
except subprocess.CalledProcessError as e:
print(f"Error: {e}")
EOF
# Install libgpiod tools (required for gpioset)
sudo apt install gpiod
12 — Mode Switching Scripts (OWon / OWoff)
Airspy HF+ can only be used by one application at a time — either OpenWebRX or SpyServer. These scripts handle the switching:
# OWon.sh — Switch to OpenWebRX mode
cat > /home/sdruser/OWon.sh << 'EOF'
#!/bin/bash
sudo systemctl stop spyserver
sudo systemctl start rtl_tcp
sudo systemctl start rtl_tcp_adsb
sudo systemctl start openwebrx
echo "OpenWebRX activated, SpyServer stopped, RTL-TCP servers started."
EOF
chmod +x /home/sdruser/OWon.sh
# OWoff.sh — Switch to SpyServer mode
cat > /home/sdruser/OWoff.sh << 'EOF'
#!/bin/bash
sudo systemctl stop openwebrx
sudo systemctl start rtl_tcp
sudo systemctl start rtl_tcp_adsb
sudo systemctl start spyserver
echo "SpyServer activated, OpenWebRX stopped, RTL-TCP servers started."
EOF
chmod +x /home/sdruser/OWoff.sh
Quick Setup Checklist
- Flash Armbian 13 (Trixie) to SD/eMMC
- Install legacy kernel: linux-image-legacy-sunxi64 ← MOST CRITICAL
- Blacklist DVB/RTL-SDR conflicting kernel modules
- Mask aw859a-wifi and aw859a-bluetooth services
- Blacklist sprdwl_ng, uwe5622_bsp_sdio, panfrost
- Add usbcore.autosuspend=-1 to armbianEnv.txt ← CRITICAL
- Set CPU governor to schedutil, enable cpufrequtils
- Disable exim4 and mask networkd-wait-online
- Install RTL-SDR drivers from RTL-SDR Blog repo (not Debian)
- Install SoapySDR + Airspy HF+ support module
- Fix and lock libairspyhfSupport.so with chattr +i ← CRITICAL
- Deploy SpyServer binary, configure port 5432
- Wire HMC241 RF switch: CTRL_A/B to GPIO, power from 3.3V
- Configure rtl_tcp on ports 1234 and 1235
- Install OpenWebRX+ and Radiosonde auto_rx
- Set up SSH Button on Android for antenna switching
- Test all clients: SDR++Brown, SDRAngel, Samsung Browser
Developed with Claude AI
This entire SDR server setup — from diagnosing kernel crashes to fixing the SoapySDR library conflict, optimizing system services, and writing this guide — was developed collaboratively with Claude, the AI assistant by Anthropic.
Over multiple sessions, Claude helped:
- ◆ Identify the legacy vs current kernel issue after 1 year of failed attempts
- ◆ Analyze system logs in real time and fix every warning and error
- ◆ Discover and resolve the SoapySDR / SpyServer library conflict
- ◆ Optimize CPU governor, USB power management, and services
- ◆ Research and verify the HMC241 RF switch GPIO wiring
- ◆ Write and format this complete documentation
Claude is available at claude.ai — highly capable for technical problem solving, embedded Linux, RF engineering, and system administration. If you are building an SDR server or debugging embedded Linux, Claude can save you days of work.
Summary
| Metric | Result |
|---|---|
| Uptime | 3+ days continuous — zero crashes |
| SDR receivers | 3 simultaneous (RTL-SDR Blog V4, ADS-B, Airspy HF+) |
| Switchable antennas | 4 via HMC241 GaAs SP4T RF switch |
| Protocols | SpyServer :5432, rtl_tcp :1234/:1235, OpenWebRX+ web, Radiosonde web |
| Android clients | SDR++Brown, SDRAngel, Samsung Browser, SSH Button |
| Desktop PC needed? | No — full Android operation |
| CPU temperature | ~45-55°C with schedutil governor |
| Power | 5V/3A — official Raspberry Pi adapter |
| Developed with | Claude AI — claude.ai by Anthropic |