use std::f32::consts::TAU;
pub const NSPS_BB: usize = 864;
pub fn mix_to_baseband(audio: &[f32], freq_hz: f32) -> (Vec<f32>, Vec<f32>) {
let n_bb = audio.len() / 8;
let mut idat = vec![0.0f32; n_bb];
let mut qdat = vec![0.0f32; n_bb];
let phase_step = TAU * freq_hz / 12_000.0;
for k in 0..n_bb {
let mut si = 0.0f32;
let mut sq = 0.0f32;
for j in 0..8usize {
let n = k * 8 + j;
let theta = phase_step * n as f32;
let (sin_t, cos_t) = theta.sin_cos();
si += audio[n] * cos_t;
sq -= audio[n] * sin_t;
}
idat[k] = si;
qdat[k] = sq;
}
(idat, qdat)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn length_is_audio_div_8() {
let audio = vec![0.0f32; 720_000]; let (i, q) = mix_to_baseband(&audio, 1000.0);
assert_eq!(i.len(), 90_000);
assert_eq!(q.len(), 90_000);
}
#[test]
fn dc_tone_concentrates_at_dc() {
let freq = 1200.0f32;
let n = 8 * NSPS_BB; let audio: Vec<f32> = (0..n)
.map(|k| (TAU * freq * k as f32 / 12_000.0).cos())
.collect();
let (idat, qdat) = mix_to_baseband(&audio, freq);
let energy_i: f32 = idat.iter().map(|x| x * x).sum();
let energy_q: f32 = qdat.iter().map(|x| x * x).sum();
assert!(
energy_i > energy_q * 10.0,
"expected I >> Q for cosine at mix freq: ei={energy_i:.1} eq={energy_q:.1}"
);
}
}