Skip to content
joecupanoPublic

About

SIGedge is an RF edge platform: it takes SDR hardware attached to one host and makes it usable — as raw IQ or already-demodulated audio — by upstream services on that host or across the network. It doesn't replace SDRangel, GQRX, or OpenWebRX+; it's the layer underneath that decides how those apps, and others like AI/analytics pipelines

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

SIGedge

If you've plugged an RTL-SDR or a HackRF straight into SDRangel, GQRX, CubicSDR, or a similar app and listened to the world go by, you already understand the core of what SIGedge does — it just gives that same radio, and others like it, two more ways to work: as a browser-based receiver anyone on the network can open, and as a radio server that many apps and services can share at once instead of one app locking up the USB device by itself.

SIGedge is an RF edge platform: it takes SDR hardware attached to one host and makes it usable — as raw IQ or already-demodulated audio — by upstream services on that host or across the network. It doesn't replace SDRangel, GQRX, or OpenWebRX+; it's the layer underneath that decides how those apps, and others like an AI/analytics pipeline, get access to the hardware.

Supported SDR hardware

The three devices used throughout this documentation's reference examples:

Device Role
RX-888 MkII Wideband HF receiver
HackRF One Wideband TX/RX
RTL-SDR (v3/v4) Low-cost VHF/UHF receiver

Also supported, installable during setup or any time afterward with SIGedge device install <device>: BladeRF, Ettus Research USRP (UHD), RigExpert Fobos, KerberosSDR, LimeSDR, PlutoSDR, and SDRplay. Run SIGedge list library for the full, current package list.

From a dedicated SDR app to a shared radio platform

The apps in the SDR world you already know — SDRangel, GQRX, CubicSDR, rtl_tcp, hackrf_transfer — all work the same way: one app opens the radio directly, either locally or over the network via SoapyRemote, and has it exclusively for as long as it's running. SIGedge supports that model exactly as-is; nothing about it changes.

What SIGedge adds is a second model. Instead of one app owning the radio, a background service called radiod (from the ka9q-radio project) owns it once, continuously, and republishes its IQ/audio/control data as an IP multicast network stream — so any number of consumers (other apps, other hosts, browser sessions, recorders, an AI pipeline) can listen to the same radio at the same time, without any of them opening the USB device themselves.

SDR hardware -> ka9q-radio radiod -> RTP/IP multicast -> any number of network consumers
SDR hardware -> direct-access app (SoapySDR / SoapyRemote) -> one exclusive owner

Both models install side by side; you choose, per device, which one a given radio is doing at any moment, with SIGedge assign <device> <service>. A single physical SDR has exactly one owning service at a time — see "Device ownership across services" below.

Services

Each of the following runs on top of one of the two models above. This section covers only what each one gives you and how it fits together — full setup steps live in the linked deployment document.

Direct SDR access (SoapySDR / SoapyRemote)

Delivers: exactly the workflow you already know. SDRangel, GQRX, CubicSDR, or a vendor tool like hackrf_transfer/rtl_tcp opens a SIGedge-attached radio directly, either on the same host or, via SoapyRemote, from another machine on the network.

How it fits together: the application links against SoapySDR (or a device-specific driver) and talks to the hardware directly — SIGedge installs the driver/plumbing but puts nothing of its own between the app and the radio. Only one such application can hold a given device at a time.

Setup: device drivers install via SIGedge device install <device>; SoapyRemote's own network service ships disabled and is an explicit opt-in. See Direct SDR use, including SoapySDR below.

ka9q-radio — shared radio server

Delivers: the radio becomes a standing network resource — any number of consumers can subscribe to it at once, instead of "whichever app has it, nothing else can touch it."

How it fits together:

                              SIGedge node

  RX-888 MkII -----+       +-------------------+
  HackRF ----------+------>| ka9q-radio radiod |----> RTP/IP multicast
  RTL-SDR ---------+       +-------------------+              |
                                                              +--> APIs / MCP
                                                              +--> analytics
                                                              +--> recorders
                                                              +--> decoders

radiod uses ka9q-radio's native hardware interfaces for the RX-888 MkII, HackRF, and RTL-SDR paths; consumers subscribe to its multicast streams instead of opening the device.

