Live RX decode runs from sdr-decode.py via remote KiwiSDR. Each row = one band+mode sweep. Newest first.
| Time (UTC) | Band | Dial (kHz) | Mode | Decodes | Grids | Best SNR | Receiver |
|---|---|---|---|---|---|---|---|
| Loading… | |||||||
Each row is a real tune to a known frequency with the live SNR over the local noise floor. This is ground-truth: if it shows here, the RSP1A is hearing it right now.
| Freq (MHz) | Service | SNR (dB) | Signal | Floor (dB) |
|---|---|---|---|---|
| Loading… | ||||
Coarse 88–470 MHz sweep, strongest signal per band. Detections include noise — treat max-SNR as the real indicator, not the count.
| Band / Service | Max SNR (dB) | Peak | Detections | Example (MHz) |
|---|---|---|---|---|
| Loading… | ||||
Full-spectrum waterfall browser UI with live demodulation. Exclusive with the Snapshot Tuner — only one can hold the RSP1A at a time.
When started, the host sdrplay_apiService is stopped and the container takes over the USB device.
admin — password in
/var/lib/jarvis/secrets/openwebrx_admin_password
Decodes Mode S / ADS-B from aircraft transponders and plots them on a live OpenStreetMap (SDRplay dump1090). Exclusive with the Snapshot Tuner and OpenWebRX — only one can hold the RSP1A at a time. Note: the HF antenna is inefficient at 1090 MHz, so expect high-altitude / nearby traffic only until a dedicated 1090 MHz antenna is fitted.
Tune the RSP1A to any frequency and capture a short live audio clip + spectrum. RX-only. One capture at a time (single-tuner device).
knowledge/radio/CONVENTIONS.md · FTS5 full-text search is live — try a frequency, callsign, mode, or gear name.
| Time UTC | RX | SNR | Callsign | Grid | Type | Frame |
|---|---|---|---|---|---|---|
| Loading… | ||||||
| Time UTC | Callsign | Freq (kHz) | Band | SNR | Message body |
|---|---|---|---|---|---|
| Loading… | |||||
| Time UTC | Band | Dial | RX | SNR | Callsign | Grid | Type | Frame |
|---|---|---|---|---|---|---|---|---|
| Loading… | ||||||||
| Callsign | Grid | Decodes | Last heard |
|---|---|---|---|
| Loading… | |||
WWV (Fort Collins, CO) and WWVH (Kauai, HI) broadcast UTC on
2.5 / 5 / 10 / 15 / 20 / 25 MHz (WWVH omits 20 & 25). Received AM off a remote KiwiSDR and
verified by deterministic DSP — scripts/wwv-decode.py. The
station is confirmed by its 1-second tick train (5 ms burst of 1000 Hz WWV /
1200 Hz WWVH at the top of every second, phase-folded to a z-score), corroborated by the
1000/1200 Hz spectral line; the 100 Hz subcarrier carries the
BCD time code and the 0.8 s minute marker anchors second :00. Which bands
decode = a live propagation ground-truth to Colorado (and Hawaii).
| Freq | Status | Station | Tick z | Duty | Line | Subcarrier 100 Hz | Min marker | SNR proxy | Receiver | Time (UTC) |
|---|---|---|---|---|---|---|---|---|---|---|
| Loading… | ||||||||||
Higher bands (15/20/25 MHz) are daytime-propagation; expect them quiet after dark. Re-sampled by the
wwv-sweep cron; newest capture per frequency shown.
scripts/sdr-decode.py --band 40m --mode ft8 --find
scripts/sdr-decode.py --list
scripts/wwv-decode.py --all --find
scripts/wwv-decode.py --freq-mhz 10 --find
scripts/sdr-decode.py --list
Native-Linux OFDM HF modem (Rhizomatica / HERMES, GPL-3.0). Drop-in VARA HF replacement —
identical TCP TNC on ports 8300/8301, so pat / Winlink work
unchanged — and it carries Reticulum mesh over HF via a KISS broadcast port. Replaces
Wine + VARA on the FZ-M1 field tablet (native binary, free, auditable).
Mercury is a per-session binary — not a persistent service. Status below reflects validated readiness when binary is present. Activate with the commands in the Reference Config cards below.
Principle (Rob 2026-07-11): be as robust as possible — independent modems, graceful fallback, one pat client switching transports by connect-URL scheme.
Email-reach order: VARA HF (future, Wine — widest RMS) → ardopcf (open, common on RMS) → Mercury (open, Linux-native, emerging RMS) → JS8Call (keyboard, last-ditch).
Fully-open / grid-down order: ardopcf → Mercury → JS8Call. Full matrix + failure-mode analysis: knowledge/radio/tools/hf-modem-comparison.md.
Plan: plans/mercury-winlink-buildout.md. Build state above is
machine-verified; the phase checklist tracks project progress (updated per session).
mercuryv2, root Makefile, no cmake)mercury-build-deps op — apt deps + headers verified/usr/bin/mercury via mercury-install op — verified -l (6 modes)/radio Mercury sub-tab + live status endpointpat at Mercury TCP TNC 8300/8301VARA modem initialized → VARA version 4.9.0 registeredTCPClientInterface (DATAC1, kiss_framing) — loopback PASS 2026-07-14: KISS frame decoded, 510 B DATAC1, PTT ON/OFF confirmedardopcf on live path (SNR floor + goodput per mode) · gate: General licenseplans/mercury-reticulum-bridge.md §7pat's VARA TNC at Mercury — same TCP interface. Then start Mercury with the audio/rig flags.config.json → "varahf": { "addr":"localhost:8300", "bandwidth":2300, "rig":"", "ptt_ctrl":false }
mercury -i plughw:0,0 -o plughw:0,0 -R 3073 -A /dev/ttyUSB0
ardop TNC on 8515 (data auto 8516). Start ardopcf with real audio + PTT (pat drives PTT via hamlib when ptt_ctrl:true).config.json → "ardop": { "addr":"localhost:8515", "arq_bandwidth":{"Max":500}, "rig":"", "ptt_ctrl":false }
ardopcf 8515 plughw:1,0 plughw:1,0 -p /dev/ttyUSB1
connect ardop:///RMSCALL · WebGUI: -G 8514
/var/lib/jarvis/rns-env.
⚠ Part-97: RNS encrypts — EMCOMM-waiver or Part 90 only. Full bridge design: plans/mercury-reticulum-bridge.mdsource /var/lib/jarvis/rns-env/bin/activate && mercury -m 0 -i plughw:1,0 -o plughw:1,0 -b 8100 &
rnsd --config ~/.config/reticulum -v &
config: TCPClientInterface kiss_framing=True target_port=8100 bitrate=980
| Model | CER | WER ▲ | Heavy-QSB WER | Gate | Speed |
|---|---|---|---|---|---|
| DSP baseline | 0.259 | 0.452 | 0.670 | — | ~0s |
| qwen3.5:9b ★ winner | 0.230 | 0.345 | 0.491 | ✗ −5% | ~14s |
| gemma3:12b | 0.272 | 0.362 | 0.521 | ✗ | ~9s |
| qwen3:1.7b | 0.398 | 0.497 | 0.739 | ✗ | ~2s |
| Model | CER | WER ▲ | Heavy-QSB WER | Gate |
|---|---|---|---|---|
| qwen3.5:9b Ph1 winner | 0.216 | 0.348 | 0.497 | ✗ +6% |
| gemma3:12b · qwen3:1.7b | A/B incomplete — background eval SIGKILLed on session resume (both were worse than qwen3.5:9b in Ph1, so non-decisive) | |||
| Stratum | n | CER | WER | Field |
|---|---|---|---|---|
| Overall | 20 | 0.259 | 0.452 | 0.625 |
| QSB none/light | 8 | 0.000 | 0.125 | 1.000 |
| QSB heavy (≥0.4) | 12 | 0.431 | 0.670 | 0.375 |
| Tight fist (<0.7×) | 3 | 0.416 | 0.889 | 0.667 |
| Normal fist | 17 | 0.231 | 0.375 | 0.618 |
| WPM slow (<15) | 3 | 0.118 | 0.222 | 1.000 |
| WPM fast (>25) | 8 | 0.216 | 0.296 | 0.688 |
cw-phase1-eval.pycw-phase15-eval.pycw-eval.py --fade-awarecw-keying-tape.py --n-tones Ncw-eval.py --two-sendercw-gold-append.py) + downtime cron (02:10 ET) + focus-lock wiredcw-2sender-model-eval.py/var/lib/jarvis/data/cw-corpus-2c/ (seeds 50k–60k; 0 errors)POST /api/radio/cw-decode backend · per-sender copy+confidence + auto-gold + P4 rx-obs sidecar (2026-07-21)[?] honest abstentions as substitution errors. A DP-based fade-aware ruler
(cw-eval.py --fade-aware) exempts [?]/[fade] tokens aligned to
real words in heavy-QSB samples, keeping the strict WER track alongside.
| Gate criterion | Value | Threshold | Status |
|---|---|---|---|
| heavy-QSB WER(fa) | 0.4688 | ≤ 0.469 | ✓ margin 0.0002 |
| clean WER vs DSP baseline | 0.125 | ≤ DSP baseline | ✓ tied |
| rel. WER reduction heavy-QSB | 30.0% | ≥ 30% | ✓ exact |
find_tones() FFT peak-picking → per-tone narrowband envelope → independent
keying tapes. Scored on 40 synthetic 2-sender mixes across 4 noise variants. Cross-contamination
is zero across all conditions — frequency-domain separation is solid.
| Variant | n | Recall (mean) | Contamination | WER (mean) |
|---|---|---|---|---|
| Overall | 40 | 0.8875 | 0.0000 | 0.122 |
| Clean (SNR 30 dB) | 10 | 1.000 | 0.0 | 0.0 |
| Low-SNR (8 dB) | 13 | 1.000 | 0.0 | 0.0 |
| QSB on one sender (0.6) | 7 | 0.679 | 0.0 | 0.408 |
| QSB on both senders (0.5) | 10 | 0.775 | 0.0 | 0.201 |
| Variant | n | DSP recall | Model recall | Δ recall | Contam | WER |
|---|---|---|---|---|---|---|
| Overall | 40 | 0.8875 | 0.9000 | +0.0125 | 0.0000 | 0.054 |
| Clean (30 dB) | 10 | 1.000 | 1.000 | — | 0.0 | 0.0 |
| Low-SNR (8 dB) | 13 | 1.000 | 1.000 | — | 0.0 | 0.0 |
| QSB one sender (0.6) | 7 | 0.679 | 0.750 | +0.071 | 0.0 | 0.158 |
| QSB both senders (0.5) | 10 | 0.775 | 0.775 | 0 | 0.0 | 0.104 |
| Gate criterion (QSB variants, n=17) | Value | Threshold | Status |
|---|---|---|---|
| QSB callsign recall (model stage) | 0.7647 | ≥ 0.850 | ✗ gap 0.085 → 2C |
| Cross-contamination (model stage) | 0.0000 | = 0.0 | ✓ |
/var/lib/jarvis/data/cw-corpus/gold-real.jsonl.
cw-gold-append.py — verified-decode → gold corpus with adjudicator guard (opus/human/rob only; model's own output never trains)cw-phase2-downtime fires 02:10 ET · focus-lock 2.5 h · defers non-essential Ollama crons/radio CW tab → "Decode WAV" form: WAV path + sender count + band/dial → POST /api/radio/cw-decode → per-sender copy+confidence displayed · auto-gold appends high-conf decodes to corpuscw-eval-CHANGELOG.jsonl/var/lib/jarvis/data/ (service path constraint). Allow ~30–60 s.
The K3NG Arduino Nano generates all Morse timing; a single transistor wired as a
low-side switch shorts the G90 KEY line to ground to key the transmitter.
One key line + shared ground is the whole interface (ptt=0 — the G90 does its own T/R).
Full spec & sources: plans/cw-keyer/hardware-assembly.md.
| Part | Value | Note |
|---|---|---|
| Arduino Nano | ATmega328P / CH340 | Have it · K3NG, D11, ptt=0 |
| Transistor (pick one) | 2N2222 NPN or 2N7000 MOSFET | Same wiring · 2N3904 also OK |
| Series resistor | 1 kΩ ¼W | base/gate |
| Pulldown resistor | 10 kΩ ¼W | control→GND · recommended (NPN) / required (MOSFET) |
| Cable to rig | 3.5 mm TRS | matches G90 KEY jack |
| Board + wire | perfboard, solder, heatshrink | or in-line "dead-bug" |
The G90 holds its KEY line at a small positive voltage (internal pull-up), low current. Keying = pull that line to ground. The Nano's 5 V logic can't do that directly across an isolated rig, so the transistor does the pulling: D11 HIGH turns it on and grounds the Tip; D11 LOW turns it off and the line floats back up.
The 10 kΩ pulldown matters for safety: while the Nano resets, D11 floats high-impedance — the pulldown holds the transistor off so the rig can't be keyed by accident. False keying = unwanted TX, exactly what the on-air gate guards.
Nano D11 ──[ 1 kΩ ]──●──────────► B/G (Base / Gate — control)
│
[ 10 kΩ ] ← pulldown: holds Q OFF while the Nano resets
│
Nano GND ────────────●──────────► E/S (Emitter / Source — common)
│
└──── common ground ───────► G90 Sleeve
G90 Tip + Ring (jumpered) ───────► C/D (Collector / Drain — switched)
Q = ONE transistor. 2N2222 / 2N3904 (NPN): B/G=Base C/D=Collector E/S=Emitter
2N7000 (N-MOSFET): B/G=Gate C/D=Drain E/S=Source
(B/G, C/D, E/S are three SEPARATE pins — never wire them to each other.)
KEY-DOWN: D11 HIGH → Q conducts → C/D↔E/S closes → G90 Tip pulled to GND → TX keys
KEY-UP: D11 LOW → Q off → Tip floats (rig's pull-up) → no key
TO-92, flat face toward you, legs down, left→right:
| Part | Pin1 | Pin2 | Pin3 |
|---|---|---|---|
| P2N2222A / PN2222A (NPN) | E | B | C |
| 2N3904 (NPN) | E | B | C |
| 2N7000 (MOSFET) | S | G | D |
| Contact | Signal | Connect to |
|---|---|---|
| Tip | DIT (dot) | jumper together → Collector/Drain |
| Ring | DAH (dash) | |
| Sleeve | Ground | common GND |
Left side Right side
(top → bottom) (top → bottom)
───────────── ──────────────
D13 (LED) D12
3V3 ★ D11 (MOSI) ← KEY LINE to 1 kΩ
REF D10 (SS)
A0 D9
A1 D8
A2 D7
A3 D6
A4 D5
A5 D4
A6 D3
A7 D2
5V ★ GND ← common ground (transistor E + G90 Sleeve)
RST RST
★ GND ← (also GND) RX0
VIN TX1
★ = pins used in this build
D11 is the 2nd pin from top on the RIGHT side.
GND is the 12th pin from top on the right, and
the 14th pin from top on the left.
D11 is the K3NG key line (tx_key_line_1 11). It goes HIGH for the duration
of each Morse element and drives the transistor Base through the 1 kΩ resistor.
GND ties to the transistor Emitter and to the G90 Sleeve — all three must share a single node. Use either GND pin (right-side pin 12 is convenient when both D11 and GND are on the right).
Part in hand: TO-92 marked 2N2222 A331 → pinout is E-B-C (flat face toward you, legs down, left→right).
This schematic uses those pin names directly — no generic B/G aliases.
Nano D11 ──[ 1 kΩ ]──●──── Base (B) ← pin 2 of TO-92 (center leg)
│
[ 10 kΩ ] ← pulldown: Base → GND, holds transistor OFF while Nano resets
│
Nano GND ────────────●──── Emitter (E) ← pin 1 of TO-92 (left leg, flat face toward you)
│
└──── common GND ──────── G90 KEY jack Sleeve (3.5 mm)
G90 KEY Tip + Ring (jumpered) ─── Collector (C) ← pin 3 of TO-92 (right leg)
TO-92 orientation: ┌─────┐ flat face toward you, pins pointing down
│ 2N2222│
└─┬─┬─┘
E B C ← left=E, center=B, right=C
KEY-DOWN: D11 HIGH → B-E forward biased → C-E conducts → G90 Tip pulled to GND → TX keys
KEY-UP: D11 LOW → 10k pulls B to GND → transistor off → Tip floats → no key
⚠ Some off-brand TO-92 "2N2222" are ECB, not EBC. Always buzz it out with a meter
in diode mode first: B→E and B→C both read ~0.6 V forward; reverse both read OL.
Date code A331 follows P2N2222A spec (EBC) — trust the meter, not the label.
Multimeter in continuity/Ω across TRS Tip↔Sleeve (rig unplugged):
hostx scripts/cw-keyer/cwctl status → onair:false.cwctl send "E" 5 → brief close (a dit) then open.cwctl send "R" 5 → di-dah-dit, three clean closures.ptt=0 flashed (P6 prereq, 05598ec4).All local mirrors served from this dashboard — accessible on LAN without internet.
| Resource | Local mirror | External (canonical) |
|---|---|---|
| Xiegu G90 operation manual KEY jack pinout p.9; CW mode p.11–14 |
✅ G90 manual PDF | manualslib.com |
| 2N2222A / P2N2222A datasheet ON Semiconductor — TO-92 pinout + absolute max ratings |
✅ 2N2222A PDF | onsemi.com |
| Arduino Nano pinout diagram Official Arduino.cc — all 30 pins labelled |
✅ Nano pinout PNG | arduino.cc |
| EI4LF G90 CW primer Straight-key workaround, QSK/semi-break-in setup |
⚠ Unavailable (obriain.com parked/404 as of 2026-08-12) | Wayback Machine |
| K3NG CW keyer firmware Repo config + feature flags (flashed commit 05598ec4) |
scripts/cw-keyer/README.md |
github.com/k3ng |
| kHz | Station | UTC Window | ET Window | Days | Language | Target | SE-US Note |
|---|