Skip to main content

harmos_storage/signal/spectrum/
decibel.rs

1/* ***
2 *
3 * Harmos
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 *
7 * ***
8 */
9
10/* === Modules === */
11
12/* === Re-Exports === */
13
14/* === Imports === */
15
16// Standard Library Imports
17
18// Third Party Imports
19
20// Local Imports
21
22/* === Constants === */
23
24/* === Macros === */
25
26/* === Type Aliases === */
27
28/* === Structs === */
29
30#[derive(Clone, Copy, Debug, PartialEq)]
31pub struct Decibel {
32    pub reference: f64,
33    pub factor: f64,
34}
35
36/* === Enums === */
37
38#[derive(Clone, Copy, Debug, Default, PartialEq)]
39pub enum Scale {
40    #[default]
41    Linear,
42
43    Decibel(Decibel),
44    Ratio {
45        factor: f64,
46    },
47}
48
49/* === Traits === */
50
51/* === Implementations === */
52
53impl Decibel {
54    #[must_use]
55    pub fn amplitude(reference: f64) -> Self {
56        Self { reference, factor: 20.0 }
57    }
58
59    #[must_use]
60    pub fn power(reference: f64) -> Self {
61        Self { reference, factor: 10.0 }
62    }
63
64    #[must_use]
65    pub fn volt() -> Self {
66        Self::amplitude(1.0)
67    }
68
69    #[must_use]
70    pub fn millivolt() -> Self {
71        Self::amplitude(1e-3)
72    }
73
74    #[must_use]
75    pub fn microvolt() -> Self {
76        Self::amplitude(1e-6)
77    }
78
79    #[must_use]
80    pub fn ampere() -> Self {
81        Self::amplitude(1.0)
82    }
83
84    #[must_use]
85    pub fn milliampere() -> Self {
86        Self::amplitude(1e-3)
87    }
88
89    #[must_use]
90    pub fn microampere() -> Self {
91        Self::amplitude(1e-6)
92    }
93
94    #[must_use]
95    pub fn sound_pressure() -> Self {
96        Self::amplitude(20e-6)
97    }
98
99    #[must_use]
100    pub fn watt() -> Self {
101        Self::power(1.0)
102    }
103
104    #[must_use]
105    pub fn milliwatt() -> Self {
106        Self::power(1e-3)
107    }
108
109    #[must_use]
110    pub fn to_db(&self, linear: f64) -> f64 {
111        if linear > 0.0 { self.factor * (linear / self.reference).log10() } else { f64::NEG_INFINITY }
112    }
113
114    #[must_use]
115    pub fn to_linear(&self, db: f64) -> f64 {
116        self.reference * 10.0_f64.powf(db / self.factor)
117    }
118
119    #[must_use]
120    pub fn is_compatible(&self, other: &Self) -> bool {
121        (self.factor - other.factor).abs() < f64::EPSILON
122    }
123
124    #[must_use]
125    pub fn offset_from(&self, other: &Self) -> Option<f64> {
126        self.is_compatible(other).then(|| self.factor * (other.reference / self.reference).log10())
127    }
128}
129
130impl Scale {
131    #[must_use]
132    pub fn factor(&self) -> Option<f64> {
133        match self {
134            Self::Linear => None,
135            Self::Decibel(scale) => Some(scale.factor),
136            Self::Ratio { factor } => Some(*factor),
137        }
138    }
139
140    #[must_use]
141    pub fn reference(&self) -> Option<f64> {
142        match self {
143            Self::Decibel(scale) => Some(scale.reference),
144            Self::Linear | Self::Ratio { .. } => None,
145        }
146    }
147
148    #[must_use]
149    pub fn is_compatible(&self, other: &Self) -> bool {
150        match (self, other) {
151            (Self::Linear, Self::Linear) => true,
152            (Self::Decibel(left), Self::Decibel(right)) => left.is_compatible(right),
153            (Self::Decibel(scale), Self::Ratio { factor }) | (Self::Ratio { factor }, Self::Decibel(scale)) => {
154                (scale.factor - factor).abs() < f64::EPSILON
155            }
156            (Self::Ratio { factor: left }, Self::Ratio { factor: right }) => (left - right).abs() < f64::EPSILON,
157            (Self::Linear, _) | (_, Self::Linear) => false,
158        }
159    }
160
161    #[must_use]
162    pub fn sum(&self, other: &Self) -> Option<Self> {
163        match (self, other) {
164            (Self::Linear, Self::Linear) => Some(Self::Linear),
165            (Self::Decibel(scale), Self::Ratio { .. }) | (Self::Ratio { .. }, Self::Decibel(scale)) if self.is_compatible(other) => {
166                Some(Self::Decibel(*scale))
167            }
168            (Self::Ratio { factor }, Self::Ratio { .. }) if self.is_compatible(other) => Some(Self::Ratio { factor: *factor }),
169            _ => None,
170        }
171    }
172
173    #[must_use]
174    pub fn difference(&self, other: &Self) -> Option<Self> {
175        match (self, other) {
176            (Self::Linear, Self::Linear) => Some(Self::Linear),
177            (Self::Decibel(left), Self::Decibel(_)) if self.is_compatible(other) => Some(Self::Ratio { factor: left.factor }),
178            (Self::Decibel(scale), Self::Ratio { .. }) if self.is_compatible(other) => Some(Self::Decibel(*scale)),
179            (Self::Ratio { factor }, Self::Ratio { .. }) if self.is_compatible(other) => Some(Self::Ratio { factor: *factor }),
180            _ => None,
181        }
182    }
183}
184
185/* === Public Functions === */
186
187/* === Private Functions === */
188
189/* === Tests === */