Setup: full install, per-mission configuration (frequency/mode presets for each supported radio), and activation steps are in KA9Q-DEPLOYMENT.md.

OpenWebRX+ — browser waterfall and audio

Delivers: a receiver reachable from any browser, no client app to install — the easiest way to give someone else on the network, or yourself remotely, a listen without a dedicated SDR app.

How it fits together: it's a direct-access consumer of the hardware, the same model as SDRangel/GQRX above, so it's mutually exclusive with a radiod mission on the same device. It can run alongside radiod on the same host as long as each owns different devices — e.g. the RX-888 in OpenWebRX+ while the HackRF feeds a radiod APRS mission. Which devices it uses follows the assignments: SIGedge assign <device> openwebrx enables that device in OpenWebRX+ and disables it everywhere else.

Setup: SIGedge install openwebrxplus installs OpenWebRX+ from the luarvique PPA (Debian 11–13 including Raspberry Pi OS, Ubuntu 22.04/24.04); add SIGedge device install rx888mk2-soapy for the RX-888. Details and RX-888 gain settings are in KA9Q-DEPLOYMENT.md's "RX-888 in OpenWebRX+" section; give it a device with SIGedge assign <device> openwebrx. DMR, D-STAR, YSF and NXDN voice need an AMBE decoder: a hardware AMBE-3000 stick, or the optional software one, SIGedge install codecserver-softmbe (see the patent note in that section).

AI and analytics bridges

Delivers: early-stage tooling for asking an LLM (via Ollama) what's active on a radiod channel, rather than watching a waterfall yourself.

How it fits together: reads radiod's multicast channel status/audio, read-only, limited to whichever channels an operator has explicitly listed.

Setup: ai/README.md.

Decoders — turning a radiod channel into decoded output

Delivers: the "decoders" leaf of the architecture diagram above — a radiod audio channel feeding an actual protocol/signal decoder (APRS, POCSAG, weather-satellite images, ...) instead of only being available to listen to.

How it fits together: ka9q-radio-tools' pcmrecord reads one channel's multicast audio and pipes it, raw, into a decoder that already accepts an audio stream on stdin — the same shape as the well-known rtl_fm | direwolf or rtl_fm | multimon-ng patterns, just with pcmrecord in place of rtl_fm.

Setup: decoders/README.md; decoders/hackrf-aprs-direwolf is the first instance, bridging the hackrf-aprs reference mission into direwolf.

Device ownership across services

Every service above eventually touches physical hardware, and an SDR has exactly one owning service at a time. The device, not the service, is the unit of assignment: each attached SDR is assigned to one service — a radiod mission, OpenWebRX+, the rtl_tcp server, SDRangel server, SoapySDR server — or to none, and that's recorded in /etc/sigedge/assignments. Two different SDRs on the same host can belong to two different services at once; the constraint is per device, not per host.

Moving a device: SIGedge assign

SIGedge assign list                       # what's attached and who it's assigned to
SIGedge assign rx888 openwebrx            # give the RX-888 to OpenWebRX+
SIGedge assign hackrf radiod:hackrf-aprs  # give the HackRF to a ka9q-radio mission
SIGedge assign 00000101 rtltcp            # a device by serial
SIGedge assign hackrf none                # release it

(SIGedge assign runs scripts/sdr-assign.) A device is named by its serial, by its USB socket for an RX-888 (e.g. port:3-4, the same in bootloader and firmware mode), or by its kind (rx888, hackrf, rtlsdr) when only one of that kind is attached. Every move runs the same sequence:

  1. Release the current owner — stop its radiod mission, disable the device in OpenWebRX+'s settings, stop rtl_tcp, ... — plus any other service whose own config still claims the device.
  2. Verify nothing still holds the USB device, by checking /dev/bus/usb/... with fuser — the ground truth whichever driver a service uses.
  3. Hand off with the device type's hook. For the RX-888 that means resetting the FX3 to its bootloader so the next owner loads its own firmware, and allowing ka9q-radio's firmware loader only when radiod is the new owner (see KA9Q-DEPLOYMENT.md's RX-888 section).
  4. Acquire: configure the new owner for exactly this device (pin a mission or rtl_tcp to its serial, enable and pin it in OpenWebRX+) and start it.
  5. Record the assignment.

Each service plugs in through a small adapter in scripts/adapters/ that knows how to read its claims, release a device and acquire one. Two services can't select a single device: SDRangel server and SoapySDR server open any SDR they can see, so SIGedge assign refuses to run either alongside other owners unless you pass --force. Neither OpenWebRX+ nor radiod can tell RX-888s apart (the RX-888's serial depends on its firmware), so either one supports a single RX-888 per host. A host that predates the assignment record is adopted automatically on the first assignment: devices that exactly one running service already claims are recorded as theirs.

