harmos_storage/signal/spectrum/
decibel.rs1#[derive(Clone, Copy, Debug, PartialEq)]
31pub struct Decibel {
32 pub reference: f64,
33 pub factor: f64,
34}
35
36#[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
49impl 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