spectrum_analyzer/
limit.rs

1/*
2MIT License
3
4Copyright (c) 2023 Philipp Schuster
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24//! Module for the struct [`FrequencyLimit`].
25
26use core::error::Error;
27use core::fmt::{Display, Formatter};
28
29/// Can be used to specify a desired frequency limit.
30///
31/// If you know that you only need frequencies `f <= 1000Hz`,
32/// `1000 <= f <= 6777`, or `10000 <= f`, then this can help you to accelerate
33/// overall computation speed and memory usage.
34///
35/// Please note that due to frequency inaccuracies the FFT result may not contain
36/// a value for `1000Hz` but for `998.76Hz`!
37#[derive(Debug, Copy, Clone)]
38pub enum FrequencyLimit {
39    /// Interested in all frequencies. [0, sampling_rate/2] (Nyquist theorem).
40    /// Semantically equivalent to "None" limit at all).
41    All,
42    /// Only interested in frequencies `Frequency <= x`. Limit is inclusive.
43    /// Supported values are `0 <= x <= Nyquist-Frequency`.
44    Min(f32),
45    /// Only interested in frequencies `x <= Frequency`. Limit is inclusive.
46    /// Supported values are `0 <= x <= N`.
47    Max(f32),
48    /// Only interested in frequencies `1000 <= f <= 6777` for example. Both values are inclusive.
49    /// The first value of the tuple is equivalent to [`FrequencyLimit::Min`] and the latter
50    /// equivalent to [`FrequencyLimit::Max`]. Furthermore, the first value must not be
51    /// bigger than the second value.
52    Range(f32, f32),
53}
54
55impl FrequencyLimit {
56    /// Returns the minimum value, if any.
57    #[inline]
58    #[must_use]
59    pub const fn maybe_min(&self) -> Option<f32> {
60        match self {
61            Self::Min(min) => Some(*min),
62            Self::Range(min, _) => Some(*min),
63            _ => None,
64        }
65    }
66
67    /// Returns the maximum value, if any.
68    #[inline]
69    #[must_use]
70    pub const fn maybe_max(&self) -> Option<f32> {
71        match self {
72            Self::Max(max) => Some(*max),
73            Self::Range(_, max) => Some(*max),
74            _ => None,
75        }
76    }
77
78    /// Returns the minimum value, panics if it's none.
79    /// Unwrapped version of [`Self::maybe_min`].
80    #[inline]
81    #[must_use]
82    pub fn min(&self) -> f32 {
83        self.maybe_min().expect("Must contain a value!")
84    }
85
86    /// Returns the minimum value, panics if it's none.
87    /// Unwrapped version of [`Self::maybe_max`].
88    #[inline]
89    #[must_use]
90    pub fn max(&self) -> f32 {
91        self.maybe_max().expect("Must contain a value!")
92    }
93
94    /// Verifies that the frequency limit has sane values and takes the maximum possible
95    /// frequency into account.
96    pub fn verify(&self, max_detectable_frequency: f32) -> Result<(), FrequencyLimitError> {
97        match self {
98            Self::All => Ok(()),
99            Self::Min(x) | Self::Max(x) => {
100                if *x < 0.0 {
101                    Err(FrequencyLimitError::ValueBelowMinimum(*x))
102                } else if *x > max_detectable_frequency {
103                    Err(FrequencyLimitError::ValueAboveNyquist(*x))
104                } else {
105                    Ok(())
106                }
107            }
108            Self::Range(min, max) => {
109                Self::Min(*min).verify(max_detectable_frequency)?;
110                Self::Max(*max).verify(max_detectable_frequency)?;
111                if min > max {
112                    Err(FrequencyLimitError::InvalidRange(*min, *max))
113                } else {
114                    Ok(())
115                }
116            }
117        }
118    }
119}
120
121/// Possible errors when creating a [`FrequencyLimit`]-object.
122#[derive(Debug)]
123pub enum FrequencyLimitError {
124    /// If the minimum value is below 0. Negative frequencies are not supported.
125    ValueBelowMinimum(f32),
126    /// If the maximum value is above Nyquist frequency. Nyquist-Frequency is the maximum
127    /// detectable frequency.
128    ValueAboveNyquist(f32),
129    /// Either the corresponding value is below or above the minimum/maximum or the
130    /// first member of the tuple is bigger than the second.
131    InvalidRange(f32, f32),
132}
133
134impl Display for FrequencyLimitError {
135    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
136        match self {
137            Self::ValueBelowMinimum(x) => write!(f, "Value below minimum: {x}"),
138            Self::ValueAboveNyquist(x) => write!(f, "Value above Nyquist: {x}"),
139            Self::InvalidRange(min, max) => write!(f, "Invalid range: {min} <= x <= {max}"),
140        }
141    }
142}
143
144impl Error for FrequencyLimitError {}
145
146#[cfg(test)]
147mod tests {
148    use crate::FrequencyLimit;
149
150    #[test]
151    fn test_panic_min_below_minimum() {
152        let _ = FrequencyLimit::Min(-1.0).verify(0.0).unwrap_err();
153    }
154
155    #[test]
156    fn test_panic_min_above_nyquist() {
157        let _ = FrequencyLimit::Min(1.0).verify(0.0).unwrap_err();
158    }
159
160    #[test]
161    fn test_panic_max_below_minimum() {
162        let _ = FrequencyLimit::Max(-1.0).verify(0.0).unwrap_err();
163    }
164
165    #[test]
166    fn test_panic_max_above_nyquist() {
167        let _ = FrequencyLimit::Max(1.0).verify(0.0).unwrap_err();
168    }
169
170    #[test]
171    fn test_panic_range_min() {
172        let _ = FrequencyLimit::Range(-1.0, 0.0).verify(0.0).unwrap_err();
173    }
174
175    #[test]
176    fn test_panic_range_max() {
177        let _ = FrequencyLimit::Range(0.0, 1.0).verify(0.0).unwrap_err();
178    }
179
180    #[test]
181    fn test_panic_range_order() {
182        let _ = FrequencyLimit::Range(0.0, -1.0).verify(0.0).unwrap_err();
183    }
184
185    #[test]
186    fn test_ok() {
187        FrequencyLimit::Min(50.0).verify(100.0).unwrap();
188        FrequencyLimit::Max(50.0).verify(100.0).unwrap();
189        // useless, but not an hard error
190        FrequencyLimit::Range(50.0, 50.0).verify(100.0).unwrap();
191        FrequencyLimit::Range(50.0, 70.0).verify(100.0).unwrap();
192    }
193}