scripts/service_toggle {ka9q-radio|openwebrx|off} still exists for "move everything to one side": it makes the equivalent per-device assignments.

Seeing who owns what: SIGedge inventory

SIGedge inventory (scripts/device-inventory.sh) lists each attached SDR with its id, its assignment, and who actually holds it right now — process and systemd unit, from fuser (run it with sudo to see other users' processes). It warns when these disagree: a device held by something other than its assigned owner, another service's config also claiming it, ka9q-radio's RX-888 firmware loader left active for an RX-888 assigned elsewhere. --detail shows every claim source; --json is machine-readable. OpenWebRX+ opens a device only while someone is listening, so an idle OpenWebRX+ assignment shows no live holder.

Access and drivers

All supported SDR device nodes are owned by one group, sdr (mode 0660, config/10-sigedge-sdr.rules), and every account that drives hardware — openwebrx, ka9q-radio's radio, the operator's login — is a member, so a device works the same for whichever service gets it. scripts/sdr-access {install|sync|check} manages this; SIGedge setup installs it. Group membership takes effect when a service restarts, or at the user's next login.

scripts/driver-check finds driver-stack problems that break services in ways their own checks don't: the same SoapySDR driver installed twice (SIGedge's build and the distro's), binaries that can't load a library, and /usr/local copies shadowing packaged libraries or Python modules. SIGedge setup runs it at the end; run it again after installing anything by hand.

Several units of the same type

When two always-on services genuinely need the same device type at once, the resolution is one physical unit per consumer, each assigned to its own service by serial number — RTL-SDR dongles are cheap enough that a dedicated unit per consumer is the practical default; RX-888/HackRF's higher cost is why the one-unit, one-owner model above still applies to them.

RTL-SDR dongles frequently ship with no serial number in EEPROM, or with a default shared across units, which defeats serial pinning before it starts. SIGedge device install rtlsdr (interactive sessions only) checks every attached unit with rtl_eeprom and prompts to assign a unique serial to any device with none, or with one that collides with another attached unit; a non-interactive/headless run skips this with a warning, and the serial can be set later by hand with sudo rtl_eeprom -d <index> -s <serial>. No other supported device in this list has a genuinely user-writable EEPROM/OTP serial: HackRF's ID is a read-only factory value, and the RX-888 path (devices/pkg_rx888) deliberately never flashes EEPROM/SPI.

Supported platforms

The current target operating system is Ubuntu Server 24.04 LTS or Debian GNU/Linux 13 (Trixie, including Raspberry Pi OS Desktop, which is Trixie-based) on:

  • amd64/x86_64 systems, typically an Intel i5-class host with 16 GB RAM and at least 128 GB storage
  • arm64/aarch64 systems, including Raspberry Pi 4B/5 with 4-8 GB RAM and at least 64 GB storage

Actual compute, USB, network, and storage requirements depend on the SDR sample rates and the number and type of downstream channels.

SIGedge installation and setup

On a fresh Ubuntu Server 24.04 LTS installation:

sudo apt update
sudo apt upgrade
sudo apt install -y build-essential cmake git
git clone https://github.com/joecupano/SIGedge.git
cd SIGedge
./SIGedge setup

Setup presents device and service choices, installs the selected components, and reboots the system when complete. RTL-SDR and HackRF are the default device selections; other supported devices can be selected during setup or installed later:

SIGedge device install <device>

Installation and activation are always kept separate, for both deployment paths:

  • The standard setup installs SDR drivers, ka9q-radio support, and direct-access (SoapySDR) plumbing.
  • No radio-specific radiod instance is configured, enabled, or started by default.
  • Direct-access network-service plumbing (SoapyRemote) is installed, but its network service is left disabled and stopped by default.
  • The operator explicitly assigns each device to a service, which configures and starts it: SIGedge assign <device> <service> (see Device ownership across services).

Package management

List the package library and show installed entries:

SIGedge list library
SIGedge list installed
SIGedge list packages

Manage an individual package with:

SIGedge install <package>
SIGedge remove <package>
SIGedge purge <package>

Build-capable packages may also support:

SIGedge build <package>
SIGedge package <package>

Direct SDR use, including SoapySDR

./SIGedge setup (via scripts/setup_devices) installs:

  • Generic SoapySDR tooling and headers (soapysdr-tools, libsoapysdr-dev) for applications that link against SoapySDR locally on the same host.
  • The RX-888 SoapySDR driver (devices/pkg_rx888mk2-soapy, built from ON5HB/RX888MK2-Soapy) when the rx888 device is selected, for direct-access RX-888 use outside of ka9q-radio — i.e. OpenWebRX+. Install it on its own with SIGedge device install rx888mk2-soapy.
  • soapyremote-server and soapysdr-module-remote, for exposing SoapySDR devices to remote network clients over SoapyRemote's own protocol.

Local direct access

An application built against SoapySDR (or a device-specific tool such as rtl_tcp, hackrf_transfer, hackrf_info) can open a locally attached, supported SDR directly once its driver package is installed via SIGedge device install <device>. No additional SIGedge service needs to be enabled for this — it's just the application and the hardware. Two conditions: your login must be in the sdr group (added by setup; takes effect at your next login), and the device must not be owned by a service — check with SIGedge inventory, and release it first with SIGedge assign <device> none, otherwise the application and the service fight over it.

Remote direct access (SoapyRemote)

soapyremote-server lets a remote SoapySDR client discover and stream from this host's SDRs over the network. Because that's a much bigger exposure than local-only access, SIGedge always leaves it disabled and stopped after install — enabling it is an explicit, separate opt-in:

sudo systemctl enable --now soapyremote-server.service

Verify it's actually running before relying on it, and stop it when you're done:

systemctl is-active soapyremote-server.service
avahi-browse -rt _soapy._tcp   # confirms it's discoverable on the network
sudo systemctl disable --now soapyremote-server.service

Before enabling it, make sure none of the SDRs it would expose are owned by another service (SIGedge inventory) — SoapyRemote does not know or care that another process has a device open, and a collision there fails at the driver/USB level, not gracefully. To run SoapySDR server as a SIGedge service instead, assign devices to it: SIGedge assign <device> soapysdrsrv.

Network and hardware notes

  • Use a USB 3.x SuperSpeed path for the RX-888 MkII. The initial target sample rate is 64.8 Msps.
  • Bind multicast deliberately on hosts with multiple Ethernet, Wi-Fi, VPN, container, or management interfaces.
  • Disable onboard Bluetooth and Wi-Fi on dedicated receive nodes when they are unnecessary and local RF emissions are a concern.
  • Size powered USB hubs and power supplies for the attached devices; do not assume every hub port can supply its maximum current simultaneously.

Further reading

  • KA9Q-DEPLOYMENT.md — full ka9q-radio deployment instructions, RX-888 firmware bring-up and handoff, and current implementation limitations.
  • scripts/README.md — the setup scripts and the device-ownership tools (sdr-assign, device-inventory.sh, sdr-access, driver-check, adapters and handoff hooks).
  • NETWORKING.md — how ka9q-radio's multicast addressing actually resolves (dns = yes vs. hashed), and this deployment's static address scheme.
  • ai/README.md — bridges/adapters exposing radiod multicast channels to AI consumers (the "APIs / MCP" branch of the architecture diagram above).
  • decoders/README.md — bridges feeding radiod multicast audio into decode-side applications (the "decoders" branch of the architecture diagram above).

About

SIGedge is an RF edge platform: it takes SDR hardware attached to one host and makes it usable — as raw IQ or already-demodulated audio — by upstream services on that host or across the network. It doesn't replace SDRangel, GQRX, or OpenWebRX+; it's the layer underneath that decides how those apps, and others like AI/analytics pipelines

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages