Skip to main content

irox_units/units/
temperature.rs

1// SPDX-License-Identifier: MIT
2// Copyright 2023 IROX Contributors
3
4//!
5//! Contains [`Temperature`] and [`TemperatureUnits`] - physical measurements of the SI "Temperature" quantity
6//!
7use crate::units::{FromUnits, Unit};
8
9///
10/// Represents a specific temperature unit - SI or otherwise
11#[derive(Debug, Clone, Copy, Default, Eq, PartialEq)]
12#[non_exhaustive]
13pub enum TemperatureUnits {
14    /// Referenced `0` is Absolute Zero and counts up at the same rate as [`TemperatureUnits::Celsius`].  0 Kelvin is
15    /// -273.15 Celsius, -459.67 Fahrenheit, and 0 Rankine
16    #[default]
17    Kelvin,
18
19    /// Referenced such that `0` is the melting point of Ice, and `100` is the boiling point of water.
20    /// Increments at the same rate as `Kelvin` as a constant offset.  0 Celsius is 273.15 Kelvin,
21    /// 32 Fahrenheit, and 491.67 Rankine.
22    Celsius,
23
24    /// Freedom unit referenced such that `0` is the freezing point of a salty bath, and `100` is
25    /// the approximate value of a human body.  0 Fahrenheit is -17.78 Celsius, 255.37 Kelvin, and
26    /// 459.67 Rankine
27    Fahrenheit,
28
29    /// Fahrenheit, except referenced to Absolute Zero.  0 Rankine is 0 Kelvin, -273.15 Celsius and
30    /// -459.67 Fahrenheit.
31    Rankine,
32}
33macro_rules! scale {
34    ($val:expr,$factor:expr,$type:ident) => {
35        $val * $factor as $type
36    };
37}
38macro_rules! offset {
39    ($val:expr,$shift:expr,$type:ident) => {
40        $val + $shift as $type
41    };
42}
43
44macro_rules! from_units_length {
45    ($type:ident) => {
46        impl crate::units::FromUnits<$type> for TemperatureUnits {
47            fn from(&self, value: $type, source_unit: Self) -> $type {
48                match self {
49                    // target
50                    TemperatureUnits::Kelvin => match source_unit {
51                        // source
52                        TemperatureUnits::Kelvin => value,
53                        TemperatureUnits::Celsius => offset!(value, CELSIUS_KELVIN_OFFSET, $type),
54                        TemperatureUnits::Fahrenheit => scale!(
55                            offset!(value, FAHRENHEIT_RANKINE_OFFSET, $type),
56                            (1.0 / CELSIUS_FAHRENHEIT_FACTOR),
57                            $type
58                        ),
59                        TemperatureUnits::Rankine => {
60                            scale!(value, (1.0 / CELSIUS_FAHRENHEIT_FACTOR), $type)
61                        }
62                    },
63                    TemperatureUnits::Celsius => match source_unit {
64                        TemperatureUnits::Kelvin => offset!(value, -CELSIUS_KELVIN_OFFSET, $type),
65                        TemperatureUnits::Celsius => value,
66                        TemperatureUnits::Fahrenheit => scale!(
67                            offset!(value, -CELSIUS_FAHRENHEIT_OFFSET, $type),
68                            (1.0 / CELSIUS_FAHRENHEIT_FACTOR),
69                            $type
70                        ),
71                        TemperatureUnits::Rankine => offset!(
72                            scale!(value, (1.0 / CELSIUS_FAHRENHEIT_FACTOR), $type),
73                            -CELSIUS_KELVIN_OFFSET,
74                            $type
75                        ),
76                    },
77                    TemperatureUnits::Fahrenheit => match source_unit {
78                        TemperatureUnits::Kelvin => offset!(
79                            scale!(value, CELSIUS_FAHRENHEIT_FACTOR, $type),
80                            -FAHRENHEIT_RANKINE_OFFSET,
81                            $type
82                        ),
83                        TemperatureUnits::Celsius => offset!(
84                            scale!(value, CELSIUS_FAHRENHEIT_FACTOR, $type),
85                            CELSIUS_FAHRENHEIT_OFFSET,
86                            $type
87                        ),
88                        TemperatureUnits::Fahrenheit => value,
89                        TemperatureUnits::Rankine => {
90                            offset!(value, -FAHRENHEIT_RANKINE_OFFSET, $type)
91                        }
92                    },
93                    TemperatureUnits::Rankine => match source_unit {
94                        TemperatureUnits::Kelvin => scale!(value, CELSIUS_FAHRENHEIT_FACTOR, $type),
95                        TemperatureUnits::Celsius => scale!(
96                            offset!(value, CELSIUS_KELVIN_OFFSET, $type),
97                            CELSIUS_FAHRENHEIT_FACTOR,
98                            $type
99                        ),
100                        TemperatureUnits::Fahrenheit => {
101                            offset!(value, FAHRENHEIT_RANKINE_OFFSET, $type)
102                        }
103                        TemperatureUnits::Rankine => value,
104                    },
105                }
106            }
107        }
108    };
109}
110
111basic_unit!(Temperature, TemperatureUnits, Kelvin);
112from_units_length!(f32);
113from_units_length!(f64);
114
115impl Temperature {
116    #[must_use]
117    pub fn new_celsius(value: f64) -> Temperature {
118        Self::new(value, TemperatureUnits::Celsius)
119    }
120
121    #[must_use]
122    pub fn new_fahrenheit(value: f64) -> Temperature {
123        Self::new(value, TemperatureUnits::Fahrenheit)
124    }
125
126    #[must_use]
127    pub fn new_kelvin(value: f64) -> Temperature {
128        Self::new(value, TemperatureUnits::Kelvin)
129    }
130
131    #[must_use]
132    pub fn new_rankine(value: f64) -> Temperature {
133        Self::new(value, TemperatureUnits::Rankine)
134    }
135
136    #[must_use]
137    pub fn as_celsius(&self) -> Temperature {
138        self.as_unit(TemperatureUnits::Celsius)
139    }
140
141    #[must_use]
142    pub fn as_kelvin(&self) -> Temperature {
143        self.as_unit(TemperatureUnits::Kelvin)
144    }
145
146    #[must_use]
147    pub fn as_fahrenheit(&self) -> Temperature {
148        self.as_unit(TemperatureUnits::Fahrenheit)
149    }
150
151    #[must_use]
152    pub fn as_rankine(&self) -> Temperature {
153        self.as_unit(TemperatureUnits::Rankine)
154    }
155}
156
157pub const CELSIUS_KELVIN_OFFSET: f64 = 273.15;
158pub const CELSIUS_FAHRENHEIT_OFFSET: f64 = 32.;
159pub const CELSIUS_FAHRENHEIT_FACTOR: f64 = 1.8;
160pub const FAHRENHEIT_RANKINE_OFFSET: f64 = 459.67;
161
162#[cfg(test)]
163mod test {
164    use crate::units::temperature::Temperature;
165
166    #[test]
167    pub fn tests() {
168        let abs_k = Temperature::new_kelvin(0.0);
169        assert_eq!(abs_k.as_kelvin().value, 0.0);
170        assert_eq!(abs_k.as_celsius().value, -273.15);
171        assert_eq!(abs_k.as_fahrenheit().value, -459.67);
172        assert_eq!(abs_k.as_rankine().value, 0.0);
173
174        let zerof = Temperature::new_fahrenheit(0.0);
175        assert_eq!(zerof.as_kelvin().value, 255.37222222222223);
176        assert_eq!(zerof.as_celsius().value, -17.77777777777778);
177        assert_eq!(zerof.as_fahrenheit().value, 0.0);
178        assert_eq!(zerof.as_rankine().value, 459.67);
179
180        let zeroc = Temperature::new_celsius(0.0);
181        assert_eq!(zeroc.as_kelvin().value, 273.15);
182        assert_eq!(zeroc.as_celsius().value, 0.0);
183        assert_eq!(zeroc.as_fahrenheit().value, 32.0);
184        assert_eq!(zeroc.as_rankine().value, 491.66999999999996);
185
186        let zeror = Temperature::new_rankine(0.0);
187        assert_eq!(zeror.as_kelvin().value, 0.0);
188        assert_eq!(zeror.as_celsius().value, -273.15);
189        assert_eq!(zeror.as_fahrenheit().value, -459.67);
190        assert_eq!(zeror.as_rankine().value, 0.0);
191
192        let stp = Temperature::new_celsius(15.0);
193        assert_eq!(stp.as_kelvin().value, 288.15);
194        assert_eq!(stp.as_celsius().value, 15.0);
195        assert_eq!(stp.as_fahrenheit().value, 59.);
196        assert_eq!(stp.as_rankine().value, 518.67);
197
198        let boil = Temperature::new_kelvin(373.1339);
199        assert_eq!(boil.as_kelvin().value, 373.1339);
200        assert_eq!(boil.as_celsius().value, 99.9839);
201        assert_eq!(boil.as_fahrenheit().value, 211.97102);
202        assert_eq!(boil.as_rankine().value, 671.64102);
203
204        let boil = Temperature::new_celsius(99.9839);
205        assert_eq!(boil.as_kelvin().value, 373.1339);
206        assert_eq!(boil.as_celsius().value, 99.9839);
207        assert_eq!(boil.as_fahrenheit().value, 211.97102);
208        assert_eq!(boil.as_rankine().value, 671.64102);
209
210        let boil = Temperature::new_fahrenheit(211.97102);
211        assert_eq!(boil.as_kelvin().value, 373.13390000000004);
212        assert_eq!(boil.as_celsius().value, 99.9839);
213        assert_eq!(boil.as_fahrenheit().value, 211.97102);
214        assert_eq!(boil.as_rankine().value, 671.64102);
215
216        let boil = Temperature::new_rankine(671.64102);
217        assert_eq!(boil.as_kelvin().value, 373.13390000000004);
218        assert_eq!(boil.as_celsius().value, 99.98390000000006);
219        assert_eq!(boil.as_fahrenheit().value, 211.97102);
220        assert_eq!(boil.as_rankine().value, 671.64102);
221    }
222}