orion-sdr
A composable SDR/DSP library in Rust with Python bindings.
Change Log
- v0.0.10: optimize SSB mod and demod path; add throughput results to readme
- v0.0.9: subdivide modulator and demodulator code into per-mode modules
- v0.0.8: add CW, SSB, FM, PM modulators; reorganize source into module tree
- v0.0.7: use {Mode}{Approach}{Demod|Mod} name convention; add Audio to IQ chain, AM modulator with tests
- v0.0.6: add FM and PM demodulators with tests; update changelog, readme
- v0.0.5: add graph scheduler; AGC, FIR decimator; CW and AM demodulators with tests
- v0.0.4: update roadmap
- v0.0.3: update description
- v0.0.2: add API implementation and basic test
- v0.0.1: placeholder API, project structure, roadmap
Status
- Pre-alpha
Features (as of v0.0.10)
- Core traits and runner ✅
- Basic, IQ->IQ, IQ->Audio, Audio->IQ graph schedulers ✅
- NCO, Phase Rotator, IIR FIR low pass, DC blocker, FIR decimator, AGC, IIR cascade ✅
- CW, AM, SSB, FM, PM modulators and demodulators ✅
- Basic DSP and Demod tests ✅
Next Milestones
- PyO3 binding for SSB and simple Python process ✅
- Expose full pipeline via Python, record/replay, UI, etc.
Throughput Test Results (v0.0.10, optimized release build)
The following results were obtained using cargo test --release -- --ignored --nocapture on Apple M2 Silicon (sans SIMD):
| Mode | Throughput (Msps) | Runtime (s) |
|---|---|---|
| SSB-USB | 122.75 | 0.011 |
| CW | 104.41 | 0.019 |
| AM | 33.40 | 0.059 |
| PM | 30.59 | 0.064 |
| FM | 26.71 | 0.074 |
All throughput tests passed successfully (5 passed; 0 failed).
Note: SSB optimizations in this release improved throughput substantially, achieving over 120 Msps, the highest among all demodulation modes.
Demodulator Usage
Below are usage patterns and examples for all demodulators currently available in orion-sdr: CW, AM, SSB, FM, PM.
All examples assume you have IQ samples as Vec<num_complex::Complex32> and show a minimal chain using IqToAudioChain. Adjust sample rates, bandwidths, and gains to your setup.
CW Demodulation (Envelope)
Extract a CW tone at a chosen audio pitch (e.g., 600–800 Hz) from complex baseband IQ.
use ;
use Complex32 as C32;
// IQ sample rate
let fs = 48_000.0;
// CW audio pitch & bandwidth
let pitch_hz = 700.0;
let audio_bw_hz = 300.0;
let mut chain = new;
// Optional: decimate IQ before demod to save CPU (design passband/transition for post-decim BW)
let m = 2; // decimate by 2
let cutoff = * 0.45;
let trans = * 0.10;
chain.push_iq;
// Optional: audio AGC
chain.push_audio;
// Run
let iq: = get_iq_block; // your source of IQ samples
let audio: = chain.process;
AM Demodulation (Envelope)
Simple envelope detector with post low-pass and DC removal.
use ;
use Complex32 as C32;
let fs = 48_000.0;
let audio_bw_hz = 5_000.0; // narrow AM voice; raise for wider audio
let mut chain = new;
chain.push_audio;
let iq: = get_iq_block;
let audio = chain.process;
SSB Demodulation (Product)
Product detector with BFO; set BFO frequency and audio bandwidth to taste.
use ;
use Complex32 as C32;
let fs = 48_000.0;
let bfo_hz = 0.0; // 0 = audio centered; use +/- offset to choose LSB/USB by tuning
let audio_bw_hz = 2_800.0; // typical SSB audio bandwidth
let mut chain = new;
// Optional: decimate IQ first
let m = 2;
let cutoff = * 0.45;
let trans = * 0.10;
chain.push_iq;
// Optional: audio AGC
chain.push_audio;
let iq: = get_iq_block;
let audio = chain.process;
FM Demodulation (Quadrature)
Phase-difference quadrature discriminator. Optional limiter and de-emphasis. Audio is scaled so roughly ±deviation → ±1.0.
use ;
use Complex32 as C32;
let fs = 48_000.0; // IQ sample rate
let dev_hz = 2_500.0; // peak deviation (e.g., 2.5k or 5k for NBFM)
let audio_bw_hz = 5_000.0; // post-demod audio low-pass
let mut chain = new;
// Optional: enable de-emphasis (try 300–750 µs for NBFM voice; 75 µs US WBFM, 50 µs EU WBFM)
// chain.demod_mut().set_deemph_tau_us(300.0);
// Optional: post audio AGC
chain.push_audio;
let iq: = get_iq_block;
let audio = chain.process;
PM Demodulation (Quadrature)
Instantaneous phase (with unwrap). Set pm_sense_rad so that your expected phase deviation maps to ~±1.0 audio.
use ;
use Complex32 as C32;
let fs = 48_000.0;
let pm_sense_rad = 0.8; // radians peak phase deviation → ~±1.0 audio
let audio_bw_hz = 5_000.0;
let mut chain = new;
// Optional: disable amplitude limiter on the demod if desired
// chain.demod_mut().set_limiter(false);
let iq: = get_iq_block;
let audio = chain.process;
Tips
- Center your signal in the complex baseband before demod (DDC/tuning not shown here).
- Decimate early to reduce CPU, but design anti-aliasing correctly (
FirDecimatorcutoff/transition relative to the post-decim rate). - AGC placement: try IQ-domain AGC (before demod) or audio-domain AGC (after demod) depending on your preference and mode.
- FM de-emphasis: speech NBFM often benefits from 300–750 µs; broadcast WBFM uses 75 µs (US) or 50 µs (EU).
- Block sizes: feed consistent chunk sizes to keep latency predictable.