atm_refraction/air/atmosphere/
mod.rs

1mod pressure_profile;
2pub mod vertical_profile;
3
4use self::{
5    pressure_profile::PressureProfile,
6    vertical_profile::{FunctionDef, VerticalProfile, VerticalProfileBuilder},
7};
8
9#[cfg(feature = "serialization")]
10use cubic_splines::BoundaryCondition;
11
12/// mu*g/R
13pub const A: f64 = 0.03416320331088684;
14
15#[derive(Clone, Copy, Debug)]
16#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
17pub struct PressureFixedPoint {
18    altitude: f64,
19    pressure: f64,
20}
21
22#[derive(Clone, Debug)]
23#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
24pub struct FunctionDefWithAlt {
25    altitude: f64,
26    function: FunctionDef,
27}
28
29#[derive(Clone, Copy, Debug)]
30#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
31pub struct TemperatureFixedPoint {
32    altitude: f64,
33    temperature: f64,
34}
35
36#[derive(Clone, Copy, Debug)]
37#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
38pub struct HumidityFixedPoint {
39    altitude: f64,
40    humidity: f64,
41}
42
43#[derive(Clone, Debug)]
44#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
45pub struct AtmosphereDef {
46    #[cfg_attr(feature = "serialization", serde(default = "default_pressure"))]
47    pressure: PressureFixedPoint,
48    first_temperature_function: FunctionDef,
49    #[cfg_attr(feature = "serialization", serde(default))]
50    next_functions: Vec<FunctionDefWithAlt>,
51    temperature_fixed_point: Option<TemperatureFixedPoint>,
52
53    #[cfg_attr(
54        feature = "serialization",
55        serde(default = "default_first_humidity_function")
56    )]
57    first_humidity_function: FunctionDef,
58    #[cfg_attr(feature = "serialization", serde(default))]
59    next_humidity_functions: Vec<FunctionDefWithAlt>,
60    humidity_fixed_point: Option<HumidityFixedPoint>,
61}
62
63impl AtmosphereDef {
64    pub fn us_76() -> Self {
65        AtmosphereDef {
66            pressure: PressureFixedPoint {
67                altitude: 0.0,
68                pressure: 101325.0,
69            },
70            first_temperature_function: FunctionDef::Linear { gradient: -0.0065 },
71            next_functions: vec![
72                FunctionDefWithAlt {
73                    altitude: 11e3,
74                    function: FunctionDef::Linear { gradient: 0.0 },
75                },
76                FunctionDefWithAlt {
77                    altitude: 20e3,
78                    function: FunctionDef::Linear { gradient: 0.001 },
79                },
80                FunctionDefWithAlt {
81                    altitude: 32e3,
82                    function: FunctionDef::Linear { gradient: 0.0028 },
83                },
84                FunctionDefWithAlt {
85                    altitude: 47e3,
86                    function: FunctionDef::Linear { gradient: 0.0 },
87                },
88                FunctionDefWithAlt {
89                    altitude: 51e3,
90                    function: FunctionDef::Linear { gradient: -0.0028 },
91                },
92                FunctionDefWithAlt {
93                    altitude: 71e3,
94                    function: FunctionDef::Linear { gradient: -0.002 },
95                },
96                FunctionDefWithAlt {
97                    altitude: 84.852e3,
98                    function: FunctionDef::Linear { gradient: 0.0 },
99                },
100            ],
101            temperature_fixed_point: Some(TemperatureFixedPoint {
102                altitude: 0.0,
103                temperature: 288.0,
104            }),
105            first_humidity_function: FunctionDef::Linear { gradient: 0.0 },
106            next_humidity_functions: vec![],
107            humidity_fixed_point: Some(HumidityFixedPoint {
108                altitude: 0.0,
109                humidity: 0.0,
110            }),
111        }
112    }
113}
114
115#[cfg(feature = "serialization")]
116fn default_pressure() -> PressureFixedPoint {
117    PressureFixedPoint {
118        altitude: 0.0,
119        pressure: 101325.0,
120    }
121}
122
123#[cfg(feature = "serialization")]
124fn default_first_humidity_function() -> FunctionDef {
125    FunctionDef::Spline {
126        points: vec![(0.0, 0.0), (1.0, 0.0), (2.0, 0.0)],
127        boundary_condition: BoundaryCondition::Natural,
128    }
129}
130
131/// A structure representing an atmospheric model. It provides the temperature and density as
132/// functions of altitude
133#[derive(Debug, Clone)]
134#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
135pub struct Atmosphere {
136    pressure: PressureProfile,
137    temperature: VerticalProfile,
138    humidity: VerticalProfile,
139}
140
141impl Atmosphere {
142    /// Creates the atmospheric model from a parsed definition.
143    pub fn from_def(def: AtmosphereDef) -> Atmosphere {
144        let mut builder = VerticalProfileBuilder::new(def.first_temperature_function);
145        if let Some(point) = def.temperature_fixed_point {
146            builder = builder.with_fixed_value(point.altitude, point.temperature);
147        }
148        for fun_def in def.next_functions {
149            builder = builder.with_next_function(fun_def.altitude, fun_def.function);
150        }
151        let temperature = builder.build().unwrap();
152
153        let mut builder = VerticalProfileBuilder::new(def.first_humidity_function);
154        if let Some(point) = def.humidity_fixed_point {
155            builder = builder.with_fixed_value(point.altitude, point.humidity);
156        }
157        for fun_def in def.next_humidity_functions {
158            builder = builder.with_next_function(fun_def.altitude, fun_def.function);
159        }
160        let humidity = builder.build().unwrap();
161
162        let pressure = PressureProfile::from_temperature_profile(
163            &temperature,
164            def.pressure.pressure,
165            def.pressure.altitude,
166        );
167
168        Atmosphere {
169            pressure,
170            temperature,
171            humidity,
172        }
173    }
174
175    /// Returns the temperature at the given altitude
176    pub fn temperature(&self, h: f64) -> f64 {
177        self.temperature.eval(h)
178    }
179
180    /// Returns the derivative of temperature with respect to altitude at the given altitude
181    pub fn dtemperature(&self, h: f64) -> f64 {
182        self.temperature.eval_derivative(h)
183    }
184
185    /// Returns the pressure at the given altitude
186    pub fn pressure(&self, h: f64) -> f64 {
187        self.pressure.eval(h)
188    }
189
190    /// Returns the derivative of pressure at the given altitude
191    pub fn dpressure(&self, h: f64) -> f64 {
192        let p = self.pressure(h);
193        let t = self.temperature(h);
194        -A * p / t
195    }
196
197    /// Returns the temperature at the given altitude
198    pub fn humidity(&self, h: f64) -> f64 {
199        self.humidity.eval(h)
200    }
201
202    /// Returns the derivative of temperature with respect to altitude at the given altitude
203    pub fn dhumidity(&self, h: f64) -> f64 {
204        self.humidity.eval_derivative(h)
205    }
206}
207
208/// Returns the US-1976 standard model of the Earth's atmosphere.
209///
210/// The temperatures are expressed in kelvins (K), and the pressure in hectopascals (hPa).
211pub fn us76_atmosphere() -> Atmosphere {
212    let atm_def = AtmosphereDef::us_76();
213    Atmosphere::from_def(atm_def)
214}
215
216#[cfg(test)]
217mod test {
218    use super::*;
219
220    use cubic_splines::BoundaryCondition;
221
222    #[test]
223    fn test_us76() {
224        let atmosphere = Atmosphere::from_def(AtmosphereDef::us_76());
225        assert_eq!(atmosphere.pressure(0.0), 101325.0);
226        assert_eq!(atmosphere.temperature(0.0), 288.0);
227    }
228
229    #[test]
230    fn test_spline() {
231        let atmosphere_def = AtmosphereDef {
232            pressure: PressureFixedPoint {
233                altitude: 0.0,
234                pressure: 1000.0,
235            },
236            first_temperature_function: FunctionDef::Spline {
237                boundary_condition: BoundaryCondition::Derivatives(-0.0065, -0.0065),
238                points: vec![
239                    (0.0, 281.6),
240                    (12.5, 283.4),
241                    (19.4, 281.9),
242                    (24.0, 284.7),
243                    (34.0, 290.5),
244                ],
245            },
246            next_functions: vec![],
247            temperature_fixed_point: None,
248            ..AtmosphereDef::us_76()
249        };
250        let atmosphere = Atmosphere::from_def(atmosphere_def);
251        for i in 0..600 {
252            let h = i as f64 * 0.5;
253            println!(
254                "{} {} {}",
255                h,
256                atmosphere.temperature(h),
257                atmosphere.pressure(h)
258            );
259        }
260    }
261}