use crate::common::diagnostics::NamErrorCode;
use crate::math::common::AlignedVec;
use crate::math::common::dispatch::InstructionSet;
use crate::math::common::dispatch::SimdMathConfig;
use crate::math::dsp::fft::RfftPlanner;
use core::fmt;
pub struct LinearFftState {
pub p: usize,
pub n: usize,
pub num_partitions: usize,
pub num_bins: usize,
pub rfft: RfftPlanner<f32>,
pub h_fdl_re: AlignedVec<f32>,
pub h_fdl_im: AlignedVec<f32>,
pub fdl_re: AlignedVec<f32>,
pub fdl_im: AlignedVec<f32>,
pub fdl_write_idx: usize,
pub input_buf: AlignedVec<f32>,
pub fft_re: AlignedVec<f32>,
pub fft_im: AlignedVec<f32>,
pub acc_re: AlignedVec<f32>,
pub acc_im: AlignedVec<f32>,
pub output_buf: AlignedVec<f32>,
pub tail_output_buf: AlignedVec<f32>,
pub sample_counter: usize,
pub isa: InstructionSet,
}
impl fmt::Debug for LinearFftState {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("LinearFftState")
.field("p", &self.p)
.field("n", &self.n)
.field("num_partitions", &self.num_partitions)
.field("num_bins", &self.num_bins)
.field("fdl_write_idx", &self.fdl_write_idx)
.field("sample_counter", &self.sample_counter)
.finish_non_exhaustive()
}
}
impl LinearFftState {
pub fn new(p: usize, weights: &[f32]) -> Result<Self, NamErrorCode> {
let n = weights.len();
assert!(p.is_power_of_two(), "P must be a power of two, got {p}");
assert!(p <= n, "P ({p}) must be ≤ N ({n})");
let block_size = 2 * p;
let num_bins = p + 1;
let num_partitions = if n > p { (n - p).div_ceil(p) } else { 0 };
let mut rfft = RfftPlanner::<f32>::new(block_size);
let fdl_total = num_partitions * num_bins;
let mut h_fdl_re = AlignedVec::<f32>::new(fdl_total, 0.0f32)?;
let mut h_fdl_im = AlignedVec::<f32>::new(fdl_total, 0.0f32)?;
let mut padded = AlignedVec::<f32>::new(block_size, 0.0f32)?;
let mut h_re = AlignedVec::<f32>::new(num_bins, 0.0f32)?;
let mut h_im = AlignedVec::<f32>::new(num_bins, 0.0f32)?;
for k in 0..num_partitions {
let start = p + k * p;
let end = (start + p).min(n);
let seg_len = end - start;
for i in 0..seg_len {
padded[i] = weights[start + i];
}
padded[seg_len..].fill(0.0f32);
rfft.process_forward(&padded, &mut h_re, &mut h_im);
let offset = k * num_bins;
h_fdl_re[offset..offset + num_bins].copy_from_slice(&h_re);
h_fdl_im[offset..offset + num_bins].copy_from_slice(&h_im);
}
let fdl_re = AlignedVec::<f32>::new(fdl_total, 0.0f32)?;
let fdl_im = AlignedVec::<f32>::new(fdl_total, 0.0f32)?;
let fdl_write_idx = num_partitions.saturating_sub(1);
let isa = SimdMathConfig::current().instruction_set;
Ok(Self {
p,
n,
num_partitions,
num_bins,
rfft,
h_fdl_re,
h_fdl_im,
fdl_re,
fdl_im,
fdl_write_idx,
input_buf: AlignedVec::<f32>::new(block_size, 0.0f32)?,
fft_re: AlignedVec::<f32>::new(num_bins, 0.0f32)?,
fft_im: AlignedVec::<f32>::new(num_bins, 0.0f32)?,
acc_re: AlignedVec::<f32>::new(num_bins, 0.0f32)?,
acc_im: AlignedVec::<f32>::new(num_bins, 0.0f32)?,
output_buf: AlignedVec::<f32>::new(block_size, 0.0f32)?,
tail_output_buf: AlignedVec::<f32>::new(p, 0.0f32)?,
sample_counter: 0,
isa,
})
}
#[inline]
pub fn h_fdl_re_partition(&self, k: usize) -> &[f32] {
let offset = k * self.num_bins;
&self.h_fdl_re[offset..offset + self.num_bins]
}
}
mod process;
#[cfg(test)]
#[path = "linear_fft_test.rs"]
mod tests;