Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Build and test

CI and local development run the same cargo xtask commands.

Prerequisites

ToolVersion
Rust1.99.1, pinned in rust-toolchain.toml; rustup installs it
Node26
pnpm12, exact version in web/package.json
FFmpeg9, built by scripts/build-media.py
NativeC/C++ compiler, Clang/libclang, CMake, GNU Make, NASM, Python 3.12+
sudo apt-get install -y build-essential cmake clang libclang-dev python3 nasm   # Debian, Ubuntu
brew install cmake python nasm                                                  # macOS

Fuzzing and cargo xtask sanitize also need a nightly toolchain. SoapySDR loads at runtime, so no development package is needed.

Build and run

git clone https://github.com/Newspicel/sdrmm.git
cd sdrmm
python3 scripts/build-media.py
pnpm --dir web install --frozen-lockfile
pnpm --dir web build
cargo run -p sdrmm

Open http://localhost:8080.

The media script builds the few FFmpeg 9.0.2 codecs SDRmm needs, plus fdk-aac 2.0.3 for xHE-AAC, from checksummed source into .media/<target> as shared libraries. For the host target it also writes .media/cargo.toml, which .cargo/config.toml includes, so every Cargo command uses that FFmpeg (FFMPEG_DIR, plus LD_LIBRARY_PATH on Linux or DYLD_LIBRARY_PATH on macOS). On Windows, also add $FFMPEG_DIR/bin to PATH. A system FFmpeg older than 9 does not compile. For cross builds, pass --target <triple> to the script. Nix uses its own FFmpeg with fdk-aac.

The server embeds web/dist, so build the frontend first. Without it, the server shows a placeholder page.

For hot reload of the frontend and automatic backend restarts:

cargo xtask dev --watch

Open http://localhost:5173. Vite forwards API and WebSocket traffic to port 8080. Without --watch, the backend runs once.

Windows

Build from a Visual Studio developer shell with LLVM and MSYS2 Make installed; the CI Rust action in .github/actions/rust shows the setup. Set MEDIA_SHELL_BIN to the folders holding bash, make, and clang-cl, and LIBCLANG_PATH to libclang. The script adds those folders only for the tools it runs, so MSYS2's link.exe never hides the MSVC linker.

ARM64 builds the codecs without assembly, because FFmpeg's ARM assembler tools do not ship with the toolchain. xtask retries Cargo up to three times on Windows, since the ffmpeg-sys-the-third build script sometimes crashes loading libclang (issue 145).

Feature flags

The server enables soapy, sdrplay, aaronia, cr8, rtlsdr, hackrf, airspy, airspyhf, funcube, espsdr, ad936x, net-client, and gpu-fft by default. Packaged releases leave out cr8. dvsi adds AMBE vocoder chips and is off by default.

cargo run -p sdrmm --no-default-features                        # no radio drivers
cargo run -p sdrmm --no-default-features --features net-client  # network radios only

Recording playback and the signal generator work in every build.

Test without a radio

Add a Signal generator node, pick a signal, and wire it to a matching channel and a Speaker.

Debug builds also offer synthetic radios on the Device node.

RadioKeyIs
Kraken bench ×5kraken5Five lanes on one clock with a noise source, like a KrakenSDR
Coherent Array ×4array4Four lanes on one clock and LO, with a pilot tone
Dongle 1, Dongle 2dongle1, dongle2One lane each, two elements of a line array; Dongle 1 holds the noise source
Test bandbandA band of test signals on one lane
Receiver ×4, Transceiver 2×2, Half-duplex 1×1quad, transceiver, halfduplexLane and transmit shapes

The bench radios hear one FM emitter at 137° and its echo at 250°. Hardware tests cover real radios.

Checks

CommandRuns
cargo xtask checkFormat, Clippy, web and site lint and type-check, crate rules, feature-set builds, generated-file drift
cargo xtask testGenerated fixtures, then Rust, web and site tests, with SoapySDR hidden
cargo xtask smokePlaywright against a real sdrmm process
cargo xtask perfDSP, channel and engine throughput and allocation gates
cargo xtask auditRustSec advisories through cargo-deny
cargo xtask desktopClippy and tests on the desktop app, no installers
cargo xtask sanitizeChannel tests, with the vendored C, under AddressSanitizer and UBSan
cargo xtask fuzzlibFuzzer on decoders, parsers and importers
cargo install --locked cargo-nextest cargo-deny cargo-fuzz
pnpm --dir web exec playwright install chromium

test needs cargo-nextest, audit cargo-deny, fuzz cargo-fuzz, and sanitize clang. Automated tests never need real hardware.

fuzz runs every target in fuzz/fuzz_targets/, or the one --target names, for --seconds (default 60) on --jobs workers. --minimize shrinks the corpus instead.

