atm_refraction/air/atmosphere/
mod.rs1mod 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
12pub 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#[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 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 pub fn temperature(&self, h: f64) -> f64 {
177 self.temperature.eval(h)
178 }
179
180 pub fn dtemperature(&self, h: f64) -> f64 {
182 self.temperature.eval_derivative(h)
183 }
184
185 pub fn pressure(&self, h: f64) -> f64 {
187 self.pressure.eval(h)
188 }
189
190 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 pub fn humidity(&self, h: f64) -> f64 {
199 self.humidity.eval(h)
200 }
201
202 pub fn dhumidity(&self, h: f64) -> f64 {
204 self.humidity.eval_derivative(h)
205 }
206}
207
208pub 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}