use alloc::vec;
use alloc::vec::Vec;
use num_complex::Complex;
use crate::core::fft::default_planner;
pub const BASEBAND_RATE: f32 = 375.0;
pub const NFFT2: usize = 46080;
pub const NFFT1: usize = NFFT2 * 32;
pub const CENTER_HZ: f32 = 1500.0;
pub const NPOINTS_MAX: usize = 114 * 12_000;
pub fn decimate_to_baseband(audio: &[f32]) -> (Vec<f32>, Vec<f32>) {
let mut buf: Vec<Complex<f32>> = Vec::with_capacity(NFFT1);
let n_in = audio.len().min(NPOINTS_MAX);
for &s in &audio[..n_in] {
buf.push(Complex::new(s, 0.0));
}
buf.resize(NFFT1, Complex::new(0.0, 0.0));
let mut planner = default_planner();
let fft = planner.plan_forward(NFFT1);
fft.process(&mut buf);
let df = 12_000.0 / NFFT1 as f32;
let i0 = (CENTER_HZ / df).round() as usize;
let nh2 = NFFT2 / 2;
let mut fftin: Vec<Complex<f32>> = vec![Complex::new(0.0, 0.0); NFFT2];
for i in 0..NFFT2 {
let j = if i > nh2 {
i0.wrapping_add(i).wrapping_sub(NFFT2)
} else {
i0 + i
};
if j < buf.len() {
fftin[i] = buf[j];
}
}
let ifft = planner.plan_inverse(NFFT2);
ifft.process(&mut fftin);
const NORM: f32 = 1.0 / 1000.0;
let mut idat = vec![0.0f32; NFFT2];
let mut qdat = vec![0.0f32; NFFT2];
for i in 0..NFFT2 {
idat[i] = fftin[i].re * NORM;
qdat[i] = fftin[i].im * NORM;
}
(idat, qdat)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn decimate_returns_expected_length_and_finite_samples() {
let fs = 12_000.0;
let f = 1500.0;
let n = NPOINTS_MAX;
let audio: Vec<f32> = (0..n)
.map(|k| (2.0 * core::f32::consts::PI * f * k as f32 / fs).cos())
.collect();
let (idat, qdat) = decimate_to_baseband(&audio);
assert_eq!(idat.len(), NFFT2);
assert_eq!(qdat.len(), NFFT2);
let max_mag = idat
.iter()
.zip(qdat.iter())
.map(|(&i, &q)| (i * i + q * q).sqrt())
.fold(0f32, f32::max);
assert!(
max_mag.is_finite() && max_mag > 0.0,
"baseband output should carry the 1500 Hz tone (max mag {})",
max_mag
);
}
#[test]
fn decimate_centres_carrier_for_full_length_tone() {
let fs = 12_000.0;
let f = 1500.0;
let n = NPOINTS_MAX;
let audio: Vec<f32> = (0..n)
.map(|k| (2.0 * core::f32::consts::PI * f * k as f32 / fs).cos())
.collect();
let (idat, qdat) = decimate_to_baseband(&audio);
let mid = NFFT2 / 2;
let mag_mid = (idat[mid] * idat[mid] + qdat[mid] * qdat[mid]).sqrt();
let mag_q = (idat[NFFT2 / 4] * idat[NFFT2 / 4] + qdat[NFFT2 / 4] * qdat[NFFT2 / 4]).sqrt();
assert!(
mag_mid > 0.0 && mag_q > 0.0,
"baseband should not be zero mid-stream; mag_mid={} mag_q={}",
mag_mid,
mag_q
);
}
}