Open-source desktop and headless SDR scanner. Wideband receiver + multi-VFO scanner UI that orchestrates open-source decoders (rtl_433, dsd-fme, native DSP, ...) through a local HTTP/WebSocket API. Runs as a Windows desktop app, a headless Linux/Windows server, or inside Docker.
PulseScope is a clean-room reimplementation: it talks to public decoder sources
through well-defined sidecar boundaries, ships its own native Rust DSP
(APRS/AX.25, CTCSS, DCS, RDS, CW, DTMF), and exposes a documented HTTP/WS API.
Compare its feature coverage against the category leaders in
NYXSCOPE_PARITY_MATRIX.md.
- ✅ Desktop app (Windows MSI/NSIS installers, Tauri 2 webview, embedded UI)
- ✅ Headless server (
--server/--apiflag, optional bearer auth, rustls TLS viaPULSESCOPE_TLS_CERT/PULSESCOPE_TLS_KEY) - ✅ SDRplay RSP1B end-to-end through SoapySDR (discovery, CF32 acquisition, retune, sample-rate rebuild, teardown, reconnect)
- ✅ Native DSP decoders: CTCSS (50 EIA tones), DCS, RDS (57 kHz BPSK), CW/Morse (ITU), DTMF (16 digits), APRS/AX.25 (1200 baud AFSK with Goertzel mark/space, NRZ-I slicer, HDLC frame parser, full AX.25 UI frame parser)
- ✅ dsd-fme sidecar wired for digital voice: P25 Phase 1/2, DMR,
NXDN48/96, D-STAR, YSF, M17, ProVoice (real
dsd-fme.exefrom lwvmobile build, runs against live RSP1B at 853 MHz) - ✅ rtl_433 sidecar with valid u8-IQ transport and JSON parser
- ✅ Spectrum + waterfall UI, live mini-spectrum per VFO, signal-event persistence, SQLite WAL database
- ✅ IQ recording (CF32, real-verified 4M samples ≈ 32 MB)
- ✅ IQ playback (interleaved little-endian CF32 to EOF through the shared capture/scanner path)
- ✅ UDP streaming (PSAU audio + PSIQ IQ with magic/version headers)
- ✅ 79 default scan ranges from AM Broadcast through GOES HRIT/LRIT
- ✅ Docker multi-stage build + systemd unit for headless deployment
- ✅ 35 unit tests passing
See NYXSCOPE_PARITY_MATRIX.md for honest feature-by-feature classification
(what's wired, what's partial, what's blocked, what's deliberately unavailable).
# 1. build
cargo build --release --features soapysdr --manifest-path src-tauri/Cargo.toml
# 2. discover & connect RSP1B (or swap for any SoapySDR-compatible SDR)
./target/release/pulsescope.exe --server
# → bound to 127.0.0.1:8765
curl http://127.0.0.1:8765/health
# {"name":"pulsescope","status":"ok","version":"0.1.0"}
# 3. open the UI
xdg-open http://127.0.0.1:8765/ # Linux / WSL
start http://127.0.0.1:8765/ # WindowsWith auth and TLS:
PULSESCOPE_AUTH_TOKEN=$(openssl rand -hex 24) \
PULSESCOPE_TLS_CERT=/etc/pulsescope/cert.pem \
PULSESCOPE_TLS_KEY=/etc/pulsescope/key.pem \
PULSESCOPE_BIND=0.0.0.0 \
./target/release/pulsescope.exe --server --port 8443
# → https://0.0.0.0:8443/ (UI + API)
# In the browser, append ?token=<your-token> once; it's cached in localStorage.pnpm install
pnpm tauri dev # hot-reload development
pnpm tauri build # produces MSI + NSIS installers in src-tauri/target/release/bundle/docker build -t pulsescope .
docker run --rm -p 8765:8765 \
-v /dev/bus/usb:/dev/bus/usb \
--device-cgroup-rule='c 189:* rwm' \
pulsescopePulseScope expects decoders on $PATH or in <exe-dir>/decoders/. Currently
verified:
| Decoder | Source / Binary | Transport | Status |
|---|---|---|---|
rtl_433 |
PothosSDR 0.8.1 | Sidecar, u8 IQ via stdin/stdout | WIRED, REAL-TESTED |
rtl_adsb |
PothosSDR | (binary available, transport TBD) | Available |
dsd-fme |
/c/Users/Dadud/pulsescope/decoders/dsd-fme/ |
Sidecar, 48 kHz mono WAV via fs | WIRED, REAL-TESTED |
AIS-catcher |
jvde-github v0.70 | (binary available, transport TBD) | Installed, exclusive SDR |
Multimon-ng, direwolf, acarsdec, nrsc5, dumpvdl2, dump978 source tarballs are
checked into decoders/ (GPL redistribution permitted by upstream authors) but
require a cmake + ninja/make build step on the host. The PulseScope daemon
finds them on $PATH once built; until then the corresponding endpoints
return explicit "decoder unavailable" responses.
pulsescope/
├─ src-tauri/ Rust backend (server + scanner core + sidecars)
│ ├─ src/
│ │ ├─ main.rs dual-mode entry: desktop or --server
│ │ ├─ api.rs ~150 HTTP/WS routes, auth middleware, TLS bind
│ │ ├─ scanner.rs capture/audio workers, VFO DSP, signal detection
│ │ ├─ demod.rs native demodulators + tone decoders
│ │ ├─ aprs.rs native AFSK 1200 APRS/AX.25 decoder
│ │ ├─ voice_decoder.rs dsd-fme sidecar (digital voice)
│ │ ├─ sidecar.rs rtl_433 sidecar (ISM sensors)
│ │ ├─ depend_manager.rs cross-platform sidecar discovery
│ │ ├─ device.rs SoapySDR hardware lifecycle
│ │ ├─ capture.rs bounded IQ rings + CaptureWorker + playback
│ │ ├─ audio.rs CPAL output + UDP PSAU streamer
│ │ └─ state.rs / db.rs shared runtime state + SQLite WAL
│ ├─ migrations/001_init.sql
│ └─ Cargo.toml
├─ ui/ Svelte 5 + SvelteKit (hash-router SPA)
│ └─ src/routes/ 19 pages: scanner, trunking, deps, recording, ...
├─ decoders/ (git-ignored) downloaded GPL sidecar binaries
├─ docs/
│ ├─ API.md HTTP/WS endpoint contract
│ ├─ SCHEMA.md SQLite schema
│ ├─ PRESETS.md scan-range preset table
│ ├─ PARSERS.md decoder transport recipes
│ └─ BUILD.md platform build matrix
├─ Dockerfile
├─ contrib/systemd/pulsescope.service
└─ NYXSCOPE_PARITY_MATRIX.md honest feature coverage comparison
| Variable | Default | Notes |
|---|---|---|
PULSESCOPE_BIND |
127.0.0.1 |
Listen addr. 0.0.0.0 for public |
PULSESCOPE_PORT |
8765 |
HTTP listen port |
PULSESCOPE_AUTH_TOKEN |
unset | If set, bearer token required on all /... |
PULSESCOPE_TLS_CERT |
unset | PEM cert bytes → enables rustls HTTPS |
PULSESCOPE_TLS_KEY |
unset | PEM key bytes |
PULSESCOPE_API_ONLY |
unset | Skip UI static mount; API-only server |
PULSESCOPE_UI_DIR |
auto-detect | Override static UI directory |
SOAPY_SDR_ROOT |
unset | PothosSDR install root for bindings |
PULSESCOPE_SOAPY_UTIL |
auto-detect | Path to SoapySDRUtil.exe |
The PulseScope core is MIT (see LICENSE). Native decoders written here
(APRS/AX.25, CTCSS, DCS, RDS, CW, DTMF, voice decoder scaffold) are MIT.
Sidecar decoders invoked at runtime (rtl_433, dsd-fme, ...) are GPL and remain
separate processes. PulseScope communicates with them through stdin/stdout and
serialised JSON / WAV — it does not link them. See
docs/THIRD_PARTY_NOTICES.md for the full inventory.
PulseScope is built on the shoulders of the SDR open-source community. The
decoder ecosystem is the actual hard part and is the work of dozens of
upstream maintainers — see NYXSCOPE_PARITY_MATRIX.md for the upstream
attribution of each protocol covered.