Changes applied to the Raspberry Pi 5 raspi / kraken-rig (the KrakenSDR DoA + wifi-hunter host) on 2026-08-26, after the Pi crashed twice (OOM) then wouldn’t boot: (1) reboot-safe zram swap (OOM fix), (2) brought up the hunter-ap WiFi AP, (3) retuned DoA to the TETRA mobile-uplink carrier at 382.114 MHz on a 200 mm-radius pentagon — applied live and verified (DoA UI HTTP 200), (4) installed gpsd for a phone/USB GPS path. Also captures the canonical in-car field-deployment plan decided this session.

For Agents

Host = raspi / 192.168.1.200 (eth0), tailnet node kraken-rig. ssh levander@192.168.1.200 (passwordless sudo). 2 GB Pi 5 — the KrakenSDR DAQ is memory-heavy, hence the OOM history. DoA UI over the AP: http://10.42.0.1:8080/doa (the :8080 is REQUIRED). The DoA config below is the LIVE current state — SDR connected, config applied to _share/settings.json, UI verified 200. It supersedes the earlier config-only 433.92 MHz keyfob draft (which was never live).

Boot recovery — the SD card had been pulled

The Pi wouldn’t boot because the SD card was physically absent. Reinserting it fixed booting immediately.

The card was NOT corrupted

Verified clean after reinsertion: bootfs FAT checked clean, ext4 root mounted rw with no EXT4 errors in dmesg. The non-boot was purely the missing card — do not reflash / assume corruption when a headless Pi won’t boot; check the card is seated first.

The two crashes earlier in the day are consistent with OOM on the 2 GB Pi 5 running the KrakenSDR DAQ — which is what change 1 addresses.

Change 1 — reboot-safe zram swap (OOM crash fix)

Added compressed RAM swap so the memory-heavy DAQ has headroom instead of getting OOM-killed.

sudo apt install zram-tools
# /etc/default/zramswap:
#   ALGO=zstd
#   PERCENT=50
#   PRIORITY=100
sudo systemctl enable --now zramswap.service

Result: /dev/zram01 GB zstd-compressed swap, priority 100, active (verified with sudo zramctl + cat /proc/swaps; ~110 MB in active use).

zramswap.service failed at BOOT with "Device or resource busy" — needed a reset drop-in

On reboot the unit tried to re-init a zram device that was already set up → Device or resource busy and the service failed. Fix = a systemd drop-in that swaps off and resets zram0 before the unit initialises it:

# /etc/systemd/system/zramswap.service.d/reset.conf
[Service]
ExecStartPre=-/sbin/swapoff /dev/zram0
ExecStartPre=-/bin/sh -c 'echo 1 > /sys/block/zram0/reset'
sudo systemctl daemon-reload
sudo systemctl restart zramswap

After this the service comes up clean on every boot; 1 GB zstd zram confirmed active.

Restarting a live zram device by hand still reports "failed"

systemctl restart zramswap on an already-active device (without the drop-in path) reports failed even though /proc/swaps shows zram0 still working — you can’t re-init a live zram device. The drop-in above is what makes the boot path reliable.

Change 2 — hunter-ap WiFi AP up

Brought up wifi-hunter’s NetworkManager AP on wlan0:

sudo nmcli connection up Hotspot
  • wlan0 in AP mode, IP 10.42.0.1/24, SSID hunter-ap, password huntme123, autoconnect yes / priority 100.
  • DoA UI reachable over the AP at http://10.42.0.1:8080/doa (the :8080 is required).

eth0 (wired) and wlan0 (AP) run SIMULTANEOUSLY

The single-radio AP-vs-CLIENT constraint is only about wlan0. eth0 + wlan0-AP are fully independent — on the bench the Pi keeps its wired uplink / tailnet AND exposes hunter-ap at the same time. The constraint only bites in the field: when eth0 is unplugged and you want wlan0 to be a client of a phone hotspot, it then can’t also be the AP. (This corrects the loose “AP and client can’t both be active” phrasing — that limit is intra-wlan0, not eth0-vs-wlan0.)

The DoA is now tuned to the TETRA mobile UPLINK — where handsets transmit — at 382.114 MHz, which is 10 MHz below the 392.114 MHz base downlink carrier found earlier. Applied to /home/levander/krakensdr/krakensdr_doa/_share/settings.json on 2026-08-26, SDR connected, verified live (DoA UI returns HTTP 200).

Final working settings.json values

KeyValueWhy
center_freq / vfo_freq_*382.114 MHz / 382114000TETRA mobile-uplink carrier (10 MHz below the 392.114 base downlink) — where handsets TX
uniform_gain19.7 dBdialed back from 38.6 to stop overdrive on the strong TETRA signal — tune just below where the overdrive flag trips
vfo_bw_025000 Hzmatches the TETRA 25 kHz channel
ant_arrangementUCAuniform circular array
ant_spacing_meters0.2200 mm array radius (see array note below)
doa_methodMUSIC
doa_decorrelation_methodOff
en_optimize_short_burstsTrueTETRA uplink is bursty / trunked
array_offset0UNCALIBRATED — bearings are relative until offset-calibrated against a known bearing
location_sourcegpsdGPS enabled; idle until a phone is USB-tethered and streaming NMEA into gpsd

