use std::time::Instant;
use std::hint::black_box;
use num_complex::Complex32 as C32;
fn real_tone(fs: f32, f_hz: f32, n: usize, amp: f32) -> Vec<f32> {
(0..n)
.map(|k| amp * (std::f32::consts::TAU * f_hz * (k as f32) / fs).sin())
.collect()
}
fn key_envelope_square(fs: f32, key_hz: f32, n: usize) -> Vec<f32> {
(0..n)
.map(|k| ((k as f32 * key_hz / fs).fract() < 0.5) as i32 as f32)
.collect()
}
fn minsps_from_env(default_msps: f32) -> f32 {
std::env::var("ORION_SDR_THROUGHPUT_MINSPS")
.ok()
.and_then(|s| s.parse::<f32>().ok())
.unwrap_or(default_msps)
}
fn measure_throughput(mut f: impl FnMut() -> usize, samples_per_pass: usize, repeats: usize) -> (f32, f64) {
let start = Instant::now();
let mut sink = 0.0f64;
for _ in 0..repeats {
let n = f();
sink = black_box(sink + (n as f64) * 1e-12);
}
let dt = start.elapsed().as_secs_f64();
let total_samples = samples_per_pass as f64 * repeats as f64;
let msps = (total_samples / dt) / 1.0e6;
black_box(sink);
(msps as f32, dt)
}
#[test]
#[ignore]
fn throughput_cw_roundtrip() {
use crate::core::{AudioToIqChain, IqToAudioChain};
use crate::modulate::CwKeyedMod;
use crate::demodulate::CwEnvelopeDemod;
let fs = 48_000.0;
let n = 65_536;
let repeats = 30;
let key = key_envelope_square(fs, 5.0, n);
let mut tx = AudioToIqChain::new(CwKeyedMod::new(fs, 700.0, 3.0, 3.0));
let mut rx = IqToAudioChain::new(CwEnvelopeDemod::new(fs, 700.0, 300.0));
let (msps, dt) = measure_throughput(
|| {
let iq: Vec<C32> = tx.process(key.clone());
let _audio: Vec<f32> = rx.process(iq);
_audio.len()
},
n,
repeats,
);
println!("[CW] {:.2} Msps in {:.3}s", msps, dt);
let min_msps = minsps_from_env(0.2);
assert!(msps >= min_msps, "CW throughput {:.2} Msps < min {:.2} Msps", msps, min_msps);
}
#[test]
#[ignore]
fn throughput_am_roundtrip() {
use crate::core::{AudioToIqChain, IqToAudioChain};
use crate::modulate::AmDsbMod;
use crate::demodulate::AmEnvelopeDemod;
let fs = 48_000.0;
let n = 65_536;
let repeats = 30;
let audio = real_tone(fs, 1000.0, n, 0.5);
let mut tx = AudioToIqChain::new(AmDsbMod::new(fs, 0.0, 0.8, 0.5));
let mut rx = IqToAudioChain::new(AmEnvelopeDemod::new(fs, 5_000.0));
let (msps, dt) = measure_throughput(
|| {
let iq = tx.process(audio.clone());
let out = rx.process(iq);
out.len()
},
n,
repeats,
);
println!("[AM] {:.2} Msps in {:.3}s", msps, dt);
let min_msps = minsps_from_env(0.2);
assert!(msps >= min_msps, "AM throughput {:.2} Msps < min {:.2} Msps", msps, min_msps);
}
#[test]
#[ignore]
fn throughput_ssb_usb_roundtrip() {
use crate::core::{AudioToIqChain, IqToAudioChain};
use crate::modulate::SsbPhasingMod;
use crate::demodulate::SsbProductDemod;
let fs = 48_000.0;
let n = 65_536;
let repeats = 20;
let audio = real_tone(fs, 1200.0, n, 0.4);
let mut tx = AudioToIqChain::new(SsbPhasingMod::new(fs, 2_800.0, 1_500.0, 0.0, true));
let mut rx = IqToAudioChain::new(SsbProductDemod::new(fs, 0.0, 2_800.0));
let (msps, dt) = measure_throughput(
|| {
let iq = tx.process(audio.clone());
let out = rx.process(iq);
out.len()
},
n,
repeats,
);
println!("[SSB-USB] {:.2} Msps in {:.3}s", msps, dt);
let min_msps = minsps_from_env(0.15);
assert!(msps >= min_msps, "SSB throughput {:.2} Msps < min {:.2} Msps", msps, min_msps);
}
#[test]
#[ignore]
fn throughput_fm_roundtrip() {
use crate::core::{AudioToIqChain, IqToAudioChain};
use crate::modulate::FmPhaseAccumMod;
use crate::demodulate::FmQuadratureDemod;
let fs = 48_000.0;
let n = 65_536;
let repeats = 30;
let audio = real_tone(fs, 1000.0, n, 0.5);
let mut tx = AudioToIqChain::new(FmPhaseAccumMod::new(fs, 2_500.0, 0.0));
let mut rx = IqToAudioChain::new(FmQuadratureDemod::new(fs, 2_500.0, 5_000.0));
let (msps, dt) = measure_throughput(
|| {
let iq = tx.process(audio.clone());
let out = rx.process(iq);
out.len()
},
n,
repeats,
);
println!("[FM] {:.2} Msps in {:.3}s", msps, dt);
let min_msps = minsps_from_env(0.25);
assert!(msps >= min_msps, "FM throughput {:.2} Msps < min {:.2} Msps", msps, min_msps);
}
#[test]
#[ignore]
fn throughput_pm_roundtrip() {
use crate::core::{AudioToIqChain, IqToAudioChain};
use crate::modulate::PmDirectPhaseMod;
use crate::demodulate::PmQuadratureDemod;
let fs = 48_000.0;
let n = 65_536;
let repeats = 30;
let audio = real_tone(fs, 900.0, n, 0.5);
let mut tx = AudioToIqChain::new(PmDirectPhaseMod::new(fs, 0.9, 0.0));
let mut rx = IqToAudioChain::new(PmQuadratureDemod::new(0.9, 5_000.0, fs));
let (msps, dt) = measure_throughput(
|| {
let iq = tx.process(audio.clone());
let out = rx.process(iq);
out.len()
},
n,
repeats,
);
println!("[PM] {:.2} Msps in {:.3}s", msps, dt);
let min_msps = minsps_from_env(0.25);
assert!(msps >= min_msps, "PM throughput {:.2} Msps < min {:.2} Msps", msps, min_msps);
}