#![cfg(all(feature = "fst4", any(feature = "fft-rustfft", feature = "fft-extern")))]
use mfsk_core::engine::sync::{AudioSource, RxGrid, SpectrogramBuilder, compute_spectra};
use mfsk_core::fst4::Fst4s60;
use mfsk_core::fst4::ddc::grid_for;
fn synth(n: usize) -> (Vec<f32>, Vec<f32>) {
let mut xi = Vec::with_capacity(n);
let mut xq = Vec::with_capacity(n);
for k in 0..n {
let t = k as f32;
let re = (t * 0.017).sin() * 0.6 + (t * 0.31).cos() * 0.25 + (t * 0.0031).sin() * 0.4;
let im = (t * 0.019).cos() * 0.55 + (t * 0.29).sin() * 0.2 + (t * 0.0027).cos() * 0.35;
xi.push(re);
xq.push(im);
}
(xi, xq)
}
#[test]
fn spectrogram_builder_matches_compute_spectra() {
let grid_desc = grid_for(2900.0);
let grid = RxGrid::complex(grid_desc.fs_c, 1550.0);
let n = (grid_desc.fs_c * 60.0) as usize;
let (xi, xq) = synth(n);
let bin_lo = 84usize;
let bin_hi = 1964usize;
let want = compute_spectra::<Fst4s60>(AudioSource::Complex(&xi, &xq), bin_lo, bin_hi, grid);
for chunk in [1usize, 7, 511, 512, 513, 1024, 4096, n] {
let mut b = SpectrogramBuilder::new::<Fst4s60>(bin_lo, bin_hi, grid);
let mut off = 0usize;
while off < n {
let end = (off + chunk).min(n);
b.push(&xi[off..end], &xq[off..end]);
off = end;
}
let got = b.finish();
assert_eq!(got.n_freq, want.n_freq, "chunk {chunk}: n_freq");
assert_eq!(got.n_time, want.n_time, "chunk {chunk}: n_time");
assert_eq!(
got.freq_offset(),
want.freq_offset(),
"chunk {chunk}: freq_offset"
);
let a = want.avg_power_per_bin();
let c = got.avg_power_per_bin();
assert_eq!(a.len(), c.len(), "chunk {chunk}: avg_power len");
for (i, (x, y)) in a.iter().zip(c.iter()).enumerate() {
assert_eq!(
x.to_bits(),
y.to_bits(),
"chunk {chunk}: avg_power_per_bin[{i}] {x} != {y}"
);
}
}
}
#[test]
fn spectrogram_builder_zero_fills_a_short_stream() {
let grid_desc = grid_for(2900.0);
let grid = RxGrid::complex(grid_desc.fs_c, 1550.0);
let n = (grid_desc.fs_c * 12.0) as usize; let (xi, xq) = synth(n);
let (bin_lo, bin_hi) = (100usize, 400usize);
let want = compute_spectra::<Fst4s60>(AudioSource::Complex(&xi, &xq), bin_lo, bin_hi, grid);
let mut b = SpectrogramBuilder::new::<Fst4s60>(bin_lo, bin_hi, grid);
b.push(&xi, &xq);
let got = b.finish();
let a = want.avg_power_per_bin();
let c = got.avg_power_per_bin();
for (i, (x, y)) in a.iter().zip(c.iter()).enumerate() {
assert_eq!(x.to_bits(), y.to_bits(), "avg_power_per_bin[{i}]");
}
}