use core::ops::Deref;
use alloc::{borrow::ToOwned, boxed::Box, vec::Vec};
use ndarray::Ix1;
use once_cell::race::OnceBox;
use rustfft::{num_complex::Complex, FftPlanner};
use crate::DisplayMode;
use super::Schedule;
#[derive(Debug)]
pub struct PointSpread {
unrotated: Box<[Complex<f64>]>,
rotated: OnceBox<Box<[Complex<f64>]>>,
}
impl Deref for PointSpread {
type Target = [Complex<f64>];
fn deref(&self) -> &Self::Target {
&self.unrotated
}
}
impl PointSpread {
pub fn into_inner(self) -> Box<[Complex<f64>]> {
self.unrotated
}
pub fn rotated(&self) -> &[Complex<f64>] {
self.rotated.get_or_init(|| {
let mut rotated = self.unrotated.to_owned();
rotated.rotate_right(rotated.len() / 2);
rotated.into()
})
}
pub(crate) fn inner_mut(&mut self) -> &mut [Complex<f64>] {
self.rotated = OnceBox::new();
&mut self.unrotated
}
pub(super) fn calculate(sched: &Schedule<Ix1>) -> PointSpread {
let mut planner = FftPlanner::new();
let fft = planner.plan_fft_forward(sched.len());
let mut data = sched
.iter()
.map(|v| Complex {
re: if *v { 1. } else { 0. },
im: 0.,
})
.collect::<Vec<_>>();
fft.process(&mut data);
for v in data.iter_mut() {
*v /= (sched.len() as f64).sqrt()
}
PointSpread {
unrotated: data.into(),
rotated: OnceBox::new(),
}
}
pub fn central_peak_radius(&self, mode: DisplayMode) -> usize {
self.windows(2)
.take_while(|v| mode.magnitude(v[0]) > mode.magnitude(v[1]))
.count()
+ 1
}
pub fn peak_to_sidelobe_ratio(&self, mode: DisplayMode) -> f64 {
let peak = mode.magnitude(self[0]);
self.iter()
.take(self.len() / 2 + 1)
.skip(self.central_peak_radius(mode) + 1)
.map(|v| mode.magnitude(*v))
.max_by(|a, b| a.total_cmp(b))
.unwrap_or(0.)
/ peak
}
}
#[cfg(feature = "terminal-viz")]
impl crate::terminal_viz::Plot for PointSpread {
type BarChart<'a> = <[Complex<f64>] as crate::terminal_viz::Plot>::BarChart<'a>
where
Self: 'a;
type RowChart<'a> = <[Complex<f64>] as crate::terminal_viz::Plot>::RowChart<'a>
where
Self: 'a;
fn bar_chart(&self, options: crate::terminal_viz::BarChartOptions) -> Self::BarChart<'_> {
self.rotated().bar_chart(options)
}
fn row_chart(&self, options: crate::terminal_viz::RowChartOptions) -> Self::RowChart<'_> {
self.rotated().row_chart(options)
}
}