1use super::measurement::*;
4use crate::{density::Density, pressure::Pressure, temperature::Temperature};
5
6#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
38#[derive(Copy, Clone, Debug, Default)]
39pub struct Humidity {
40 relative_humidity: f64, }
42
43impl Humidity {
44 pub fn from_percent(percent: f64) -> Self {
46 Humidity {
47 relative_humidity: percent,
48 }
49 }
50
51 pub fn from_ratio(relative_humidity: f64) -> Self {
53 Humidity {
54 relative_humidity: relative_humidity * 100.0,
55 }
56 }
57 pub fn as_ratio(&self) -> f64 {
59 self.relative_humidity / 100.0
60 }
61
62 pub fn as_percent(&self) -> f64 {
64 self.relative_humidity
65 }
66
67 #[cfg(feature = "std")]
71 pub fn as_dewpoint(&self, temp: Temperature) -> Temperature {
72 let humidity = self.relative_humidity / 100.0;
73 let celsius = temp.as_celsius();
74 let dewpoint: f64 = 243.04 * (humidity.ln() + ((17.625 * celsius) / (243.04 + celsius)))
75 / (17.625 - humidity.ln() - ((17.625 * celsius) / (243.04 + celsius)));
76 Temperature::from_celsius(dewpoint)
77 }
78
79 #[cfg(not(feature = "std"))]
83 pub fn as_dewpoint(&self, temp: Temperature) -> Temperature {
84 let humidity = self.relative_humidity / 100.0;
85 let celsius = temp.as_celsius();
86 let humidity_ln = libm::log(humidity);
87 let dewpoint: f64 = 243.04 * (humidity_ln + ((17.625 * celsius) / (243.04 + celsius)))
88 / (17.625 - humidity_ln - ((17.625 * celsius) / (243.04 + celsius)));
89 Temperature::from_celsius(dewpoint)
90 }
91
92 #[cfg(feature = "std")]
96 pub fn as_vapor_pressure(&self, temp: Temperature) -> Pressure {
97 let temp = temp.as_celsius();
98 let saturation_vapor_pressure =
99 0.61121 * ((18.678 - (temp / 234.5)) * (temp / (257.14 + temp))).exp();
100 Pressure::from_kilopascals((self.relative_humidity * saturation_vapor_pressure) / 100.0)
101 }
102
103 #[cfg(not(feature = "std"))]
107 pub fn as_vapor_pressure(&self, temp: Temperature) -> Pressure {
108 let temp = temp.as_celsius();
109 let saturation_vapor_pressure =
110 0.61121 * libm::exp((18.678 - (temp / 234.5)) * (temp / (257.14 + temp)));
111 Pressure::from_kilopascals((self.relative_humidity * saturation_vapor_pressure) / 100.0)
112 }
113
114 pub fn as_absolute_humidity(&self, temp: Temperature) -> Density {
117 let density = self.as_vapor_pressure(temp).as_pascals() / (temp.as_kelvin() * 461.5);
120 Density::from_kilograms_per_cubic_meter(density)
121 }
122
123 #[cfg(feature = "std")]
127 pub fn from_dewpoint(dewpoint: Temperature, temp: Temperature) -> Humidity {
128 let dewpoint = dewpoint.as_celsius();
129 let temp = temp.as_celsius();
130 let rh = 100.0
131 * (((17.625 * dewpoint) / (243.04 + dewpoint)).exp()
132 / ((17.625 * temp) / (243.04 + temp)).exp());
133 Humidity::from_percent(rh)
134 }
135
136 #[cfg(not(feature = "std"))]
140 pub fn from_dewpoint(dewpoint: Temperature, temp: Temperature) -> Humidity {
141 let dewpoint = dewpoint.as_celsius();
142 let temp = temp.as_celsius();
143 let rh = 100.0
144 * (libm::exp((17.625 * dewpoint) / (243.04 + dewpoint))
145 / libm::exp((17.625 * temp) / (243.04 + temp)));
146 Humidity::from_percent(rh)
147 }
148}
149
150impl Measurement for Humidity {
151 fn get_base_units_name(&self) -> &'static str {
152 "%"
153 }
154
155 fn as_base_units(&self) -> f64 {
156 self.relative_humidity
157 }
158
159 fn from_base_units(relative_humidity: f64) -> Self {
160 Self::from_percent(relative_humidity)
161 }
162}
163
164impl ::core::cmp::Eq for Humidity {}
165impl ::core::cmp::PartialEq for Humidity {
166 fn eq(&self, other: &Self) -> bool {
167 self.as_base_units() == other.as_base_units()
168 }
169}
170
171impl ::core::cmp::PartialOrd for Humidity {
172 fn partial_cmp(&self, other: &Self) -> Option<::core::cmp::Ordering> {
173 self.as_base_units().partial_cmp(&other.as_base_units())
174 }
175}
176
177implement_display!(Humidity);
178
179#[cfg(test)]
180mod test {
181 use crate::{humidity::*, test_utils::assert_almost_eq};
182
183 #[test]
185 fn percent() {
186 let t = Humidity::from_percent(50.0);
187 let o = t.as_percent();
188
189 assert_almost_eq(o, 50.0);
190 }
191
192 #[test]
193 fn ratio() {
194 let t = Humidity::from_ratio(0.1);
195 let o = t.as_ratio();
196 assert_almost_eq(o, 0.1);
197 }
198 #[test]
200 fn to_dewpoint1() {
201 let humidity = Humidity::from_percent(85.0);
202 let temp = Temperature::from_celsius(18.0);
203 let dewpoint = humidity.as_dewpoint(temp);
204 assert_almost_eq(dewpoint.as_celsius(), 15.44);
205 }
206 #[test]
207 fn to_dewpoint2() {
208 let humidity = Humidity::from_percent(40.0);
209 let temp = Temperature::from_celsius(5.0);
210 let dewpoint = humidity.as_dewpoint(temp);
211 assert_almost_eq(dewpoint.as_celsius(), -7.5);
212 }
213 #[test]
214 fn to_dewpoint3() {
215 let humidity = Humidity::from_percent(95.0);
216 let temp = Temperature::from_celsius(30.0);
217 let dewpoint = humidity.as_dewpoint(temp);
218 assert_almost_eq(dewpoint.as_celsius(), 29.11);
219 }
220 #[test]
221 fn from_dewpoint1() {
222 let temp = Temperature::from_celsius(18.0);
223 let dewpoint = Temperature::from_celsius(15.44);
224 let rh = Humidity::from_dewpoint(dewpoint, temp);
225 assert_almost_eq(rh.as_percent(), 85.0);
226 }
227 #[test]
228 fn vapour_pressure() {
229 let humidity = Humidity::from_percent(60.0);
230 let temp = Temperature::from_celsius(25.0);
231 let vp = humidity.as_vapor_pressure(temp);
232 assert_almost_eq(vp.as_hectopascals(), 19.011);
233 }
234 #[test]
235 fn absolute_humidity() {
237 let humidity = Humidity::from_percent(60.0);
238 let temp = Temperature::from_celsius(25.0);
239 let density = humidity.as_absolute_humidity(temp);
240 assert_almost_eq(density.as_kilograms_per_cubic_meter(), 0.0138166);
241 }
242 #[test]
243 fn from_dewpoint2() {
245 let humidity = Humidity::from_percent(95.0);
246 let temp = Temperature::from_celsius(30.0);
247 let dewpoint = humidity.as_dewpoint(temp);
248 let rh = Humidity::from_dewpoint(dewpoint, temp);
249 assert_almost_eq(humidity.as_percent(), rh.as_percent());
250 }
251
252 #[test]
254 fn eq() {
255 let a = Humidity::from_percent(20.0);
256 let b = Humidity::from_percent(20.0);
257 assert_eq!(a == b, true);
258 }
259
260 #[test]
261 fn neq() {
262 let a = Humidity::from_percent(20.0);
263 let b = Humidity::from_percent(19.0);
264 assert_eq!(a == b, false);
265 }
266
267 #[test]
268 fn cmp() {
269 let a = Humidity::from_percent(19.0);
270 let b = Humidity::from_percent(20.0);
271 assert_eq!(a < b, true);
272 assert_eq!(a <= b, true);
273 assert_eq!(a > b, false);
274 assert_eq!(a >= b, false);
275 }
276}