1#![forbid(unsafe_code)]
2use std::f64::consts::PI;
19
20#[derive(Debug, Clone, Copy, PartialEq)]
21pub struct Frequency {
22 pub hz: f64,
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub enum FrequencyError {
27 InvalidFrequency,
28 InvalidSampleRate,
29}
30
31fn validate_positive(value: f64, error: FrequencyError) -> Result<f64, FrequencyError> {
32 if !value.is_finite() || value <= 0.0 {
33 Err(error)
34 } else {
35 Ok(value)
36 }
37}
38
39impl Frequency {
40 pub fn new(hz: f64) -> Result<Self, FrequencyError> {
41 Ok(Self {
42 hz: validate_positive(hz, FrequencyError::InvalidFrequency)?,
43 })
44 }
45
46 #[must_use]
47 pub fn hz(&self) -> f64 {
48 self.hz
49 }
50
51 #[must_use]
52 pub fn period_seconds(&self) -> f64 {
53 1.0 / self.hz
54 }
55
56 #[must_use]
57 pub fn angular_frequency(&self) -> f64 {
58 2.0 * PI * self.hz
59 }
60}
61
62pub fn period_seconds(hz: f64) -> Result<f64, FrequencyError> {
63 Ok(1.0 / validate_positive(hz, FrequencyError::InvalidFrequency)?)
64}
65
66pub fn angular_frequency(hz: f64) -> Result<f64, FrequencyError> {
67 Ok(2.0 * PI * validate_positive(hz, FrequencyError::InvalidFrequency)?)
68}
69
70pub fn nyquist_frequency(sample_rate_hz: f64) -> Result<f64, FrequencyError> {
71 Ok(validate_positive(sample_rate_hz, FrequencyError::InvalidSampleRate)? / 2.0)
72}
73
74#[cfg(test)]
75mod tests {
76 use std::f64::consts::PI;
77
78 use super::{Frequency, FrequencyError, angular_frequency, nyquist_frequency, period_seconds};
79
80 #[test]
81 fn constructs_frequency_and_reports_values() {
82 let frequency = Frequency::new(4.0).unwrap();
83
84 assert_eq!(frequency.hz(), 4.0);
85 assert_eq!(frequency.period_seconds(), 0.25);
86 assert_eq!(frequency.angular_frequency(), 8.0 * PI);
87 }
88
89 #[test]
90 fn computes_free_frequency_helpers() {
91 assert_eq!(period_seconds(2.0).unwrap(), 0.5);
92 assert_eq!(angular_frequency(2.0).unwrap(), 4.0 * PI);
93 assert_eq!(nyquist_frequency(48_000.0).unwrap(), 24_000.0);
94 }
95
96 #[test]
97 fn rejects_invalid_frequencies() {
98 assert_eq!(Frequency::new(0.0), Err(FrequencyError::InvalidFrequency));
99 assert_eq!(
100 period_seconds(f64::NAN),
101 Err(FrequencyError::InvalidFrequency)
102 );
103 }
104
105 #[test]
106 fn rejects_invalid_sample_rates() {
107 assert_eq!(
108 nyquist_frequency(0.0),
109 Err(FrequencyError::InvalidSampleRate)
110 );
111 assert_eq!(
112 nyquist_frequency(f64::INFINITY),
113 Err(FrequencyError::InvalidSampleRate)
114 );
115 }
116}