use num_complex::Complex32 as C32;
use crate::core::{Block, WorkReport};
#[inline]
pub fn rms(x: &[f32]) -> f32 {
if x.is_empty() { return 0.0; }
let s: f32 = x.iter().map(|v| v*v).sum();
(s / (x.len() as f32)).sqrt()
}
pub fn hann(n: usize) -> Vec<f32> {
(0..n).map(|k| 0.5 - 0.5 * (core::f32::consts::TAU * k as f32 / n as f32).cos()).collect()
}
pub fn tone(fs: f32, f_hz: f32, n: usize, amp: f32) -> Vec<f32> {
(0..n)
.map(|k| amp * (core::f32::consts::TAU * f_hz * (k as f32) / fs).sin())
.collect()
}
pub fn gen_complex_tone(fs: f32, f_hz: f32, n: usize) -> Vec<C32> {
(0..n)
.map(|k| {
let ph = core::f32::consts::TAU * f_hz * (k as f32) / fs;
C32::new(ph.cos(), ph.sin())
})
.collect()
}
pub fn snr_db_at(fs: f32, f_hz: f32, x: &[f32]) -> f32 {
let n = x.len().max(1);
let w = hann(n);
let two_pi = core::f32::consts::TAU;
let mut re = 0.0f32;
let mut im = 0.0f32;
for (k, (&xi, &wi)) in x.iter().zip(w.iter()).enumerate() {
let ph = two_pi * f_hz * (k as f32) / fs;
re += wi * xi * ph.cos();
im += wi * xi * ph.sin();
}
let sig = (re*re + im*im).sqrt() / (w.iter().sum::<f32>() + 1e-12);
let p_total: f32 = x.iter().map(|v| v*v).sum::<f32>() / (n as f32);
let p_sig = sig * sig;
let p_noise = (p_total - p_sig).max(1e-12);
10.0 * (p_sig / p_noise).log10()
}
pub fn measure<F: FnMut() -> usize>(mut f: F, n: usize) -> (f32, f32) {
let t0 = std::time::Instant::now();
let _ = f();
let dt = t0.elapsed().as_secs_f32();
let msps = (n as f32) / dt / 1e6;
(msps, dt)
}
#[inline]
pub fn run_block<B: Block>(blk: &mut B, input: &[B::In], output: &mut [B::Out]) -> WorkReport {
blk.process(input, output)
}
#[inline]
pub fn run_block_vec<B: Block>(blk: &mut B, input: &[B::In]) -> (Vec<B::Out>, WorkReport)
where
B::Out: Default + Copy,
{
let mut out = vec![B::Out::default(); input.len()];
let wr = blk.process(input, &mut out);
(out, wr)
}