TargetFeeds
channel_chainIQ into every decoder
channel_settingsRandom settings into every decoder
dv_voiceIQ into the digital voice decoders
media_bitstreamAudio codec bitstreams
net_radio_protortl_tcp, SpyServer, KiwiSDR and SDRconnect messages
iqlink_headeriqlink packets
recorder_importSigMF archives
workspace_importWorkspace exports
cps_imageCodeplug images

CI runs check, perf, smoke, test, GPU parity, SIMD kernels, the ETSI table check and cargo-deny's licenses, bans and sources on every push and pull request. test builds once per architecture (test archive) and runs the archive in four slices (test shard). The nightly workflow runs the rest: macOS tests, array realtime, desktop, mobile, iPhone, Android, container image, Nix, advisories, sanitizers, four hours of fuzzing per target, and measurement suites.

Performance baselines

Throughput gates compare a measurement with a committed baseline:

GateBaseline
ber::perf::tests::compare_perf_baseline in sdrmm-modem-test-supportcrates/modem/baselines/perf_phase0.json, rows tagged by host
compare_*perf_baseline* in sdrmm-modem --test integrationcrates/modem/baselines/*/*perf*.json, rows tagged by host
survey_every_channel_search_path in sdrmm-channelscrates/channels/baselines/channel_perf/<arch>-<os>.json

Every row carries the host it was measured on, <arch>-<os>. Gates compare only the rows of the host they run on and fail when they find none, so a new runner architecture cannot quietly switch them off.

Record on a quiet machine, in release, and commit the files:

export SDRMM_WRITE_BASELINES=1
cargo test -p sdrmm-modem-test-support --release -- --ignored --exact ber::perf::tests::write_perf_baseline
cargo test -p sdrmm-modem --release --test integration -- --ignored --test-threads=1 --skip compare_ perf_baseline
cargo test -p sdrmm-channels --release --test integration -- --ignored --exact channel_perf::write_perf_baseline

For a CI host, dispatch the Perf baseline workflow (or push to the perf-baseline branch), download its artifacts, and commit them. Recording replaces the rows of the host that measured them and keeps every other host's.

Each baseline also stores a reference kernel measurement, so a faster or slower machine of the same host shifts the expectation instead of failing the gate. Run a gate on a machine that is otherwise idle: the kernel is cache resident and barely notices a busy host, while a decoder reading tens of megabytes does.

The modem gate allows 10% below the expectation. The channel survey allows 35%: it compares every channel at once, and ft8, ft4, wspr and stanag are timed over one pass whose deferred decode may or may not land inside the window.

Generated files

Regenerate and commit these with the change that caused them:

When you changeRunUpdates
REST routes or wire typescargo xtask codegenopenapi.json, web/src/generated/
Dependenciescargo xtask licensesTHIRD_PARTY_NOTICES.md, crates/server/data/notices.json, phone notices
Band-plan sourcescargo xtask bandplancrates/server/data/bandplan/
assets/icon.svgcargo xtask iconsDesktop, iPhone, web, site, and docs icons
Demo scenes in web/e2e/scenes.tspnpm --dir web demo:recordsite/public/demo/
README screenshotscargo xtask screenshotsassets/screenshots/

cargo xtask check catches stale codegen and notices.

cargo xtask fixtures writes the synthesized decoder SigMF pairs into fixtures/. Git ignores them, and cargo xtask test regenerates them.

Other tasks

CommandDoes
cargo xtask excerptTrims a capture into a fixture; see fixtures/README.md
cargo xtask net-captureRecords a public KiwiSDR or SpyServer to SigMF
cargo xtask replayRuns a capture through one channel; --center sets the RF centre of a raw file, --images saves pictures
cargo xtask ber <entry>Bit error rate curves into target/ber
cargo xtask ident-matrixSignal identifier against the fixtures
cargo xtask compare <dsp|decoders|apps>Compares with other SDR software; --ours measures SDRmm alone, decoders --signal <id> one signal

Desktop app

The desktop app embeds Chromium through cef-rs. It is a workspace member but not a default member. Its build downloads CEF into .cef and needs CMake and Ninja. On Linux, Chromium also needs its runtime libraries:

sudo apt-get install -y ninja-build libnss3 libatk-bridge2.0-0t64 libcups2t64 libgbm1 libxkbcommon0 libxcomposite1 libxdamage1 libxrandr2 libasound2t64

cargo xtask desktop lints and tests it, cargo xtask desktop --run starts it, and Releases builds installers.

Before a commit

Format, lint, check, and test what you changed. For docs, run mdbook build docs and check links. The full gates are cargo xtask check and cargo xtask test.