diff --git a/src/bin/psd.rs b/src/bin/psd.rs index 51bccfb..fb846ff 100644 --- a/src/bin/psd.rs +++ b/src/bin/psd.rs @@ -379,6 +379,14 @@ impl App { fn row0(&mut self, ui: &mut Ui) { ui.checkbox(&mut self.hold, "Hold") .on_hover_text("Stop updating plot\nAcquiusition continues in background"); + ui.separator(); + if ui + .button("Reset") + .on_hover_text("Reset PSD stages and begin anew") + .clicked() + { + self.cmd_send.send(Cmd::Reset).unwrap(); + } ui.add( Slider::new(&mut self.repaint, 0.01..=10.0) .text("Repaint") @@ -387,6 +395,15 @@ impl App { ) .on_hover_text("Request repaint after timeout (seconds)"); ui.separator(); + ui.add( + Slider::new(&mut self.acq.fs, 1e-3..=1e9) + .text("Sample rate") + .custom_formatter(|x, _| format!("{:.2e}", x)) + .suffix(" Hz") + .logarithmic(true), + ) + .on_hover_text("Input sample rate"); + ui.separator(); ComboBox::from_label("Detrend") .selected_text(format!("{:?}", self.acq.detrend)) .show_ui(ui, |ui| { @@ -398,41 +415,22 @@ impl App { }) .response .on_hover_text("Segment detrending method"); - ui.separator(); - ui.add( - Slider::new(&mut self.acq.fs, 1e-3..=1e9) - .text("Sample rate") - .custom_formatter(|x, _| format!("{:.2e}", x)) - .suffix(" Hz") - .logarithmic(true), - ) - .on_hover_text("Input sample rate"); - ui.separator(); - if ui - .button("Reset") - .on_hover_text("Reset PSD stages and begin anew") - .clicked() - { - self.cmd_send.send(Cmd::Reset).unwrap(); - } } fn row1(&mut self, ui: &mut Ui) { ui.add( - Slider::new(&mut self.acq.avg_max, 1..=1_000_000.min(self.acq.avg)) - .text("Averaging limit") + Slider::new(&mut self.acq.avg, 1..=1_000_000_000) + .text("Averages") .logarithmic(true), ) - .on_hover_text("Averaging limit:\nClip averaging amount to this"); + .on_hover_text("Target averaging count at the top stage"); ui.separator(); ui.add( - Slider::new(&mut self.acq.avg, 1..=1_000_000_000) - .text("Averages") + Slider::new(&mut self.acq.avg_max, 1..=1_000_000.min(self.acq.avg)) + .text("Averaging limit") .logarithmic(true), ) - .on_hover_text( - "Averaging count:\nthe averaging starts as boxcar,\nthen continues exponential", - ); + .on_hover_text("Averaging limit:\nClip averaging at each stage to this\nThe averaging starts as boxcar,\nthen continues exponential"); ui.separator(); ui.add( Slider::new(&mut self.acq.avg_min, 0..=self.acq.avg_max) @@ -440,12 +438,12 @@ impl App { .logarithmic(true), ) .on_hover_text("Minimum averaging count to\nshow data from a stage"); - ui.separator(); - ui.checkbox(&mut self.acq.keep_overlap, "Keep overlap") - .on_hover_text("Do not remove low-resolution bins"); } fn row2(&mut self, ui: &mut Ui) { + ui.checkbox(&mut self.acq.keep_overlap, "Keep overlap") + .on_hover_text("Do not remove low-resolution bins"); + ui.separator(); ui.checkbox(&mut self.acq.integrate, "Integrate") .on_hover_text("Show integrated PSD as linear cumulative sum"); ui.separator(); diff --git a/src/psd.rs b/src/psd.rs index 1bdd745..abe7b10 100644 --- a/src/psd.rs +++ b/src/psd.rs @@ -222,6 +222,8 @@ impl PsdStage for Psd { // fft in-place self.fft.process(&mut c); + let is_first = self.count == 0; + // normalize and keep for EWMA let g = if self.count > self.avg { let g = self.avg as f32 / self.count as f32; @@ -230,6 +232,8 @@ impl PsdStage for Psd { } else { 1.0 }; + self.count += 1; + // convert positive frequency spectrum to power and accumulate for (c, p) in c[..N / 2 + 1] .iter() @@ -238,7 +242,7 @@ impl PsdStage for Psd { *p = g * *p + c.norm_sqr(); } - let start = if self.count == 0 { + let start = if is_first { // decimate entire segment into lower half, keep overlap later 0 } else { @@ -258,12 +262,10 @@ impl PsdStage for Psd { y[n..][..yi.len()].copy_from_slice(yi); n += yi.len(); - if self.count == 0 { + if is_first { // keep overlap after decimating entire segment self.buf.copy_within(N - self.win.overlap..N, 0); } - - self.count += 1; self.idx = self.win.overlap; } &mut y[..n] @@ -482,7 +484,11 @@ impl PsdCascade { let mut b = Vec::with_capacity(self.stages.len()); let mut decimation = 0; let f_pass = 2 * N / 5; // 0.8 Nyquist, floor - let full = self.stages.iter().take_while(|s| s.count > 0).count(); + let full = self + .stages + .iter() + .take_while(|stage| stage.count() >= opts.min_count) + .count(); for (i, stage) in self.stages.iter().enumerate() { // a stage yields frequency bins 0..N/2 from DC up to its nyquist // 0..floor(0.4*N) is its passband if it was preceeded by a decimator