A resilient, open-source software modem that turns an SSB transceiver and a sound interface into an adaptive, long-range data link — carrying messages and data with no towers, no internet and no satellites.
Author: Simon Keimer · DC0SK
Architecture
Amateur radio does not map perfectly onto the OSI model, but the model shows where each part of OpenPulseHF lives. A message travels down the stack to the air — and back up on receive at the far station.
Authenticates the remote station over the radio: Ed25519 signed handshake and a trust store (Release 1), plus an experimental post-quantum path (ML-DSA / ML-KEM, not part of Release 1).
Authenticates the operator client that commands the transmitter: a PSK-authenticated, encrypted Noise channel between the daemon and its control clients.
Abstract
OpenPulseHF is a software-defined data modem and messaging stack for HF (shortwave) radio. It turns an ordinary SSB transceiver and a computer sound interface into a digital link that, on HF, can span hundreds to thousands of kilometres — without any intervening infrastructure. At its core is an adaptive modem that measures the channel and steps its speed and modulation up or down a single 14-rung ladder to keep the link alive as propagation changes. Messages are protected by forward error correction on every rung and automatic retransmission; station identities by Ed25519 signatures, with an experimental post-quantum option.
The first release (Release 1, v0.17.0) is deliberately narrow: the
ladder, session compression, the background daemon and CLI, and an ARDOP-compatible TNC for Pat
(Winlink). The much larger rest of the workspace ships disabled or experimental.
Status
A multi-crate Rust workspace whose first release is deliberately narrow: one adaptive rate ladder with FEC on every rung, session compression, the daemon and CLI, and an ARDOP-compatible TNC for Pat. The rest of the stack — KISS/AX.25, Winlink B2F and the CMS gateway, QSY, mesh, relay, repeater, JS8 discovery, file transfer, the post-quantum handshake, GPU kernels and the GUIs — is in the code but ships disabled or experimental. All tests run against a deterministic loopback backend — no audio hardware is required to build or test.
In Release 1: openpulse-daemon (binary openpulse-server),
openpulse-cli (binary openpulse) and the ARDOP TNC
(openpulse-ardop, binary openpulse-tnc); the hpx_hf ladder
SL1–SL14 and its plugins (MFSK16 with its return channel, BPSK, QPSK250-D, OFDM52, FSK4 ACK);
each rung's FEC; session compression; PTT backends; station ID; the signed classical (Ed25519)
handshake.
Not in Release 1 (still in the code; ships disabled or experimental, with no release
gate): mesh, repeater, relay, discovery/JS8, file transfer, QSY, FreeDV auth, Winlink B2F and the
CMS gateway, KISS, the post-quantum handshake, GPU acceleration, the operator panel and TUI, and
the plugins not on hpx_hf (psk8, 64qam,
scfdma, pilot).
Where it stands (15 October 2026): M0 (governance reset) and M1 (v0.17 wire format,
1 October) are done. M2 (release path) is done in code as of 14 October; its on-air follow-ups
— the PTT leader measured at gate G1 and a Raspberry Pi receive-cost re-measure — wait for the
radios. Next is M3: on air on 2 m (IC-9700 ↔ FT-818, 144.640 MHz) — gates G0–G3, the
A1/A2/A4/A5 runs and a Pat session over the ARDOP TNC. HF follows only with the release
candidate; M4 cuts v0.17.0. main still reports version 0.16.0.
Update both ends of a link: v0.16.0 and v0.17 do not talk to each other at all. Every
transmitted frame is now whitened (added after v0.16.0 in #1027, fixed in #1148), the signed
handshake became binary (#1147), and since 10 October (#1062) BPSK63, BPSK100 and BPSK250
(SL3–SL5) send a PN-63 preamble. Profiles changed too: there are now exactly two, fast and
robust; the ten earlier names were deleted with no aliases.
It shipped the OpenPulseHF book and a rewritten operator manual, and replaced acceptance tests that could not fail with real AWGN round-trips. The modem itself was unchanged from v0.15.0. What changed since, for operators: the release notes, Unreleased section.
v0.15.0 — Winlink stack hardening: one live DoS fixed, an aggregate decompression cap, per-operation read deadlines. · v0.4.0 — JS8-based station discovery and rendezvous, signed P2P file transfer, OFDM re-seat and HARQ soft-combining. · v0.3.0 — signed handshake over the air on connect, automatic ADIF logbook, opt-in ARDOP adaptive ARQ, generic serial CAT. · v0.2.1 — CE-SSB transmit envelope conditioning for the high-PAPR multicarrier modes.
hpx_hf ladder, SL1–SL14fast and robustWhy OpenPulseHF
Only the first three are part of Release 1. The others are implemented and tested in simulation, but ship disabled or experimental.
hpx_hf, SL1–SL14: MFSK16 → coded BPSK → differential QPSK250-D → OFDM52 up to
64QAM with high-rate LDPC (r≈8/9), ~9 bps to 7.7 kbps. ACK/NACK- and SNR-driven. Part of
Release 1.
Soft LLR values accumulate across retransmissions (PACTOR-style Memory-ARQ), turning each retry into a soft combining gain. Part of Release 1.
Controlled-Envelope SSB (Hershberger W9GR, QEX 2014) — a voice-SSB technique — applied as a per-mode TX conditioner on the QPSK-subcarrier OFDM modes (OFDM52 on the ladder; the denser OFDM and all SC-FDMA modes are excluded). +1.18 dB average power measured on air at fixed PEP (FT-991A, 2 m). Part of Release 1, default on.
ML-DSA-44 + ML-KEM-768 in the ConReq/ConAck frames; Hybrid mode signs with both Ed25519 and ML-DSA-44. Not part of Release 1.
DFT-spread OFDM with DFT-CE pilot channel estimation and MMSE equalisation. The frequency-interleaved pilots limit the realised PAPR advantage; OFDM, not SC-FDMA, is the high-throughput path. Not part of Release 1.
PILOT-* modes up to DVB-S2 32APSK: known in-band pilots drive carrier tracking
instead of a decision-directed loop. Not part of Release 1.
wgpu compute kernels (BPSK modulate/demodulate, timing search, RRC FIR matched filter,
256-pt FFT, soft LLR demod) with automatic CPU fallback, behind the gpu feature.
Not part of Release 1.
A signed QSY_REQ/LIST/VOTE/ACK protocol, verified against the handshake peer, with initiator and responder in the daemon and rig CAT via rigctld. Not part of Release 1.
Trust-weighted multi-hop relay with query propagation, mesh re-broadcast with authenticated beacons, and a cross-band repeater. Not part of Release 1.
A native JS8 waveform (8-GFSK, LDPC(174,87)) to find OpenPulse stations on the JS8 calling
frequency, marked by an @OPULSE hint, and negotiate a working frequency. Off by
default; RX-only until the operator opts into TX. Not part of Release 1.
Offer/accept transfer with an inline signed TransferManifest + SHA-256 checked
against the peer's handshake key; a failed check quarantines the file. Not part of
Release 1.
Ed25519-signed beacons for FreeDV voice via the FreeDV GUI's UDP data port
(openpulse-freedv-auth) — no FreeDV fork. Not part of Release 1.
On-air status: a first rig-to-rig decode on 2 m passed on 28 July 2026 (phase A1, on the pre-v0.17 format), and the CE-SSB power and spectrum checks were made on real RF. The Release 1 on-air campaign (M3) has not run yet. Fade performance on HF is validated in the Watterson simulator only.
Modulation modes
hpx_hf ladder, ~9 bps to 7.7 kbpsRelease 1 has one rate ladder, SL1–SL14, with forward error correction on every rung.
An adaptive session walks it up and down, driven by ACK/NACK feedback and per-level SNR gates.
Adaptive operation is opt-in: [modem] ota_enabled = true on the daemon (default fixed
mode BPSK250), [ardop] enable_adaptive_arq = true on the ARDOP TNC.
| SL | Mode | Plugin | FEC | Net bps | SNR floor (dB) | Declared BW (Hz) | Preamble |
|---|---|---|---|---|---|---|---|
| Sub-floor · deep-fade rung | |||||||
| 1 | MFSK16 | mfsk16 | RS | ~9 | none | 500 | 7-symbol Costas sync |
Coded single carrier · ≤ 500 Hz · the robust profile tops out at SL6 | |||||||
| 2 | BPSK31 | bpsk | RS | 27 | 3 | 62.5 | 32-symbol --++ run · initial level |
| 3 | BPSK63 | bpsk | RS | 54 | 4 | 125 | PN-63 (x⁶+x+1) since #1062 |
| 4 | BPSK100 | bpsk | RS | 87 | 4.5 | 200 | PN-63 (x⁶+x+1) since #1062 |
| 5 | BPSK250 | bpsk | RS | 219 | 5 | 500 | PN-63 (x⁶+x+1) since #1062 |
| 6 | QPSK250-D | qpsk | RS | 437 | 7 | 500 | Designed 16-symbol sequence · differential QPSK |
| OFDM52 · 52 subcarriers · ~2 031 Hz · soft-concatenated FEC | |||||||
| 7 | OFDM52 | ofdm | soft-concatenated | 1 264 | 9 | 2 031 | Schmidl-Cox symbol |
| 8 | OFDM52-8PSK | ofdm | soft-concatenated | 1 895 | 10 | 2 031 | Schmidl-Cox symbol |
| 9 | OFDM52-16QAM | ofdm | soft-concatenated | 2 527 | 12 | 2 031 | Schmidl-Cox symbol |
| 10 | OFDM52-32QAM | ofdm | soft-concatenated | 3 159 | 14 | 2 031 | Schmidl-Cox symbol |
| 11 | OFDM52-64QAM | ofdm | soft-concatenated | 3 790 | 16 | 2 031 | Schmidl-Cox symbol |
| OFDM52 · high-rate LDPC (r≈8/9) | |||||||
| 12 | OFDM52-16QAM | ofdm | LDPC high-rate | 5 141 | 18 | 2 031 | Schmidl-Cox symbol |
| 13 | OFDM52-32QAM | ofdm | LDPC high-rate | 6 426 | 19 | 2 031 | Schmidl-Cox symbol |
| 14 | OFDM52-64QAM | ofdm | LDPC high-rate | 7 710 | 20 | 2 031 | Schmidl-Cox symbol |
The BPSK rungs carry RS(255,223), upgraded per frame to RS(255,191) where that
costs no extra RS block. ACKs go out on FSK4-ACK (fsk4, 300 Hz) or, when the receiver
recommends SL1, on the MFSK16 return channel. "Declared BW" is what the plugin reports and what
ARDOP ARQBW sizes against; for the single-carrier modes it is a conservative
2 × baud. Wire break: v0.16.0 and v0.17 do not talk to each other on any rung —
update both ends of a link.
| Profile | SL range | Top mode | Occupied BW | Use when |
|---|---|---|---|---|
| fast (default) | SL1–SL14 | OFDM52-64QAM + LDPC r≈8/9 | up to ≈2 031 Hz | Good conditions, a 2.4 kHz SSB filter, a linear PA |
| robust | SL1–SL6 | QPSK250-D | ≤ 500 Hz | Poor conditions or limited gear (narrow filters, small or non-linear PAs); single-carrier, all coded |
robust is the same ladder capped at SL6, so a fast and a
robust station interoperate; the robust side never climbs past SL6. The ten earlier
profile names (hpx500, hpx_modcod, hpx_pilot*,
hpx_ofdm_hf, hpx_wideband*, hpx_narrowband, …) were deleted
on 1 October 2026 with no aliases; hpx_hf is now fast, and any other name
is rejected with the list of valid names. The ARDOP TNC floors its adaptive ladder at SL2 (it has
no MFSK16).
| Plugin | Modes | Notes |
|---|---|---|
| bpsk | BPSK250-RRC | Off-ladder; keeps the --++ preamble |
| qpsk | QPSK125, QPSK250, QPSK500, QPSK500-D, QPSK1000, QPSK1000-HF, QPSK2000, plus -RRC variants | QPSK250 is coherent; the ladder uses the differential QPSK250-D |
| ofdm | OFDM16 | 16 subcarriers, QPSK; LS + ZF |
| psk8 | 8PSK500, 8PSK1000, 8PSK1000-HF, plus -RRC variants, 8PSK2000-RRC | Gray-coded |
| 64qam | 64QAM500, 64QAM1000, 64QAM2000-RRC | Up to 12 000 bps gross; 64QAM2000-RRC needs a very clean channel |
| scfdma | SCFDMA16, SCFDMA26-{8PSK,16QAM,32QAM}, SCFDMA52, SCFDMA52-P2, SCFDMA52-LP, SCFDMA52-{8PSK,16QAM,32QAM,64QAM}, SCFDMA52-64QAM-P4 | DFT-CE + MMSE; SCFDMA52-P2 and -LP are low-PAPR demonstrators (-LP is flat-channel CE only and mis-decodes on selective channels) |
| pilot | PILOT-{QPSK,8PSK,16QAM,32APSK} at 500/1000 baud and -RRC, plus 2000-RRC | Pilot-framed single carrier; up to DVB-S2 32APSK |
| js8 | JS8-SLOW, JS8-NORMAL, JS8-FAST, JS8-TURBO, JS8-ULTRA | 8-GFSK; discovery only, RX-only by default |
These are registered and selectable as fixed modes ([modem] mode), but
are not on the Release 1 ladder and carry no release gate; openpulse modes prints the
live registry. At 2000 baud use the -RRC variants: plain rectangular
QPSK2000 is registered but RRC-superseded, and 8PSK2000 is no longer
advertised (#1359), because the crossfade pulse is ISI-limited at 4 samples/symbol. The 9600-baud
QPSK9600-RRC / 8PSK9600-RRC waveforms keep their code but are not
advertised: they need ≥ 38.4 kHz audio and the engine runs at 8 kHz (post-1.0).
The full rationale for the ladder, its FEC choices and its floors is in docs/mode-fec-ladder.md.
Who it's for
Operator interfaces
In Release 1 you drive the modem from the CLI against the background daemon
(openpulse-server), or from Pat through the ARDOP TNC (openpulse-tnc). The
GUIs are not part of Release 1; two of them are developer tools.
| Interface | Release 1 | Description |
|---|---|---|
CLIopenpulse | yes | transmit, receive, modes, devices, mode-advisor, adaptive, arq, benchmark, monitor,
calibrate, config init, and daemon … to control a running
openpulse-server. |
Operator panelopenpulse-panel | no | iced GUI on the daemon control port: controls, spectrum, waterfall, rate ladder, messages, event log. |
TUIopenpulse-tui | no | ratatui terminal UI: HPX state, AFC/rate meters, DCD energy bar, transitions log. |
Twin-station viewopenpulse-twinview | dev tool | egui viewer over two daemons, so both link directions show at once. |
Signal testbenchopenpulse-testbench | dev tool | egui TX / channel / mixed / RX views with waterfall, spectrum and scatter. |
Quick start
The test suite runs entirely against a deterministic loopback backend. No audio hardware, radio or station is required to build or test.
# Toolchain preflight (required: rustc >= 1.97.1) ./scripts/check-toolchain.sh # Build (requires libasound2-dev on Linux for the CPAL audio feature) cargo build --workspace # Test suite — no audio hardware required cargo test --workspace --no-default-features # Lint cargo clippy --workspace --no-default-features --all-targets -- -D warnings cargo fmt --all -- --check # Workspace gate (quote results only from its GATE: line) scripts/gate.sh # Benchmark regression gate cargo run -p openpulse-cli --no-default-features -- --backend loopback --log error benchmark run # Automated mode × channel test matrix (outputs to docs/test-reports/) cargo run -p openpulse-testmatrix --no-default-features
A purely rig-less demo to get a first impression of the GUIs and the adaptive modem. None of it needs a radio or sound card, and none of it transmits.
# Two-station ARQ link simulator GUI — simulates BOTH stations over a virtual # HF channel. Change SNR / channel model / mode live and watch the ladder adapt. cargo run -p openpulse-linksim --features gui --bin openpulse-linksim-gui # With the operator panel: --serve makes the simulator speak the daemon control # protocol. Press Run in the simulator, then Connect in the panel (default 127.0.0.1:9000). cargo run -p openpulse-linksim --features "gui,serve" --bin openpulse-linksim-gui -- --serve 127.0.0.1:9000 cargo run -p openpulse-panel # in another terminal
For on-air operation, build the daemon or TNC with audio (--features cpal
on openpulse-daemon and openpulse-ardop; the CLI has it by default) plus
the PTT feature your backend needs (serial for RTS/DTR, gpio), and
configure ~/.config/openpulse/config.toml — see the
example
config, the operator
manual and the on-air
test plan.
Interoperability
OpenPulseHF is GPL v3, but these interfaces let non-GPL software interoperate without
GPL obligations — each sits behind a process or network protocol boundary. Note that plugins which
statically link openpulse-core are derivative works and must be GPL-compatible; the only
supported path for a proprietary DSP backend is a separate process bridged over one of these
interfaces.
| Interface | Transport | Release 1 | Description |
|---|---|---|---|
ARDOP TNCopenpulse-tnc | TCP 8515/8516 | yes | ARDOP ASCII command protocol, as used by Pat — including GRIDSQUARE, ARQBW, ARQTIMEOUT, CWID, SENDID and PING |
| Daemon control port | NDJSON / TCP 9000 | yes | Full event stream + control commands; operator panel and scripting |
| Daemon WebSocket | JSON / WS 9001 | yes | Same protocol as the TCP port; browser and Electron clients |
| CLI subprocess | stdin / stdout | yes | NDJSON output; invocation crosses the process boundary |
KISS/AX.25 TNCopenpulse-kisstnc | TCP 8100 | no | Standard KISS framing, AX.25 UI frames |
Winlink CMS gatewayopenpulse-gateway | B2F / TCP 8772 | no | Outbound-only client to cms.winlink.org (CMS is proprietary); B2F Type D (gzip) only — Type C/LZHUF is unsupported and inbound Type C proposals are rejected |
| PKI REST API | HTTP/JSON 8080 | no | Trust-bundle and key-management endpoints; read-only routes unauthenticated |
Supporters
For providing access to a remote-controllable station for testing.
For helping stuff the budget sink-hole.
For providing space on their booth at HAM RADIO 2026.
OpenPulseHF is free and open source under GPL v3. If it is useful to you, a donation funds sustained time on the modem, on-air testing, and better documentation — and keeps the whole stack open, auditable and vendor-independent.