1#![forbid(unsafe_code)]
2fn validated_samples(samples: &[f64]) -> Option<&[f64]> {
19 if samples.is_empty() || samples.iter().any(|sample| !sample.is_finite()) {
20 None
21 } else {
22 Some(samples)
23 }
24}
25
26pub fn peak_amplitude(samples: &[f64]) -> Option<f64> {
27 let mut values = validated_samples(samples)?.iter().copied();
28 let first = values.next()?.abs();
29
30 Some(values.fold(first, |peak, sample| peak.max(sample.abs())))
31}
32
33pub fn min_amplitude(samples: &[f64]) -> Option<f64> {
34 let mut values = validated_samples(samples)?.iter().copied();
35 let first = values.next()?;
36
37 Some(values.fold(first, f64::min))
38}
39
40pub fn max_amplitude(samples: &[f64]) -> Option<f64> {
41 let mut values = validated_samples(samples)?.iter().copied();
42 let first = values.next()?;
43
44 Some(values.fold(first, f64::max))
45}
46
47pub fn peak_to_peak_amplitude(samples: &[f64]) -> Option<f64> {
48 Some(max_amplitude(samples)? - min_amplitude(samples)?)
49}
50
51pub fn mean_amplitude(samples: &[f64]) -> Option<f64> {
52 let samples = validated_samples(samples)?;
53 let sum = samples.iter().sum::<f64>();
54
55 Some(sum / samples.len() as f64)
56}
57
58pub fn rms_amplitude(samples: &[f64]) -> Option<f64> {
59 let samples = validated_samples(samples)?;
60 let mean_square =
61 samples.iter().map(|sample| sample * sample).sum::<f64>() / samples.len() as f64;
62
63 Some(mean_square.sqrt())
64}
65
66#[cfg(test)]
67mod tests {
68 use super::{
69 max_amplitude, mean_amplitude, min_amplitude, peak_amplitude, peak_to_peak_amplitude,
70 rms_amplitude,
71 };
72
73 #[test]
74 fn computes_basic_amplitude_summaries() {
75 let samples = [-2.0, -0.5, 1.0, 3.0];
76
77 assert_eq!(peak_amplitude(&samples), Some(3.0));
78 assert_eq!(min_amplitude(&samples), Some(-2.0));
79 assert_eq!(max_amplitude(&samples), Some(3.0));
80 assert_eq!(peak_to_peak_amplitude(&samples), Some(5.0));
81 assert_eq!(mean_amplitude(&samples), Some(0.375));
82 }
83
84 #[test]
85 fn computes_rms_amplitude() {
86 let rms = rms_amplitude(&[-1.0, 0.0, 1.0]).unwrap();
87
88 assert!((rms - (2.0_f64 / 3.0).sqrt()).abs() < 1.0e-12);
89 }
90
91 #[test]
92 fn rejects_empty_inputs() {
93 assert_eq!(peak_amplitude(&[]), None);
94 assert_eq!(rms_amplitude(&[]), None);
95 }
96
97 #[test]
98 fn handles_single_values() {
99 assert_eq!(peak_amplitude(&[-0.5]), Some(0.5));
100 assert_eq!(peak_to_peak_amplitude(&[-0.5]), Some(0.0));
101 assert_eq!(mean_amplitude(&[-0.5]), Some(-0.5));
102 }
103
104 #[test]
105 fn rejects_non_finite_samples() {
106 assert_eq!(peak_amplitude(&[0.0, f64::NAN]), None);
107 assert_eq!(mean_amplitude(&[1.0, f64::INFINITY]), None);
108 }
109}