nmr_schedule/schedule/
point_spread.rs1use core::ops::Deref;
6
7use alloc::{borrow::ToOwned, boxed::Box, vec::Vec};
8use ndarray::Ix1;
9use once_cell::race::OnceBox;
10
11use rustfft::{num_complex::Complex, FftPlanner};
12
13use crate::DisplayMode;
14
15use super::Schedule;
16
17#[derive(Debug)]
18pub struct PointSpread {
20 unrotated: Box<[Complex<f64>]>,
21 rotated: OnceBox<Box<[Complex<f64>]>>,
22}
23
24impl Deref for PointSpread {
25 type Target = [Complex<f64>];
26
27 fn deref(&self) -> &Self::Target {
28 &self.unrotated
29 }
30}
31
32impl PointSpread {
33 pub fn into_inner(self) -> Box<[Complex<f64>]> {
35 self.unrotated
36 }
37
38 pub fn rotated(&self) -> &[Complex<f64>] {
42 self.rotated.get_or_init(|| {
43 let mut rotated = self.unrotated.to_owned();
44
45 rotated.rotate_right(rotated.len() / 2);
46
47 rotated.into()
48 })
49 }
50
51 pub(crate) fn inner_mut(&mut self) -> &mut [Complex<f64>] {
52 self.rotated = OnceBox::new();
53 &mut self.unrotated
54 }
55
56 pub(super) fn calculate(sched: &Schedule<Ix1>) -> PointSpread {
57 let mut planner = FftPlanner::new();
58 let fft = planner.plan_fft_forward(sched.len());
59
60 let mut data = sched
61 .iter()
62 .map(|v| Complex {
63 re: if *v { 1. } else { 0. },
64 im: 0.,
65 })
66 .collect::<Vec<_>>();
67 fft.process(&mut data);
68
69 for v in data.iter_mut() {
70 *v /= (sched.len() as f64).sqrt()
71 }
72
73 PointSpread {
74 unrotated: data.into(),
75 rotated: OnceBox::new(),
76 }
77 }
78
79 pub fn central_peak_radius(&self, mode: DisplayMode) -> usize {
83 self.windows(2)
84 .take_while(|v| mode.magnitude(v[0]) > mode.magnitude(v[1]))
85 .count()
86 + 1
87 }
88
89 pub fn peak_to_sidelobe_ratio(&self, mode: DisplayMode) -> f64 {
93 let peak = mode.magnitude(self[0]);
94
95 self.iter()
96 .take(self.len() / 2 + 1)
97 .skip(self.central_peak_radius(mode) + 1)
98 .map(|v| mode.magnitude(*v))
99 .max_by(|a, b| a.total_cmp(b))
100 .unwrap_or(0.)
101 / peak
102 }
103}
104
105#[cfg(feature = "terminal-viz")]
106impl crate::terminal_viz::Plot for PointSpread {
107 type BarChart<'a> = <[Complex<f64>] as crate::terminal_viz::Plot>::BarChart<'a>
108 where
109 Self: 'a;
110
111 type RowChart<'a> = <[Complex<f64>] as crate::terminal_viz::Plot>::RowChart<'a>
112 where
113 Self: 'a;
114
115 fn bar_chart(&self, options: crate::terminal_viz::BarChartOptions) -> Self::BarChart<'_> {
116 self.rotated().bar_chart(options)
117 }
118
119 fn row_chart(&self, options: crate::terminal_viz::RowChartOptions) -> Self::RowChart<'_> {
120 self.rotated().row_chart(options)
121 }
122}