1use idsp::hbf::{Filter, HbfDecCascade};
2use rustfft::{num_complex::Complex, Fft, FftPlanner};
3use std::sync::Arc;
4
5#[derive(Copy, Clone, Debug, PartialEq)]
11pub struct Window<const N: usize> {
12 pub win: [f32; N],
13 pub power: f32,
15 pub nenbw: f32,
17 pub overlap: usize,
19}
20
21impl<const N: usize> Window<N> {
22 pub fn rectangular() -> Self {
24 assert!(N > 0);
25 Self {
26 win: [1.0; N],
27 power: 1.0,
28 nenbw: 1.0,
29 overlap: 0,
30 }
31 }
32
33 pub fn hann() -> Self {
42 assert!(N > 0);
43 let df = core::f32::consts::PI / N as f32;
44 let mut win = [0.0; N];
45 for (i, w) in win.iter_mut().enumerate() {
46 *w = (df * i as f32).sin().powi(2);
47 }
48 Self {
49 win,
50 power: 0.25,
51 nenbw: 1.5,
52 overlap: N / 2,
53 }
54 }
55}
56
57#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, clap::ValueEnum)]
59pub enum Detrend {
60 #[default]
62 None,
63 Midpoint,
65 Span,
67 Mean,
69 Linear,
71}
72
73impl core::fmt::Display for Detrend {
74 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
75 core::fmt::Debug::fmt(self, f)
76 }
77}
78
79impl Detrend {
80 pub fn apply<const N: usize>(&self, x: &[f32; N], win: &Window<N>) -> [Complex<f32>; N] {
81 let mut c = [Complex::default(); N];
83
84 match self {
85 Detrend::None => {
86 for ((c, x), w) in c.iter_mut().zip(x.iter()).zip(win.win.iter()) {
87 c.re = x * w;
88 c.im = 0.0;
89 }
90 }
91 Detrend::Midpoint => {
92 let offset = x[N / 2];
93 for ((c, x), w) in c.iter_mut().zip(x.iter()).zip(win.win.iter()) {
94 c.re = (x - offset) * w;
95 c.im = 0.0;
96 }
97 }
98 Detrend::Span => {
99 let mut offset = x[0];
100 let slope = (x[N - 1] - x[0]) / (N - 1) as f32;
101 for ((c, x), w) in c.iter_mut().zip(x.iter()).zip(win.win.iter()) {
102 c.re = (x - offset) * w;
103 c.im = 0.0;
104 offset += slope;
105 }
106 }
107 Detrend::Mean => {
108 let offset = x.iter().sum::<f32>() / N as f32;
109 for ((c, x), w) in c.iter_mut().zip(x.iter()).zip(win.win.iter()) {
110 c.re = (x - offset) * w;
111 c.im = 0.0;
112 }
113 }
114 Detrend::Linear => unimplemented!(),
115 };
116 c
117 }
118}
119
120#[derive(Clone)]
124pub struct Psd<const N: usize> {
125 hbf: HbfDecCascade,
126 buf: [f32; N],
127 idx: usize,
128 spectrum: [f32; N], count: u32,
130 drain: usize,
131 fft: Arc<dyn Fft<f32>>,
132 win: Arc<Window<N>>,
133 detrend: Detrend,
134 avg: u32,
135}
136
137impl<const N: usize> Psd<N> {
138 pub fn new(fft: Arc<dyn Fft<f32>>, win: Arc<Window<N>>) -> Self {
139 let hbf = HbfDecCascade::default();
140 assert_eq!(N, fft.len());
141 assert!(N >= 2); let mut s = Self {
144 hbf,
145 buf: [0.0; N],
146 idx: 0,
147 spectrum: [0.0; N],
148 count: 0,
149 fft,
150 win,
151 detrend: Detrend::default(),
152 drain: 0,
153 avg: u32::MAX,
154 };
155 s.set_stage_depth(0);
156 s
157 }
158
159 pub fn set_avg(&mut self, avg: u32) {
160 self.avg = avg;
161 }
162
163 pub fn set_detrend(&mut self, d: Detrend) {
164 self.detrend = d;
165 }
166
167 pub fn set_stage_depth(&mut self, n: usize) {
168 self.hbf.set_depth(n);
169 self.drain = self.hbf.response_length() as _;
170 }
171}
172
173pub trait PsdStage {
174 fn process<'a>(&mut self, x: &[f32], y: &'a mut [f32]) -> &'a mut [f32];
193 fn spectrum(&self) -> &[f32];
195 fn gain(&self) -> f32;
199 fn count(&self) -> u32;
201 fn buf(&self) -> &[f32];
203}
204
205impl<const N: usize> PsdStage for Psd<N> {
206 fn process<'a>(&mut self, mut x: &[f32], y: &'a mut [f32]) -> &'a mut [f32] {
207 let mut n = 0;
208 while !x.is_empty() {
210 let take = x.len().min(self.buf.len() - self.idx);
212 let chunk;
213 (chunk, x) = x.split_at(take);
214 self.buf[self.idx..][..take].copy_from_slice(chunk);
215 self.idx += take;
216 if self.idx < N {
217 break;
218 }
219
220 let mut c = self.detrend.apply(&self.buf, &self.win);
222 self.fft.process(&mut c);
224
225 let g = if self.count > self.avg {
227 let g = self.avg as f32 / self.count as f32;
228 self.count = self.avg;
229 g
230 } else {
231 1.0
232 };
233 for (c, p) in c[..N / 2 + 1]
235 .iter()
236 .zip(self.spectrum[..N / 2 + 1].iter_mut())
237 {
238 *p = g * *p + c.norm_sqr();
239 }
240
241 let start = if self.count == 0 {
242 0
244 } else {
245 self.buf.copy_within(N - self.win.overlap..N, 0);
247 self.win.overlap
249 };
250
251 let mut yi = self.hbf.process_block(None, &mut self.buf[start..]);
253 let skip = self.drain.min(yi.len());
255 self.drain -= skip;
256 yi = &mut yi[skip..];
257 y[n..][..yi.len()].copy_from_slice(yi);
259 n += yi.len();
260
261 if self.count == 0 {
262 self.buf.copy_within(N - self.win.overlap..N, 0);
264 }
265
266 self.count += 1;
267 self.idx = self.win.overlap;
268 }
269 &mut y[..n]
270 }
271
272 fn spectrum(&self) -> &[f32] {
273 &self.spectrum[..N / 2 + 1]
274 }
275
276 fn count(&self) -> u32 {
277 self.count
278 }
279
280 fn gain(&self) -> f32 {
281 (N as u32 / 2 * self.count) as f32 * self.win.nenbw * self.win.power
284 }
285
286 fn buf(&self) -> &[f32] {
287 &self.buf[..self.idx]
288 }
289}
290
291#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
293pub struct Break {
294 pub start: usize,
296 pub count: u32,
298 pub avg: u32,
300 pub highest_bin: usize,
302 pub fft_size: usize,
304 pub decimation: usize,
306 pub pending: usize,
308 pub processed: usize,
310}
311
312impl Break {
313 pub fn frequencies(b: &[Self], opts: &MergeOpts) -> Vec<f32> {
315 let Some(bi) = b.last() else { return vec![] };
316 let mut f = Vec::with_capacity(bi.start + bi.highest_bin);
317 for bi in b.iter() {
318 if opts.remove_overlap {
319 f.truncate(bi.start);
320 }
321 let df = 1.0 / bi.effective_fft_size() as f32;
322 f.extend((0..bi.highest_bin).rev().map(|f| f as f32 * df));
323 }
324 assert_eq!(f.len(), bi.start + bi.highest_bin);
325 debug_assert_eq!(f.first(), Some(&0.5));
326 debug_assert_eq!(f.last(), Some(&0.0));
327 f
328 }
329
330 pub fn effective_fft_size(&self) -> usize {
331 self.fft_size << self.decimation
332 }
333}
334
335#[derive(Copy, Clone, Debug, PartialEq, Eq)]
337pub struct MergeOpts {
338 pub remove_overlap: bool,
340 pub min_count: u32,
342}
343
344impl Default for MergeOpts {
345 fn default() -> Self {
346 Self {
347 remove_overlap: true,
348 min_count: 1,
349 }
350 }
351}
352
353#[derive(Copy, Clone, Debug, PartialEq, Eq)]
355pub struct AvgOpts {
356 pub scale: bool,
358 pub count: u32,
360}
361
362impl Default for AvgOpts {
363 fn default() -> Self {
364 Self {
365 scale: false,
366 count: u32::MAX,
367 }
368 }
369}
370
371#[derive(Clone)]
393pub struct PsdCascade<const N: usize> {
394 stages: Vec<Psd<N>>,
395 fft: Arc<dyn Fft<f32>>,
396 stage_depth: usize,
397 detrend: Detrend,
398 win: Arc<Window<N>>,
399 avg: AvgOpts,
400}
401
402impl<const N: usize> PsdCascade<N> {
403 pub fn new(stage_depth: usize) -> Self {
409 assert!(stage_depth > 0);
410 Self {
411 stages: Vec::with_capacity(4),
412 fft: FftPlanner::new().plan_fft_forward(N),
413 stage_depth,
414 detrend: Detrend::None,
415 win: Arc::new(Window::hann()),
416 avg: AvgOpts::default(),
417 }
418 }
419
420 pub fn set_avg(&mut self, avg: AvgOpts) {
421 self.avg = avg;
422 for (i, stage) in self.stages.iter_mut().enumerate() {
423 stage.set_avg(
424 self.avg.count
425 >> if self.avg.scale {
426 self.stage_depth * i
427 } else {
428 0
429 },
430 );
431 }
432 }
433
434 pub fn set_detrend(&mut self, d: Detrend) {
435 self.detrend = d;
436 for stage in self.stages.iter_mut() {
437 stage.set_detrend(self.detrend);
438 }
439 }
440
441 fn get_or_add(&mut self, i: usize) -> &mut Psd<N> {
442 while i >= self.stages.len() {
443 let mut stage = Psd::new(self.fft.clone(), self.win.clone());
444 stage.set_stage_depth(self.stage_depth);
445 stage.set_detrend(self.detrend);
446 stage.set_avg(
447 self.avg.count
448 >> if self.avg.scale {
449 self.stage_depth * i
450 } else {
451 0
452 },
453 );
454 self.stages.push(stage);
455 }
456 &mut self.stages[i]
457 }
458
459 pub fn process(&mut self, x: &[f32]) {
461 let mut a = ([0f32; N], [0f32; N]);
462 let (mut y, mut z) = (&mut a.0, &mut a.1);
463 for mut x in x.chunks(N << self.stage_depth) {
464 let mut i = 0;
465 while !x.is_empty() {
466 let n = self.get_or_add(i).process(x, y).len();
467 core::mem::swap(&mut z, &mut y);
468 x = &z[..n];
469 i += 1;
470 }
471 }
472 }
473
474 pub fn psd(&self, opts: &MergeOpts) -> (Vec<f32>, Vec<Break>) {
484 let mut p = Vec::with_capacity(self.stages.len() * (N / 2 + 1));
485 let mut b = Vec::with_capacity(self.stages.len());
486 let mut n = 0;
487 for stage in self.stages.iter().take_while(|s| s.count >= opts.min_count) {
488 let mut pi = stage.spectrum();
489 if !p.is_empty() {
494 let f_pass = 2 * N / 5;
497 pi = &pi[..f_pass];
498 if opts.remove_overlap {
499 let d = stage.hbf.depth();
501 let f_low = (f_pass + (1 << d) - 1) >> d;
502 p.truncate(p.len() - f_low);
503 }
504 }
505 b.push(Break {
506 start: p.len(),
507 count: stage.count(),
508 avg: stage.avg,
509 highest_bin: pi.len(),
510 fft_size: N,
511 decimation: n,
512 processed: ((N - stage.win.overlap) * stage.count() as usize
513 + stage.win.overlap * stage.count().min(1) as usize),
514 pending: stage.buf().len(),
515 });
516 let g = (1 << n) as f32 / stage.gain();
517 p.extend(pi.iter().rev().map(|pi| pi * g));
518 n += stage.hbf.depth();
519 }
520 (p, b)
540 }
541}
542
543#[cfg(test)]
544mod test {
545 use super::*;
546
547 #[test]
549 #[ignore]
550 fn insn() {
551 let mut s = PsdCascade::<{ 1 << 9 }>::new(3);
552 let x: Vec<_> = (0..1 << 16).map(|_| rand::random::<f32>() - 0.5).collect();
553 for _ in 0..(1 << 12) {
554 s.process(&x);
556 }
557 }
558
559 #[test]
561 fn exact() {
562 const N: usize = 4;
563 let mut s = Psd::<N>::new(
564 FftPlanner::new().plan_fft_forward(N),
565 Arc::new(Window::rectangular()),
566 );
567 let x = vec![1.0; N];
568 let mut y = vec![0.0; N];
569 let y = s.process(&x, &mut y);
570 assert_eq!(y, &x[..N]);
571 println!("{:?}, {}", s.spectrum(), s.gain());
572
573 let mut s = PsdCascade::<N>::new(1);
574 s.process(&x);
575 let merge_opts = MergeOpts {
576 remove_overlap: false,
577 min_count: 0,
578 };
579 let (p, b) = s.psd(&merge_opts);
580 let f = Break::frequencies(&b, &merge_opts);
581 println!("{:?}, {:?}", p, f);
582 assert!(p
583 .iter()
584 .zip([0.0, 4.0 / 3.0, 16.0 / 3.0].iter())
585 .all(|(p, p0)| (p - p0).abs() < 1e-7));
586 assert!(f
587 .iter()
588 .zip([0.5, 0.25, 0.0].iter())
589 .all(|(p, p0)| (p - p0).abs() < 1e-7));
590 }
591
592 #[test]
593 fn test() {
594 assert_eq!(idsp::hbf::HBF_PASSBAND, 0.4);
595
596 let x: Vec<_> = (0..1 << 16)
598 .map(|_| (rand::random::<f32>() - 0.5) * 12f32.sqrt())
599 .collect();
600 let xm = x.iter().map(|x| *x as f64).sum::<f64>() as f32 / x.len() as f32;
601 assert!(xm.abs() < 10.0 / (x.len() as f32).sqrt());
603 let xv = x.iter().map(|x| (x * x) as f64).sum::<f64>() as f32 / x.len() as f32;
604 assert!((xv - 1.0).abs() < 10.0 / (x.len() as f32).sqrt());
606
607 const N: usize = 1 << 9;
608 let n = 3;
609 let mut s = Psd::<N>::new(
610 FftPlanner::new().plan_fft_forward(N),
611 Arc::new(Window::hann()),
612 );
613 s.set_stage_depth(n);
614 let mut y = vec![0.0; x.len() >> n];
615 let y = s.process(&x, &mut y[..]);
616
617 let mut hbf = HbfDecCascade::default();
618 hbf.set_depth(n);
619 assert_eq!(y.len(), (x.len() >> n) - hbf.response_length());
620 let g = 1.0 / s.gain();
621 let p: Vec<_> = s.spectrum().iter().map(|p| p * g).collect();
622 assert!(
624 p.iter()
625 .all(|p| (p * 0.5 - 1.0).abs() < 10.0 / (s.count() as f32).sqrt()),
627 "{:?}",
628 &p[..]
629 );
630
631 let mut d = PsdCascade::<N>::new(n);
632 d.process(&x);
633 let (p, b) = d.psd(&MergeOpts::default());
634 let n = p.len();
636 for (i, bi) in b.iter().enumerate() {
637 let end = b.get(i + 1).map(|bi| bi.start).unwrap_or(n);
639 let pi = &p[bi.start..end];
640 assert!(pi
642 .iter()
643 .all(|p| (p * 0.5 - 1.0).abs() < 10.0 / (bi.count as f32).sqrt()));
645 }
646 }
647}