uniform_gain — overdrive corrupts the DoA, not just the spectrum

“Overdrive” = ADC clipping, which corrupts the phase the DoA solver depends on → garbage bearings, not merely a clipped waterfall. On this strong TETRA signal 38.6 dB clipped; 19.7 dB sits just below the overdrive flag. Always tune gain to the highest value that does NOT trip overdrive.

Array geometry — 200 mm radius (CORRECTED)

The array radius is 200 mm (0.20 m), NOT the earlier "25 cm" / "30 cm" numbers

The rig now uses the 200 mm hole on the KrakenSDR printed pentagon spacer template (its holes are at 100 / 150 / 200 / 250 mm). 200 mm radius is the official KrakenSDR default spacing multiplier s ≈ 0.33 across this band.

Per the official antenna wiki: interelement spacing Iₑ = s·λ must stay < 0.5 λ (else ambiguity), ideal 0.2–0.5, KrakenSDR uses s = 0.33. Radius for a regular n-gon: r = s·λ / √(2(1−cos(360/n))), which for n = 5 is r = s·λ / 1.176.

At s = 0.33 this puts r ≈ 220 mm at 382 MHz and ≈ 194 mm at 434 MHz → the 200 mm hole is the correct default for the whole 382–434 band. Set ant_spacing_meters = 0.2 to match.

Whip extension — ext-1 covers BOTH bands, no swap needed

Antenna extension ext-1 (366–950 MHz) covers 382 AND 433, so no whip change is needed between the TETRA-uplink target and any 433 target. (ext-3 = 290–420 centers 382 slightly better for SWR, but for a coherent DF array where all 5 elements are identical any common extension is fine — antenna-length is common-mode and doesn’t bias bearings.) Per-band coverage: 382 / 392 / 433 all fall in ext-1’s 366–950 MHz range.

Why the earlier “rebuild to 25 cm for 433” note was superseded

The prior draft chased a 433.92 MHz keyfob and said “rebuild the pentagon to 25 cm radius.” That was never applied. The 200 mm radius is unambiguous for both 382 and 433 (spacing 1.176 × 0.20 = 0.235 m stays under λ/2 = 0.346 m at 434, and = 0.393 m at 382). The 30 cm array’s ~428 MHz ambiguity ceiling (element spacing 1.176 × 0.30 = 35.3 cm must stay < λ/2) remains a correct fact about that old geometry — but the fix is the 200 mm template hole, not 25 cm.

Band antenna math (reusable)

Bandλλ/4 whipλ/2 (max spacing)ext for whip200 mm spacing (1.176 × 0.2 = 0.235 m)
382 MHz (TETRA uplink)78.5 cm19.6 cm39.3 cmext-1 (or ext-3 for SWR)s = 0.30 ✅
392 MHz (TETRA downlink)76.5 cm19.1 cm38.2 cmext-1s = 0.31 ✅
433.92 MHz (keyfob)69.1 cm17.3 cm34.6 cmext-1s = 0.34 ✅

Change 4 — gpsd installed for the phone/USB GPS path

Installed gpsd + gpsd-clients on the Pi so the DoA (location_source = gpsd) can read a GPS fix. It stays idle until a GPS source feeds it NMEA (phone over the USB tether, or a USB GNSS puck — see the field plan).

sudo apt install gpsd gpsd-clients

Field-deployment plan (canonical in-car setup)

Decided this session — the target layout for driving with the rig.

Dual-channel networking — tether an ANDROID phone

USB-tether an Android phone → the Pi gets mobile data on usb0, leaving wlan0 free to stay the hunter-ap AP. This dodges the intra-wlan0 AP-vs-client conflict entirely (no need to flip wlan0 to client). An ESP32 is NOT a second Pi NIC — it’s a microcontroller, not a Linux netdev. If not tethering, the alternative is a USB WiFi dongle as the second interface.

GPS options, best first

  1. BEST: a cheap USB GNSS puck (u-blox VK-172 / VK-162) straight into the Pi — gpsd reads it directly, no phone-app hassle.
  2. Phone GPS over the tether: an Android app (e.g. “Share GPS” / GpsdForwarder) streams NMEA over the USB tether → gpsd on the Pi → DoA reads gpsd.
  3. iOS/iPad is a poor GPS source — WiFi-only iPads have no GPS chip, and iOS locks down NMEA sharing.

iPad = the in-car DISPLAY only

The iPad connects to hunter-ap and opens the DoA UI in Safari at http://10.42.0.1:8080/doa. It is not used for data or GPS.

  • Antennas mounted on the car roof (ground plane); coax equal-length within ~1 cm to every element.