SIGINT / wideband SDR (LimeSDR Mini) - "dragonegg"¶
The dragonegg laydown is AryaOS + a LimeSDR Mini + GPS: a wideband (10 MHz-3.5 GHz) software-defined radio on the edge for spectrum monitoring and signal-collection tasks, with the box's own position on the map.
AryaOS ships the driver and access layer for the LimeSDR. The collection/analysis application You point at it is deployment-specific.
What one LimeSDR can receive¶
Everything below was measured on a dragonegg box with an outdoor antenna, not inferred from datasheets. One SDR does one band at a time - these are alternatives, not a simultaneous list.
| Capability | Band | Verified result |
|---|---|---|
| ADS-B | 1090 MHz | 5,173 usable messages, 711 positions, 10 aircraft tracked |
| UAT | 978 MHz | aircraft decoded (N93214, light aircraft) |
| AIS | 162 MHz | 15 vessels, incl. USCG base stations at −27 dBFS |
| APRS | 144.39 MHz | 20 packets from 7 stations, MIC-E position reports |
| ACARS | 130-132 MHz | 5 aircraft, incl. routes (KSFO KABQ) and ARINC-622 |
| Band survey | 100 kHz-3.5 GHz | occupancy, noise floor, carrier detection |
The antenna decides, not the SDR
This is the single most important fact about the laydown. The same box, same software and same SDR produced 0 usable ADS-B messages on a short indoor whip and 5,173 on an outdoor antenna.
A 1090 MHz ADS-B antenna is nearly deaf at 131 MHz ACARS, and a VHF antenna is poor at 1090. Budget for the antenna and its feedline before anything else, and expect to swap it when you change bands.
Because the box cannot see what antenna is attached, SDR capabilities are Never auto-enabled - see Device roles.
What's on the image¶
soapysdr-module-lms7- the SoapySDR driver for LimeSDR / LMS7002M. Any SoapySDR client (readsb, SDR++, SDRangel, GQRX, GNU Radio) can drive the Lime asdriver=lime.limesuite- LimeSuite tools:LimeUtil(find/probe/update),LimeQuickTest.soapysdr-module-remote- SoapyRemote, so a remote operator can use the Lime over the network (see Remote access).soapysdr-tools-SoapySDRUtilfor enumeration/probing.
First use¶
Verify the device and update its gateware (a LimeSDR Mini usually needs a one-time update):
LimeUtil --find # should list "LimeSDR Mini"
SoapySDRUtil --probe="driver=lime" # full capability probe
sudo LimeUtil --update # one-time gateware/firmware update
Power
The LimeSDR Mini is a heavy, bursty USB draw. With GPS and a Pi 5 this is one of The highest-draw laydowns - use a proper 5V/5A (27 W) supply (or a true PoE+ 802.3at source). On a marginal supply the box can brown out. AryaOS will flag under-voltage (power-health) and, if it crash-loops, drop into safe mode.
Using the LimeSDR¶
As the AIS receiver¶
AIS-catcher is built with SoapySDR from 0.68-snstac3 onward, so it can drive the Lime
directly:
Note the syntax: SOAPYSDR: in AIS-catcher -h is a section heading, not part of the argument.
Passing -d SOAPYSDR sends it looking for a device with the literal serial SOAPYSDR instead of
using the one you selected.
Earlier builds were compiled without SoapySDR (readelf -d showed zero libSoapySDR
entries), so the Lime was invisible to them no matter how it was configured - -l listed only
The GPS.
As the ADS-B 1090 front-end¶
This sets readsb to --device-type soapysdr --soapy-device driver=lime and restarts it. CoT then
flows through adsbcot > COTBridge as usual. Two readsb bugs that made this path useless are
fixed from 3.16.15-4 onward: --gain was applied ten times too large on the SoapySDR path. A
block was filled with a single readStream() call. This returns at most the driver's stream MTU.
2040 samples on a Lime against a 65536-sample buffer.
The antenna decides this, not the SDR
With both fixes and an outdoor antenna, a LimeSDR Mini v2 decodes ADS-B properly. Measured on a dragonegg box, 70 seconds at gain 40:
- 5,173 usable messages, 711 airborne position reports
- 10 aircraft tracked, all 10 with position - RSSI −42 to −48 dBFS, altitudes 3,200 to 39,000 ft
The same box, same software, on a short indoor whip produced 0-2 usable messages and no positions at all. Nothing changed but the antenna.
So budget for the antenna and its feedline before reaching for anything else. An inline 1090 MHz filtered LNA can still help at a site with strong out-of-band transmitters nearby. But it is not required. It will not rescue a receiver that cannot hear the band in the first place.
An RTL-SDR remains a reasonable choice for a dedicated ADS-B box. It is cheaper. Its front end is already tuned for 1090 - but "the Lime cannot do ADS-B" is not true.
USB stability
The FT601 bridge sometimes drops off the bus under sustained streaming.
repeated reset SuperSpeed USB device messages, then a fallback to high-speed, then a full
disconnect requiring a reboot or re-plug to recover. If a capture stops without explanation,
check lsusb and dmesg before suspecting the decoder. A powered hub is worth trying.
As the ACARS receiver¶
ACARS is the VHF datalink airliners use for operational traffic: flight plans, position and weather reports, engine data, gate requests and crew messages.
That enables two units - acarsdec demodulates VHF, acarscot turns its JSON
into CoT - mirroring the readsb/adsbcot split. Frequencies live in
/etc/default/acarsdec. The defaults are the common US channels.
ARINC-622 is decoded from acarsdec 4.6-snstac2 onward, via
libacars: FANS-1/A ADS-C and CPDLC,
MIAM, OHMA and Media Advisory. Verified on live traffic:
Cathay Pacific talking to Oakland Oceanic. Without libacars that line is an opaque string.
Most ACARS messages have no position
Measured over San Francisco: 0 parseable positions in 48 messages. The payloads were mostly airline engine and maintenance blobs.
So acarscot emits nothing to the map for the majority of traffic, and that
is correct rather than broken - it refuses to invent a position. What ACARS
reliably adds is the operational context ADS-B cannot give you: tail number,
flight ID and route.
Dedicated ACARSCOT TAK egress¶
The normal AryaOS routing remains ACARSCOT to COTBridge to TAK Server. A box
with a dedicated ACARS server identity can deliberately bypass COTBridge by
putting its tak:// enrollment URL in /etc/default/acarscot:
sudoedit /etc/default/acarscot
# COT_URL='tak://com.atakmap.app/enroll?host=takserver.example.com&username=USER&token=TOKEN'
sudo chmod 0600 /etc/default/acarscot
PyTAK resolves that URL to WSS and caches the issued client certificate below the service account's
home. ACARSCOT 0.1.1 and newer create persistent state under /var/lib/acarscot. No local systemd
drop-in is required. PyTAK 7.4.3 and newer retry an unavailable TAK endpoint in the same process
with bounded, jittered exponential backoff. A server outage therefore leaves the unit active instead
of driving a systemd restart storm. Extracted certificate PEMs are removed after every connection
attempt instead of accumulating in tmpfs.
Run sudo systemctl restart acarscot. systemctl is-active acarscot must
remain active even while the server is unavailable. The journal will show the
next retry delay. Once reachable, it must show a resolved
wss://.../takproto/1 destination plus both WSTXWorker and WSRXWorker.
Keep the enrollment URL and cached certificate files private. Do not put either
in a support bundle or source control.
Audio-band decoders (APRS, pagers)¶
aryaos-sdr-fm demodulates NBFM from any SoapySDR receiver and writes PCM to
stdout, which is what direwolf and multimon-ng consume. Without it those
decoders only work with an RTL dongle, since their usual front end is rtl_fm.
# APRS
aryaos-sdr-fm --freq 144.390M --antenna LNAW | direwolf -r 48000 -b 16 -n 1 -
# POCSAG pagers
aryaos-sdr-fm --freq 152.0075M | multimon-ng -t raw -a POCSAG1200 -
# listen (NOAA weather radio)
aryaos-sdr-fm --freq 162.400M | aplay -f S16_LE -r 48000 -c 1
--mode am handles aviation voice and other AM signals. It is experimental,
with a documented level-settling caveat. De-emphasis is off by default because
It distorts AFSK tone balance - use it only when a human is listening.
Tune the output level against the decoder, not by ear: direwolf prints
audio level = N per packet and wants roughly 50.
Remote access (SoapyRemote)¶
Expose the Lime to a remote operator running SDR++, SDRangel or GQRX with the SoapyRemote
plugin (they connect to driver=remote,remote=<host>):
Not enabled by default
SoapySDRServer is not started automatically - it opens the SDR to the network
unauthenticated. Start it manually only when needed, and bind it to the Tailscale/VPN
interface, not the open LAN (see Firewall and
VPN). Stop it when you are done.
Band occupancy survey¶
aryaos-spectrum-survey sweeps a band plan and reports how busy each band is. It answers
"what is active here" without needing a decoder for any of it, which is the question a
wideband box can always answer.
sudo aryaos-spectrum-survey --antenna LNAW --gain 25
sudo aryaos-spectrum-survey --bands fmbcast,adsb1090 --json
fmbcast 98.000 MHz floor= -16.9 dBFS occ= 0.000% OCCUPIED [continuous] carrier +46.7dB @ 97.357 MHz
adsb1090 1090.000 MHz floor= -66.2 dBFS occ= 0.233% quiet [none]
It reports measurements, never identifications. A band being busy does not tell you what is transmitting, and the tool will not guess - the band names are operator context only.
Each band is classified as bursty, continuous, both. Alternatively, neither. This is because
those need different tests. A packetised emitter shows up as excursions above the band's noise
floor. At the same time, a steady carrier is the floor. Has to be found in the frequency domain
instead.
occupied is a convenience flag. The number is the measurement
The OCCUPIED threshold is 0.5% of samples more than 20 dB above the band's own
median. That is tuned for continuously busy bands and it under-reports short bursts.
Measured case: with an outdoor antenna, 1090 MHz surveys at 0.301% - below the
threshold, so it prints quiet - while readsb on the same box at the same time decoded
5,173 messages and tracked 10 aircraft. ADS-B bursts are about 120 µs against a
2-second dwell, so low occupancy is the correct measurement and the boolean is what
misleads. Read occupancy_pct, and compare a band against a known-quiet control rather
than against the flag.
dBFS is not dBm
All levels are relative to the receiver's own full scale, not absolute power. The same signal moved the reported floor from −31.9 to −13.4 dBFS purely by changing gain. Comparing two bands in one sweep is meaningful. comparing to another receiver, or to a published dBm figure, is not.
Pass --zmeta to emit ZMeta OBSERVATION_EVENT
records (newline-delimited JSON) instead of the table, for feeding a metadata bus rather than
A human.
Local capture / analysis¶
Any SoapySDR/LimeSuite-based tool (GNU Radio, SoapySDRUtil, custom collectors) can open the
Lime locally as driver=lime. The specific SIGINT collection/analysis application is chosen
per deployment and is outside the base image.
See also¶
- Radios & SDRs - SDR selection and serials
- Own position (GPS) - the GPS half of dragonegg
- Hardware & requirements - power