v1.1.0 Β· Live on the FT-710 station

Headless FT8 Server
Real DSP. Real QSOs.

MRRC-FT8 runs the WSJT-X 3.0.2 Improved DSP core (FT8/FT4 decode and encode) behind a supervised worker, with a Python FastAPI control plane, rigctld radio control and a landscape mobile Web cockpit. Point a browser at it β€” decode the band, pick a station, and let the sequencer close one standard QSO.

Live Cockpit Explore Features GitHub

Join the MRRC-HAM WeChat group

Scan the QR code with WeChat to join the MRRC-HAM group.

WeChat QR code for MRRC-HAM group

QR code valid until Aug 29, 2026. Will be updated when refreshed.

Windows Desktop Installer

Windows 11/10 x64 β€” embedded Python runtime + WSJT-X DSP decoder + Hamlib rigctld, no manual Python or Node.js install. First run generates a self-signed HTTPS certificate; default password abcd1234; LAN/remote access over HTTPS.

MRRC_FT8-Setup.exe β€” v1.1.0 Stable

60 MB Β· Windows 11/10 x64
SHA-256: ee866947731ac2318f4c1c7dc14511ba8a67b67ffbb68a2f32c8d9a3f68318b2

Full MRRC-FT8 station: FastAPI server + FT8 DSP (decode/encode) + rigctld + web/desktop client. Windows build: 832 tests passed; installer boot and serve verified. Radio/audio RF acceptance remains an operator-side check on a physical station.

Download for Windows Versioned v1.1.0

WSJT-X DSP, Server-Grade

The same improved decoder you know from WSJT-X, wrapped in a headless server with safe, deterministic scheduling.

WSJT-X 3.0.2 Improved DSP

Compiled selected Improved ft8var sources behind a C ABI into a shared library. FT8 standard + Improved decoding with OpenMP Auto/1–12 sub-bands, FT4 following the FT8 acceptance.

  • Relocatable build β€” no vendor tree in production
  • Deterministic sub-band partition across OpenMP teams
  • FT8 174-bit messages, exact LDPC encode/decode

Supervised DSP Worker

Fortran/OpenMP state lives only in a dedicated worker process behind supervised IPC. Every DSP call goes through one binding lock β€” no cross-thread packjt77 races, no Python callbacks from OpenMP threads.

  • Crash-restart supervision with clean state recovery
  • Global DSP lock serialises all ctypes calls
  • Process boundary isolates native memory

UTC Slot Discipline

Every schedule decision uses floor(epoch/TRperiod) β€” the only slot identity rule. RX, TX, decode and sequencing all align to the same absolute UTC slot grid.

  • UtcRing keyed by absolute sample index
  • No drift, no eviction misalignment
  • 15 s FT8 / 7.5 s FT4 periods

Sequencer + PTT Safety

Human selects the target β€” the sequencer drives one standard QSO to completion on the correct opposite TX slot, with a manual reply decision window, fit guard and central PTT safety controller.

  • Opposite-slot phase computed from decode slot parity
  • Fit guard: 12.64 s waveform fits the 15 s slot
  • Any authenticated session can STOP TX instantly

rigctld Radio Control

The serial port has exactly one owner: rigctld. The server talks CAT over loopback TCP β€” frequency, mode, PTT β€” with no module ever opening the serial device directly.

  • Single serial owner β€” no interleaved CAT corruption
  • Loopback TCP, unix-socket option
  • PTT via rigctl / hamlib commands

Durable QSO Logging + ADIF

Every completed QSO lands in the canonical SQLite store with start/band context captured at sequencer completion, plus ADIF export for your favourite logging suite.

  • SQLite canonical QSO/audit/history records
  • Dial frequency β†’ ADIF band name mapping
  • on_qso fires once at completion, not per leg

DXCC Live View

Track worked entities straight from the cockpit: a full-screen stats overlay parses the bundled cty.dat (exact, digit-replacement and longest-prefix lookup) and summarises total / per-entity / per-band β€” recomputed only after a QSO write, never on every open.

  • 186 entities from the real 10k-row log
  • Cache-until-QSO-write, no push/poll
  • New-DXCC CQs highlighted in purple

New-DXCC Auto-Call

