Skip to main content

weather_utils/
pressure.rs

1#[allow(unused_imports)]
2#[cfg(feature = "no-std")]
3use micromath::F32Ext;
4#[cfg(not(feature = "no-std"))]
5extern crate std;
6
7use approx::relative_eq;
8
9use crate::{Celsius, Fahrenheit, Temperature};
10
11/// The barometric pressure type (in hPa).
12#[derive(Clone, Copy, Debug, Default)]
13pub struct BarometricPressure(pub f32);
14
15impl BarometricPressure {
16    /// Get the value of the barometric pressure.
17    pub fn value(&self) -> f32 {
18        self.0
19    }
20}
21
22impl From<f32> for BarometricPressure {
23    fn from(value: f32) -> Self {
24        Self(value)
25    }
26}
27
28impl PartialEq for BarometricPressure {
29    fn eq(&self, other: &Self) -> bool {
30        relative_eq!(self.0, other.0, epsilon = 0.01)
31    }
32}
33
34/// The altitude type (in m).
35#[derive(Clone, Copy, Debug, Default)]
36pub struct Altitude(pub f32);
37
38impl Altitude {
39    /// Get the value of the altitude.
40    pub fn value(&self) -> f32 {
41        self.0
42    }
43}
44
45impl From<f32> for Altitude {
46    fn from(value: f32) -> Self {
47        Self(value)
48    }
49}
50
51impl PartialEq for Altitude {
52    fn eq(&self, other: &Self) -> bool {
53        relative_eq!(self.0, other.0, epsilon = 0.01)
54    }
55}
56
57/// The combination of the temperature and the barometric pressure.
58#[derive(Clone, Copy, Debug, Default)]
59pub struct TemperatureAndBarometricPressure<T: Temperature> {
60    /// The temperature (either in °C or °F).
61    pub temperature: T,
62    /// The barometric pressure (in hPa).
63    pub barometric_pressure: BarometricPressure,
64}
65
66impl<T: Temperature> TemperatureAndBarometricPressure<T> {
67    /// Compute the altitude (in m).
68    pub fn altitude(&self) -> Altitude {
69        Altitude(
70            ((1_013.25 / self.barometric_pressure.value()).powf(1.0 / 5.257) - 1.0)
71                * (self.temperature.celsius().value() + 273.15)
72                / 0.0065,
73        )
74    }
75}
76
77impl<T: Temperature + PartialEq> PartialEq for TemperatureAndBarometricPressure<T> {
78    fn eq(&self, other: &Self) -> bool {
79        self.barometric_pressure.eq(&other.barometric_pressure)
80            && self.temperature.eq(&other.temperature)
81    }
82}
83
84impl From<TemperatureAndBarometricPressure<Fahrenheit>>
85    for TemperatureAndBarometricPressure<Celsius>
86{
87    fn from(value: TemperatureAndBarometricPressure<Fahrenheit>) -> Self {
88        Self {
89            temperature: value.temperature.celsius(),
90            barometric_pressure: value.barometric_pressure,
91        }
92    }
93}
94
95impl From<TemperatureAndBarometricPressure<Celsius>>
96    for TemperatureAndBarometricPressure<Fahrenheit>
97{
98    fn from(value: TemperatureAndBarometricPressure<Celsius>) -> Self {
99        Self {
100            temperature: value.temperature.fahrenheit(),
101            barometric_pressure: value.barometric_pressure,
102        }
103    }
104}
105
106#[cfg(test)]
107mod tests {
108    use approx::assert_relative_eq;
109    use rstest::rstest;
110
111    use super::*;
112
113    #[rstest]
114    #[case(TemperatureAndBarometricPressure{ temperature: Celsius(20.55), barometric_pressure: BarometricPressure(991.32) }, Altitude(188.46))]
115    #[case(TemperatureAndBarometricPressure{ temperature: Celsius(17.93), barometric_pressure: 1013.25.into() }, 0.0.into())]
116    #[case(TemperatureAndBarometricPressure{ temperature: Celsius(37.5), barometric_pressure: BarometricPressure(1013.25) }, Altitude(0.0))]
117    #[case(TemperatureAndBarometricPressure{ temperature: Celsius(19.37), barometric_pressure: 962.81.into() }, 439.25.into())]
118    #[case(TemperatureAndBarometricPressure{ temperature: Fahrenheit(99.5), barometric_pressure: BarometricPressure(1013.25) }, Altitude(0.0))]
119    fn test_altitude_computation<T: Temperature>(
120        #[case] input: TemperatureAndBarometricPressure<T>,
121        #[case] expected_altitude: Altitude,
122    ) {
123        assert_eq!(input.altitude(), expected_altitude);
124        assert_relative_eq!(
125            input.altitude().value(),
126            expected_altitude.value(),
127            epsilon = 0.01
128        );
129    }
130
131    #[rstest]
132    #[case(
133        TemperatureAndBarometricPressure{ temperature: Celsius(21.18), barometric_pressure: BarometricPressure(991.32) },
134        TemperatureAndBarometricPressure{ temperature: Fahrenheit(70.12), barometric_pressure: 991.32.into() }
135    )]
136    #[case(
137        TemperatureAndBarometricPressure{ temperature: Celsius(37.5), barometric_pressure: 1013.25.into() },
138        TemperatureAndBarometricPressure{ temperature: Fahrenheit(99.5), barometric_pressure: BarometricPressure(1013.25) }
139    )]
140    fn test_temperature_and_barometric_pressure_celsius_to_fahrenheit_conversion(
141        #[case] input: TemperatureAndBarometricPressure<Celsius>,
142        #[case] expected: TemperatureAndBarometricPressure<Fahrenheit>,
143    ) {
144        let value: TemperatureAndBarometricPressure<Fahrenheit> = input.into();
145        assert_eq!(value, expected);
146    }
147
148    #[rstest]
149    #[case(
150        TemperatureAndBarometricPressure{ temperature: Fahrenheit(70.12), barometric_pressure: BarometricPressure(991.32) },
151        TemperatureAndBarometricPressure{ temperature: Celsius(21.18), barometric_pressure: 991.32.into() }
152    )]
153    #[case(
154        TemperatureAndBarometricPressure{ temperature: Fahrenheit(99.5), barometric_pressure: 1013.25.into() },
155        TemperatureAndBarometricPressure{ temperature: Celsius(37.5), barometric_pressure: BarometricPressure(1013.25) }
156    )]
157    fn test_temperature_and_barometric_pressure_fahrenheit_to_celsius_conversion(
158        #[case] input: TemperatureAndBarometricPressure<Fahrenheit>,
159        #[case] expected: TemperatureAndBarometricPressure<Celsius>,
160    ) {
161        let value: TemperatureAndBarometricPressure<Celsius> = input.into();
162        assert_eq!(value, expected);
163    }
164}