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

Decoder plugins

A plugin is a decoder compiled to WebAssembly. Drop the .wasm into the plugins folder (--plugins-dir, default sdrmm/plugins in the platform data folder) and it appears next to the built-in decoders: same palette entry, same settings form, same logs, maps and wires. The folder is watched, so a rebuilt plugin is loaded again on its own, and its running channels reopen on the new build with their settings and wires. A channel the new build refuses keeps the old one and the reason shows as an error.

Write one

This section is for Rust. For C, Zig, Go, AssemblyScript and others, see Plugins in any language.

Start a new plugin from crates/plugin-example, a carrier detector. Pick the tag of the server you run:

cargo install cargo-generate
cargo generate --git https://github.com/newspicel/sdrmm --tag v2.4.3 crates/plugin-example \
  --name my-plugin --define sdrmm_tag=v2.4.3
cd my-plugin
cargo build --target wasm32-unknown-unknown --release
cp target/wasm32-unknown-unknown/release/my_plugin.wasm ~/Library/Application\ Support/sdrmm/plugins/

An existing crate adds the SDK as a git dependency:

sdrmm-plugin = { git = "https://github.com/newspicel/sdrmm", tag = "v2.4.3" }

A plugin crate depends on sdrmm-plugin, builds as a cdylib, implements Plugin and calls export! once:

Plugin itemGives
SettingsYour settings struct, read from JSON with defaults for absent fields
manifest()Type id, name, family, input rate, settings form and event kinds
layout()Input rate and the band the settings occupy
open(), apply()A running channel, and new settings for it
process()One block of IQ in; audio, events and node readouts out
present()How one of your events reads in logs and on maps

The settings form is what the channel node shows: choices, numbers with ranges and units, toggles, text, conditional fields and lists. The frontend draws it from the manifest, so a plugin ships no UI code. Event kinds carry view hints (table, meter, trend, picture) and a map symbol; a status carries the health footer (lock, SNR, frequency error, errors per second). A family or symbol the server does not know gets its own palette group or a dot on the map.

Transmit

Set can_transmit on the descriptor, implement Transmitter and call export_transmit! once. submit() takes audio or a frame, generate() fills baseband at the input rate. The example's Beacon sends frames as two-tone FSK.

Array processors

A plugin can also bring an array processor: it runs on every lane of an Array node at once, like the direction finder. Declare it as the manifest's processor (type id, name, lane range, needs, settings form, readouts, emitted kinds), implement Processor and call export_processor! once. process() gets one block of every lane and sets readouts by their declared order, the health footer and events. Its node has the same form and readouts as any other, without a plot. The example's LaneMeter reports each lane's power and phase against the first.

ABI

The server runs plugins built for its own ABI version and refuses any other.

Speed

sdrmm_plugin::dsp and sdrmm_plugin::modem are the building blocks the built-in decoders use. The heavy kernels in sdrmm_plugin::kernels (FIR, FFT, resampler) run natively in the server on the plugin's own memory. Under cargo test they run in-process, so a plugin tests like any crate.

Safety

Each call has a time budget. A plugin stuck in a loop stops its own channel, which then says so on its node, and never holds the receiver. A plugin is code you chose to install: it is isolated from crashing the server, not vetted.