With the Auto-call toggle on, the server safety-arms and auto-QSOs the first new-DXCC CQ when idle β€” no control lease needed, never interrupts an in-flight QSO, and completed entities naturally stop re-triggering.

  • Pure-function candidate filter + audit trail
  • Interlock rejections skip, never crash
  • Setting persisted server-side

3 kHz Waterfall

The FT8 passband is ~0–3 kHz, so the spectrum frame emits exactly the 3 kHz span β€” no blank half-canvas, half the payload, and the client maps every bin across the full waterfall.

  • Display bandwidth configurable
  • Validated within 0..rate/2
  • Band selector: 7/14/21/28 MHz

Landscape Mobile Cockpit

A mobile-first PWA built for a phone lying in landscape next to the rig: full control in landscape, degraded observer/STOP view in portrait. No build step β€” vanilla JS modules.

  • One control lease across many observer sessions
  • 5 s heartbeat / 15 s TTL lease renewal
  • WebSocket auth via secure cookie, Caddy TLS edge

FT-710 Field Sessions

v1.1.0 is running the real FT-710 station. The 2026-08-03 field session closed the RX/TX root causes β€” UTC-ring eviction misalignment, Replay opposite-slot phase and the manual-Reply decision window β€” and real FT8 QSOs are being worked through the mobile cockpit. On 2026-08-04 the FT-710 rig controls were completed: filter bandwidth and ATT / PREAMP / AGC / RF gain now switch through raw CAT frames (hamlib's FT-710 level path is unreliable, and the AGC AUTO code was wrong), verified live on the station radio. v1.1.0 adds the DXCC live view (cty.dat entity stats, cached until a QSO write), the new-DXCC auto-call toggle (first new-entity CQ auto-worked when idle), and JTDX ADIF auto-sync with a 7-day LOG window.

MRRC-FT8 web cockpit β€” live FT8 band activity and decodes on 14.074 MHz (20 m), with TX state and slot timing

The live cockpit: 20 m FT8 band activity and decodes on 14.074 MHz, TX state and slot timing.

Open Live Cockpit Source Code

Live edge: https://radio.vlsc.net:9988 β€” Caddy TLS with acme.sh DNS-01 certificates; FastAPI and rigctld stay loopback-only.

Build and Run in Five Steps

macOS or Linux. Requires gfortran-mp-13 + FFTW3f for the DSP core, then a plain Python venv for the server.

1 Β· Build the DSP Core

CMake builds the Fortran shim + selected Improved sources into wsjt_core.

cmake -S dsp -B dsp/build -DCMAKE_Fortran_COMPILER=gfortran-mp-13 && cmake --build dsp/build -j

2 Β· Install Python Environment

Editable install pulls FastAPI, uvicorn, numpy and the test deps.

python3 -m venv venv && venv/bin/pip install -e '.[dev]'

3 Β· Set OMP_STACKSIZE

Improved sync8var allocates large work arrays on the thread stack β€” 10 MB, set before NumPy/OpenMP load.

export OMP_STACKSIZE=10M

4 Β· Configure and Launch

Set MRRC_FT8_* env vars (password hash via --hash-password, call, grid, audio device, allowed hosts).

venv/bin/python -m server.main

5 Β· Point a Browser at It

Open the cockpit, log in, select a station and run a QSO. Full deployment templates (Caddy, LaunchAgent, systemd) live in deploy/.

https://your-host:9988 β†’ login β†’ band select β†’ work the pileup

Production Deployment

The app binds loopback-only; Caddy owns the public edge. macOS LaunchAgent and Linux systemd units plus Caddy configs live in deploy/.

1 Β· Generate the password hash

Argon2id hash for MRRC_FT8_PASSWORD_HASH β€” prompts so the password never lands in shell history.

venv/bin/python -m server.main --hash-password

2 Β· Configure secrets

A .env (or the service env file) must define the hash, call sign and grid; optional overrides for allowed hosts and the audio device. The server refuses to start without the first three.

MRRC_FT8_PASSWORD_HASH=… MRRC_FT8_MY_CALL=YOURCALL MRRC_FT8_MY_GRID=…

3 Β· Run as a service

macOS: copy deploy/com.mrrc.ft8.plist to ~/Library/LaunchAgents/ and bootstrap it. Linux: install deploy/mrrc-ft8.service, create /etc/mrrc-ft8.env, and systemctl enable --now mrrc-ft8.

launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.mrrc.ft8.plist

4 Β· Public edge (Caddy)

Copy deploy/Caddyfile to /etc/caddy/ and run Caddy as a LaunchDaemon; set MRRC_FT8_ALLOWED_HOSTS to the same domain so the Host/Origin checks pass.

reverse_proxy 127.0.0.1:8000

5 Β· Deploy the website

This site ships from the repo β€” website/deploy.sh backs up, uploads to www.vlsc.net/mrrc_ft8 and reloads nginx.

cd website && ./deploy.sh

What You Need

Any CAT-capable HF rig rigctld (hamlib) macOS / Linux gfortran-mp-13 + FFTW3f Python 3.11+ CMake β‰₯ 3.16 Modern mobile browser Caddy TLS edge (optional)

Layered, Supervised, Deterministic

Browser β†’ TLS edge β†’ FastAPI control plane β†’ supervised DSP worker β†’ shared native library. One owner per resource.

Browser Landscape Mobile Cockpit Waterfall Β· Decode Feed Vanilla JS Β· PWA FastAPI Main auth Β· lease Β· UTC Β· audio Β· rig sequencer Β· PTT safety SQLite Β· ADIF Β· waterfall DSP Worker supervised IPC binding lock Β· packjt77 OpenMP teams 1–12 HF Radio FT-710 field station CAT via rigctld USB audio 48 kHz HTTPS / WSS Caddy TLS edge supervised IPC loopback /WScontrol JSON Β· select / reply / CQ /WSstate decode feed Β· band Β· lease /WSwaterfall 3 kHz span bins /WSqso sequencer Β· QSO log events
DSP Pipeline
12 kHz int16 mono in
ft8var OpenMP decode
Supervised worker β†’ wsjt_core β†’ decodes; TX waveform always 48 kHz
UTC Slot Grid
floor(epoch / TRperiod)
Absolute slot identity
UtcRing keyed by absolute sample index; no eviction drift
Control Lease
Many observers, one control
5 s heartbeat / 15 s TTL
Any authenticated session may STOP TX; lease required to start
Public Edge
Caddy :80/:443 only
acme.sh DNS-01 TLS
FastAPI + rigctld loopback-only; secure-cookie WebSocket auth

DSP, Timing, Safety β€” by the Numbers

12 kHz
Decoder Input
int16 mono β€” the only allowed rate
48 kHz
TX Waveform
Fixed TX rate domain
15 s
FT8 Slot Period
floor(epoch/TRperiod) identity
1–12
OpenMP Sub-Bands
Deterministic partition
3 kHz
Waterfall Span
FT8 passband, no blank half
40
Regression Tests
ft8sim/ft4sim synthetic signals

Technology Stack

WSJT-X 3.0.2 Improved Fortran + OpenMP FastAPI + Uvicorn asyncio rigctld / hamlib SQLite ADIF Caddy TLS PortAudio capture Vanilla JS PWA

15 SDD Chapters

Full IBM TeamSD-style System Design Description β€” requirements, architecture decisions, safety analysis and version history.

AD-001 ... AD-015

AD-001 Headless Layered Server AD-002 WSJT-X C ABI AD-003 Supervised DSP Worker AD-004 Fixed Audio Rate Domains AD-005 Single DSP Owner + Lock AD-006 UTC Epoch Slot Discipline AD-007 Sequencer/PTT Safety AD-008 rigctld Serial Owner AD-009 Caddy Edge + Cookie Auth AD-010 Single Control Lease AD-011 Landscape-First Cockpit AD-012 Human Selection, Single QSO AD-013 Separate Bounded Streams AD-014 SQLite + ADIF AD-015 Native Service Deployment

Division of Work and Contracts Left Behind

What this project loads before editing, what a human still signs off, and what stays readable afterwards.

01 Β· Loaded before editing
AGENTS.md with a 14-Rule Registry (Hook Not Wired)
AGENTS.md, constraints.json (14 rules), the pre-edit hook ships as an installable snippet (install_hooks.py) and is not wired in this checkout, so no automated pre-edit gate is claimed here.
02 Β· Human sign-off
On-Air Decoding and UTC Slots
Decoding fidelity under real band conditions, and the UTC slot timing judgements, stay with a human at the station.
03 Β· Contract left behind
vendor-readonly as a Contract
wsjtx-3.0.2/ stays read-only and changes go through dsp/patched/, so the delta against the vendor tree remains reviewable.