use alloc::boxed::Box;
use alloc::vec;
use alloc::vec::Vec;
use num_complex::{Complex, Complex32};
#[cfg(not(feature = "std"))]
use num_traits::Float;
use super::fft_15::fft_15;
use super::fft_sc16_r2::{bit_rev_sc16, fft2r_sc16, gen_w_r2_sc16};
const N: usize = 3840;
const N1: usize = 256;
const N2: usize = 15;
pub struct Plan3840Sc16 {
w_inner: Vec<Complex<i16>>,
twiddles_outer: Box<[Complex32; N]>,
}
impl Plan3840Sc16 {
pub fn new() -> Self {
Self {
w_inner: gen_w_r2_sc16(N1),
twiddles_outer: super::fft_mixed_3840::build_twiddles(),
}
}
pub fn process(&self, buf: &mut [Complex<i16>]) {
assert_eq!(buf.len(), N, "Plan3840Sc16: input must be 3840 samples");
let mut rows: Vec<Complex<i16>> = vec![Complex::new(0i16, 0); N];
for n1 in 0..N1 {
for n2 in 0..N2 {
rows[n2 * N1 + n1] = buf[15 * n1 + n2];
}
}
for n2 in 0..N2 {
let row = &mut rows[n2 * N1..(n2 + 1) * N1];
fft2r_sc16(row, &self.w_inner);
bit_rev_sc16(row);
}
let mut m: Vec<Complex32> = vec![Complex32::new(0.0, 0.0); N];
for (i, c) in rows.iter().enumerate() {
m[i] = Complex32::new(c.re as f32, c.im as f32) * self.twiddles_outer[i];
}
let mut col = [Complex32::new(0.0, 0.0); N2];
for k1 in 0..N1 {
for k2 in 0..N2 {
col[k2] = m[k2 * N1 + k1];
}
fft_15(&mut col);
for k2 in 0..N2 {
m[k2 * N1 + k1] = col[k2];
}
}
for k2 in 0..N2 {
for k1 in 0..N1 {
let c = m[k2 * N1 + k1];
let re = c.re.round().clamp(i16::MIN as f32, i16::MAX as f32) as i16;
let im = c.im.round().clamp(i16::MIN as f32, i16::MAX as f32) as i16;
buf[N1 * k2 + k1] = Complex::new(re, im);
}
}
}
}
impl Default for Plan3840Sc16 {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn sc16_3840_pure_tone() {
let plan = Plan3840Sc16::new();
let k_bin = 40; let mut buf: Vec<Complex<i16>> = vec![Complex::new(0i16, 0); N];
for i in 0..N {
let phase = core::f32::consts::TAU * (k_bin as f32) * (i as f32) / (N as f32);
buf[i] = Complex::new((8000.0 * phase.cos()) as i16, (8000.0 * phase.sin()) as i16);
}
plan.process(&mut buf);
let peak_mag2 = (buf[k_bin].re as i32).pow(2) + (buf[k_bin].im as i32).pow(2);
let mut total_mag2: u64 = 0;
for c in buf.iter() {
total_mag2 += ((c.re as i32).pow(2) + (c.im as i32).pow(2)) as u64;
}
assert!(
(peak_mag2 as u64) > total_mag2 / 100,
"peak {peak_mag2} not dominating (sum={total_mag2})"
);
}
}