Mercury — HERMES HF Modem
Mercury is part of the HERMES project (High-Frequency Emergency and Rural Multimedia Exchange System) by Rhizomatica, funded by ARDC and others.
There are currently two versions:
- Mercury v2 (this branch) — a complete rewrite in C with a new ARQ data link. This is the recommended version.
- Mercury v1 — the original Mercury modem written in C++. Legacy; use only if you know what you are doing.
A Qt-based GUI is available: mercury-qt
Mailing list: https://lists.riseup.net/www/info/hermes-general
Mercury v2 is a complete rewrite of the HERMES modem ARQ data link, replacing the monolithic state machine with a modular reactor architecture featuring per-direction mode selection, hybrid SNR + delivery-feedback gear-shifting, split control/data channel design (DATAC13 for signaling, DATAC4/DATAC3/DATAC1 for payload), and a persistent FreeDV mode pool eliminating codec re-initialization overhead. Built for reliable store-and-forward email and file transfer over HF radio links in rural and emergency scenarios.
What this software does
- ARQ data link for P2P sessions with connect/accept handshake, ACK/retry logic, keepalive, and controlled disconnect.
- Adaptive payload "gear-shifting" (DATAC4/DATAC3/DATAC1) driven by link quality and backlog, with DATAC13 used for control signaling.
- Per-direction mode selection: each path (A→B and B→A) negotiates its mode independently based on local SNR.
- Broadcast data mode in parallel to ARQ, with dedicated broadcast framing and TCP ingress port.
- VARA-style TCP TNC interface with separate control and data sockets (base port and base+1), including commands/status like
MYCALL,LISTEN,CONNECT,BUFFER,SN, andBITRATE. - Audio modem operation over multiple backends (
alsa,pulse,oss,coreaudio,aaudio,dsound,wasapi,shm,null,fifo) with split RX/TX modem orchestration. - Direct radio control via HAMLIB or HERMES shared-memory interface for direct PTT keying.
Usage modes:
./mercury -m [mode_index] -i [device] -o [device] -x [sound_system] -p [arq_tcp_base_port] -b [broadcast_tcp_port] -f [freedv_verbosity] -H [hamlib_log_level] -k [rx_input_channel] [-G] [-T] [-U ui_port] [-W] [-C config_file]
./mercury [-h -l -z]
Options:
-c [cpu_nr] Run on CPU [cpu_nr]. Use -1 to disable CPU selection, which is the default.
-m [mode_index] Startup payload mode index shown in "-l" output. Used for broadcast and idle/disconnected ARQ decode. Default is 1 (DATAC3).
-s [mode_index] Legacy alias for -m.
-f [freedv_verbosity] FreeDV modem verbosity level (0..3). Default is 0.
-H [hamlib_log_level] Hamlib radio log level (0..6). Default is 0.
-k [rx_input_channel] Capture input channel: left, right, or stereo. Default is left.
-i [device] Radio Capture device id (eg: "plughw:0,0").
-o [device] Radio Playback device id (eg: "plughw:0,0").
-x [sound_system] Sets the sound system or IO API to use: alsa, pulse, oss, coreaudio, aaudio, dsound, wasapi, shm, null or fifo. Default is alsa on Linux, dsound on Windows.
null and fifo are developer/test backends; fifo uses raw s32le PCM at 8 kHz via -i/-o paths.
-p [arq_tcp_base_port] Sets the ARQ TCP base port (control is base_port, data is base_port + 1). Default is 8300.
-b [broadcast_tcp_port] Sets the broadcast TCP port. Default is 8100.
-G Enable UI communication (WebSocket status/spectrum/command interface for mercury-qt). Off by default.
-T Use WSS (WebSocket Secure/TLS) for UI communication. Requires -G. Default uses plain WS (no TLS).
-U [ui_port] Sets the UI port (WebSocket port). Default is 10000. Requires -G.
-W Disable waterfall/spectrum data sent to the UI (saves CPU). Requires -G.
-l Lists all modulator/coding modes.
-z Lists all available sound cards.
-v Verbose mode. Prints more information during execution.
-L [path] Write log to file (TIMING level and above).
-J Write log file in JSONL format (requires -L).
-R [radio_model] Sets HAMLIB radio model.
-A [radio_address] Sets HAMLIB radio device file or ip:port address.
-S Use HERMES shared memory radio control (Linux-only; do not use with -R and -A).
-K List HAMLIB supported radio models.
-C [path] Path to INI configuration file (default: mercury.ini in the current directory).
-t Test TX mode.
-r Test RX mode.
-h Prints this help.
Mode behavior notes:
-m/-saffects broadcast and test modes only.- During an active ARQ link, control frames use DATAC13 and ARQ payload starts in DATAC4 (then may adapt to DATAC3/DATAC1).
- VARA
BW500blocks DATAC1;BW2300andBW2750both allow the full Mercury payload-mode ladder. CALLadvertises the local BW token andACCEPTreturns the negotiated session token. If either side usesBW500, the link stays narrow;BW2750is preserved inCONNECTED ... BWonly when both peers advertise it.FSK_LDPCis currently experimental (mainly for lab/test usage), may have longer decode/sync latency depending on setup, and is not recommended for production links yet.
Radio control notes:
- With no
-R,-A, or-S, Mercury does not key the radio directly; it leaves the radio keying task to the TCP client. -Rselects a HAMLIB model ID,-Aoptionally points HAMLIB at a device path orip:port, and-Kprints the available HAMLIB models.-Sselects the HERMES shared-memory controller interface, is mutually exclusive with-A, and is unavailable on Windows builds.
Getting Started with Mercury
Compile and Install from Git repository on Linux
-
Install the dependencies:
sudo apt-get update && sudo apt-get install build-essential pkg-config libasound2-dev libpulse-dev libhamlib-dev make git -
Clone Mercury GitHub repository:
git clone https://github.com/Rhizomatica/mercury.git
-
Change directory to
mercury/: -
Build mercury (edit
config.mkfirst if you need a custom compiler or flags; defaults are fine for most): -
Install system-wide:
The FreeDV codec is vendored in-tree — no external FreeDV or codec2 packages are needed.
Build targets
| Target | Description |
|---|---|
make / make all |
Build the standalone CLI mercury binary |
make libmercury_core.a |
Compile all Mercury C objects into a host-arch static library (used by fyne-ui) |
make fyne-ui |
Build the single-binary native GUI (mercury-ui), embedding the engine via CGo. Requires make libmercury_core.a first. |
make libmercury_core_w64.a |
Cross-compile Mercury C objects for Windows x64 (requires gcc-mingw-w64-x86-64) |
make fyne-ui-windows |
Cross-compile the single-binary GUI to mercury-ui.exe. Depends on libmercury_core_w64.a. |
make windows-installer |
Full Windows installer pipeline: cross-compile engine + GUI, copy DLLs, patch config. Output goes to windows-installer/. Requires MinGW + Go. |
make windows-zip |
Build standalone CLI + GUI Windows binaries and zip them for distribution |
make fyne-ui-macos |
Package Mercury.app for the host architecture (run on macOS; needs the fyne tool). |
make fyne-ui-macos-dmg |
Wrap the host-arch Mercury.app in a drag-to-install Mercury.dmg at the repo top level. |
make fyne-ui-macos-universal-dmg |
Distribution build: universal (x86_64 + arm64) Mercury.app wrapped in Mercury.dmg at the repo top level. This is the artifact for the website. |
Build the Fyne GUI (Linux)
sudo apt install golang-go libhamlib-dev libpulse-dev libasound2-dev make fyne-ui ./mercury-ui
Cross-compile the Windows installer (from Linux)
sudo apt install golang-go gcc-mingw-w64-x86-64 mingw-w64-x86-64-dev make windows-installer # → windows-installer/ contains mercury-ui.exe + DLLs + mercury.ini # Use Inno Setup Compiler on Windows to build the .exe installer from installer.iss
The FreeDV codec is vendored in-tree — no external FreeDV or codec2 packages are needed.
Build the macOS app for distribution (universal .dmg)
Run on macOS. This produces the self-contained, universal (Intel + Apple
Silicon) Mercury.dmg used for the website — hamlib and libusb are vendored as
static universal libraries in-tree, so no Homebrew dependencies are needed at
build or run time.
# one-time tools: brew install go go install fyne.io/tools/cmd/fyne@latest export PATH="$PATH:$HOME/go/bin" # so `fyne` is on PATH (add to ~/.bash_profile) make fyne-ui-macos-universal-dmg # → ./Mercury.dmg (universal: x86_64 + arm64)
The finished Mercury.dmg lands at the repository top level, ready to upload.
It is unsigned, so on first launch Gatekeeper warns — right-click the app →
Open to run it. For a quick host-architecture-only build use
make fyne-ui-macos-dmg (also emits ./Mercury.dmg).
Install via Debian package on Linux
For now just Debian 13 (Trixie) packages are built, for both arm64 (works on both RaspberryPi OS and Debian) and amd64.
-
Install the repository certificate:
wget --no-check-certificate -qO- https://debian.hermes.radio/hermes/hermes.key | gpg --dearmor -o - | sudo tee /etc/apt/trusted.gpg.d/hermes.gpg > /dev/null
-
Add mercury to the sources list (change it for your current architecture):
ARM64:
echo 'deb [arch=arm64] http://debian.hermes.radio/hermes trixie main' | sudo tee /etc/apt/sources.list.d/hermes.list
AMD64:
echo 'deb [arch=amd64] http://debian.hermes.radio/hermes trixie main' | sudo tee /etc/apt/sources.list.d/hermes.list
-
Update the Debian packages:
-
Install mercury:
sudo apt-get install mercury
Note: Installation via Debian package requires Debian 13 (Trixie)
Install on Windows
Via installer (recommended): Download the Mercury_HF_Modem_Setup.exe from the releases page, run it, and the single-binary GUI (mercury-ui.exe) will be installed. The GUI launches the Mercury engine in-process — no separate backend needed. Desktop and Start Menu shortcuts are created automatically.
Via ZIP: Download the ZIP package from the releases page, extract it, and run mercury-ui.exe for the GUI or mercury.exe for the headless CLI.
Install on macOS
Download Mercury.dmg from the website, open it, and drag Mercury into
Applications. The build is universal (runs natively on Intel and Apple
Silicon) and self-contained (no Homebrew needed). It is unsigned, so on first
launch right-click Mercury → Open to get past Gatekeeper.
Configuration File
Mercury reads an INI-format configuration file at startup. The default path is mercury.ini in the current working directory; use -C to specify an alternative path. Command-line arguments take priority over values from the file.
See the included mercury.ini.example for all available settings and their default values — copy it to mercury.ini and edit as needed.
Documentation
Online HTML docs: https://rhizomatica.github.io/mercury/
Logging and timing traces
- Default run (
./mercury): logger runs at INFO level with timestamps ([INF]/[WRN]/[ERR]). - Verbose run (
./mercury -v): logger runs at DEBUG level and includes all detailed ARQ/modem traces ([DBG]and[TMG]). ./mercury -v -L /tmp/session.log— write full DEBUG+TIMING log to file../mercury -v -L /tmp/session.log -J— same, but in JSONL format for machine parsing withjq.- TX state transitions are logged with timestamps at INFO level as:
TX enabled (PTT ON)TX disabled (PTT OFF)
See docs/ARQ.md for full ARQ architecture, protocol reference, and OTA tuning guide.
Reticulum
Mercury can carry Reticulum mesh networking over HF via its KISS-over-TCP broadcast port (verified) or as a point-to-point ARQ backbone. See docs/RETICULUM.md for the integration architectures and configuration.
Physical Layer
Mercury v2 currently uses FreeDV modulator code developed by David Rowe. We plan to introduce other modulator modes present in Mercury v1.
Graphical Interfaces
Mercury v2 has three interfaces:
- Built-in Fyne UI — a single-binary GUI embedded in the engine via CGo (this repository,
gui_interface/fyne-ui/). Shows waterfall/spectrum, telemetry, and controls. Build withmake fyne-ui(Linux) ormake windows-installer(Windows cross-compile). - Mercury-qt (desktop): https://github.com/Rhizomatica/mercury-qt
- Web-based: located in
docs/app/in this repository, and accessible via https://rhizomatica.github.io/mercury/app/
Also, community interfaces also exist:
- Mercury-tk: https://github.com/odorajbotoj/mercury-tk/
About
Mercury v2 is developed by Rhizomatica's HERMES team, namely:
- Rafael Diniz (ARQ, Broadcast, TCP interface, etc)
- Pedro Messetti (Testing framework, general improvements, etc)
- Matheus Thibau (Graphical User Interface)
This project is sponsored by ARDC.
LICENSE
Mercury is free software, licensed under the GNU General Public License, version 3 or (at your option) any later version (GPL-3.0-or-later). See the LICENSE file and the per-file headers.
Mercury bundles third-party components which carry their own licenses when taken separately:
modem/freedv— a subset of FreeDV / codec2, LGPL-2.1 (see LICENSE-freedv)common/iniparser— MIT (see common/iniparser/LICENSE)audioio/ffaudioandffbase— Unlicense / public domain (see audioio/ffaudio/UNLICENSE)- Windows binary releases link against Hamlib — LGPL-2.1 (see radio_io/hamlib-w64/COPYING.LIB.txt)
The combined work (the Mercury binary) is distributed under the terms of the GPL-3.0-or-later. The LGPL/MIT/Unlicense terms apply to those components only when they are used separately from Mercury.