]> git.mxchange.org Git - flightgear.git/blob - src/Environment/environment.cxx
Removed FGEnvironmentMgr as a special case in globals, initialization,
[flightgear.git] / src / Environment / environment.cxx
1 // environment.cxx -- routines to model the natural environment
2 //
3 // Written by David Megginson, started February 2002.
4 //
5 // Copyright (C) 2002  David Megginson - david@megginson.com
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 //
21 // $Id$
22
23
24 #ifdef HAVE_CONFIG_H
25 #  include <config.h>
26 #endif
27
28 #ifdef HAVE_WINDOWS_H
29 #  include <windows.h>                     
30 #endif
31
32 #include <math.h>
33
34 #include <plib/sg.h>
35
36 #include <simgear/constants.h>
37 #include <simgear/debug/logstream.hxx>
38 #include <simgear/math/interpolater.hxx>
39
40 #include <Main/fg_props.hxx>
41 #include "environment.hxx"
42
43
44 \f
45 ////////////////////////////////////////////////////////////////////////
46 // Atmosphere model.
47 ////////////////////////////////////////////////////////////////////////
48
49 // Copied from YASim Atmosphere.cxx, with m converted to ft, degK
50 // converted to degC, Pa converted to inHG, and kg/m^3 converted to
51 // slug/ft^3; they were then converted to deltas from the sea-level
52 // defaults (approx. 15degC, 29.92inHG, and 0.00237slugs/ft^3).
53
54 // Original comment from YASim:
55
56 // Copied from McCormick, who got it from "The ARDC Model Atmosphere"
57 // Note that there's an error in the text in the first entry,
58 // McCormick lists 299.16/101325/1.22500, but those don't agree with
59 // R=287.  I chose to correct the temperature to 288.20, since 79F is
60 // pretty hot for a "standard" atmosphere.
61
62 // Elevation (ft), temperature factor (degK), pressure factor (inHG)
63 static double atmosphere_data[][3] = {
64  { 0.00, 1.00, 1.000 },
65  { 2952.76, 0.98, 0.898 },
66  { 5905.51, 0.96, 0.804 },
67  { 8858.27, 0.94, 0.719 },
68  { 11811.02, 0.92, 0.641 },
69  { 14763.78, 0.90, 0.570 },
70  { 17716.54, 0.88, 0.506 },
71  { 20669.29, 0.86, 0.447 },
72  { 23622.05, 0.84, 0.394 },
73  { 26574.80, 0.82, 0.347 },
74  { 29527.56, 0.80, 0.304 },
75  { 32480.31, 0.78, 0.266 },
76  { 35433.07, 0.76, 0.231 },
77  { 38385.83, 0.75, 0.201 },
78  { 41338.58, 0.75, 0.174 },
79  { 44291.34, 0.75, 0.151 },
80  { 47244.09, 0.75, 0.131 },
81  { 50196.85, 0.75, 0.114 },
82  { 53149.61, 0.75, 0.099 },
83  { 56102.36, 0.75, 0.086 },
84  { 59055.12, 0.75, 0.075 },
85  { 62007.87, 0.75, 0.065 },
86  { -1, -1, -1 }
87 };
88
89 static SGInterpTable * _temperature_degc_table = 0;
90 static SGInterpTable * _pressure_inhg_table = 0;
91
92 static void
93 _setup_tables ()
94 {
95   if (_temperature_degc_table != 0)
96       return;
97
98   _temperature_degc_table = new SGInterpTable;
99   _pressure_inhg_table = new SGInterpTable;
100
101   for (int i = 0; atmosphere_data[i][0] != -1; i++) {
102     _temperature_degc_table->addEntry(atmosphere_data[i][0],
103                                       atmosphere_data[i][1]);
104     _pressure_inhg_table->addEntry(atmosphere_data[i][0],
105                                    atmosphere_data[i][2]);
106   }
107 }
108
109
110 \f
111 ////////////////////////////////////////////////////////////////////////
112 // Implementation of FGEnvironment.
113 ////////////////////////////////////////////////////////////////////////
114
115 FGEnvironment::FGEnvironment()
116   : elevation_ft(0),
117     visibility_m(32000),
118     temperature_sea_level_degc(15),
119     temperature_degc(15),
120     dewpoint_sea_level_degc(5), // guess
121     dewpoint_degc(5),
122     pressure_sea_level_inhg(29.92),
123     pressure_inhg(29.92),
124     wind_from_heading_deg(0),
125     wind_speed_kt(0),
126     wind_from_north_fps(0),
127     wind_from_east_fps(0),
128     wind_from_down_fps(0),
129     turbulence_norm(0)
130 {
131   _setup_tables();
132 }
133
134 FGEnvironment::FGEnvironment (const FGEnvironment &env)
135 {
136     FGEnvironment();
137     copy(env);
138 }
139
140 FGEnvironment::~FGEnvironment()
141 {
142 }
143
144 void
145 FGEnvironment::copy (const FGEnvironment &env)
146 {
147     elevation_ft = env.elevation_ft;
148     visibility_m = env.visibility_m;
149     temperature_sea_level_degc = env.temperature_sea_level_degc;
150     dewpoint_sea_level_degc = env.dewpoint_sea_level_degc;
151     pressure_sea_level_inhg = env.pressure_sea_level_inhg;
152     wind_from_heading_deg = env.wind_from_heading_deg;
153     wind_speed_kt = env.wind_speed_kt;
154     wind_from_north_fps = env.wind_from_north_fps;
155     wind_from_east_fps = env.wind_from_east_fps;
156     wind_from_down_fps = env.wind_from_down_fps;
157     turbulence_norm = env.turbulence_norm;
158 }
159
160 static inline bool
161 maybe_copy_value (FGEnvironment * env, const SGPropertyNode * node,
162                   const char * name, void (FGEnvironment::*setter)(double))
163 {
164     const SGPropertyNode * child = node->getChild(name);
165                                 // fragile: depends on not being typed
166                                 // as a number
167     if (child != 0 && child->getStringValue()[0] != '\0') {
168         (env->*setter)(child->getDoubleValue());
169         return true;
170     } else {
171         return false;
172     }
173 }
174
175 void
176 FGEnvironment::read (const SGPropertyNode * node)
177 {
178     maybe_copy_value(this, node, "visibility-m",
179                      &FGEnvironment::set_visibility_m);
180
181     if (!maybe_copy_value(this, node, "temperature-sea-level-degc",
182                           &FGEnvironment::set_temperature_sea_level_degc))
183         maybe_copy_value(this, node, "temperature-degc",
184                          &FGEnvironment::set_temperature_degc);
185
186     if (!maybe_copy_value(this, node, "dewpoint-sea-level-degc",
187                           &FGEnvironment::set_dewpoint_sea_level_degc))
188         maybe_copy_value(this, node, "dewpoint-degc",
189                          &FGEnvironment::set_dewpoint_degc);
190
191     if (!maybe_copy_value(this, node, "pressure-sea-level-inhg",
192                           &FGEnvironment::set_pressure_sea_level_inhg))
193         maybe_copy_value(this, node, "pressure-inhg",
194                          &FGEnvironment::set_pressure_inhg);
195
196     maybe_copy_value(this, node, "wind-from-heading-deg",
197                      &FGEnvironment::set_wind_from_heading_deg);
198
199     maybe_copy_value(this, node, "wind-speed-kt",
200                      &FGEnvironment::set_wind_speed_kt);
201
202     maybe_copy_value(this, node, "elevation-ft",
203                      &FGEnvironment::set_elevation_ft);
204
205     maybe_copy_value(this, node, "turbulence-norm",
206                      &FGEnvironment::set_turbulence_norm);
207 }
208
209
210 double
211 FGEnvironment::get_visibility_m () const
212 {
213   return visibility_m;
214 }
215
216 double
217 FGEnvironment::get_temperature_sea_level_degc () const
218 {
219   return temperature_sea_level_degc;
220 }
221
222 double
223 FGEnvironment::get_temperature_degc () const
224 {
225   return temperature_degc;
226 }
227
228 double
229 FGEnvironment::get_dewpoint_sea_level_degc () const
230 {
231   return dewpoint_sea_level_degc;
232 }
233
234 double
235 FGEnvironment::get_dewpoint_degc () const
236 {
237   return dewpoint_degc;
238 }
239
240 double
241 FGEnvironment::get_pressure_sea_level_inhg () const
242 {
243   return pressure_sea_level_inhg;
244 }
245
246 double
247 FGEnvironment::get_pressure_inhg () const
248 {
249   return pressure_inhg;
250 }
251
252 double
253 FGEnvironment::get_density_slugft3 () const
254 {
255   return density_slugft3;
256 }
257
258 double
259 FGEnvironment::get_wind_from_heading_deg () const
260 {
261   return wind_from_heading_deg;
262 }
263
264 double
265 FGEnvironment::get_wind_speed_kt () const
266 {
267   return wind_speed_kt;
268 }
269
270 double
271 FGEnvironment::get_wind_from_north_fps () const
272 {
273   return wind_from_north_fps;
274 }
275
276 double
277 FGEnvironment::get_wind_from_east_fps () const
278 {
279   return wind_from_east_fps;
280 }
281
282 double
283 FGEnvironment::get_wind_from_down_fps () const
284 {
285   return wind_from_down_fps;
286 }
287
288 double
289 FGEnvironment::get_turbulence_norm () const
290 {
291   return turbulence_norm;
292 }
293
294 double
295 FGEnvironment::get_elevation_ft () const
296 {
297   return elevation_ft;
298 }
299
300 void
301 FGEnvironment::set_visibility_m (double v)
302 {
303   visibility_m = v;
304 }
305
306 void
307 FGEnvironment::set_temperature_sea_level_degc (double t)
308 {
309   temperature_sea_level_degc = t;
310   if (dewpoint_sea_level_degc > t)
311       dewpoint_sea_level_degc = t;
312   _recalc_alt_temperature();
313   _recalc_density();
314 }
315
316 void
317 FGEnvironment::set_temperature_degc (double t)
318 {
319   temperature_degc = t;
320   _recalc_sl_temperature();
321   _recalc_density();
322 }
323
324 void
325 FGEnvironment::set_dewpoint_sea_level_degc (double t)
326 {
327   dewpoint_sea_level_degc = t;
328   if (temperature_sea_level_degc < t)
329       temperature_sea_level_degc = t;
330   _recalc_alt_dewpoint();
331   _recalc_density();
332 }
333
334 void
335 FGEnvironment::set_dewpoint_degc (double t)
336 {
337   dewpoint_degc = t;
338   _recalc_sl_dewpoint();
339   _recalc_density();
340 }
341
342 void
343 FGEnvironment::set_pressure_sea_level_inhg (double p)
344 {
345   pressure_sea_level_inhg = p;
346   _recalc_alt_pressure();
347   _recalc_density();
348 }
349
350 void
351 FGEnvironment::set_pressure_inhg (double p)
352 {
353   pressure_inhg = p;
354   _recalc_sl_pressure();
355   _recalc_density();
356 }
357
358 void
359 FGEnvironment::set_wind_from_heading_deg (double h)
360 {
361   wind_from_heading_deg = h;
362   _recalc_ne();
363 }
364
365 void
366 FGEnvironment::set_wind_speed_kt (double s)
367 {
368   wind_speed_kt = s;
369   _recalc_ne();
370 }
371
372 void
373 FGEnvironment::set_wind_from_north_fps (double n)
374 {
375   wind_from_north_fps = n;
376   _recalc_hdgspd();
377 }
378
379 void
380 FGEnvironment::set_wind_from_east_fps (double e)
381 {
382   wind_from_east_fps = e;
383   _recalc_hdgspd();
384 }
385
386 void
387 FGEnvironment::set_wind_from_down_fps (double d)
388 {
389   wind_from_down_fps = d;
390   _recalc_hdgspd();
391 }
392
393 void
394 FGEnvironment::set_turbulence_norm (double t)
395 {
396   turbulence_norm = t;
397 }
398
399 void
400 FGEnvironment::set_elevation_ft (double e)
401 {
402   elevation_ft = e;
403   _recalc_alt_temperature();
404   _recalc_alt_dewpoint();
405   _recalc_alt_pressure();
406   _recalc_density();
407 }
408
409 void
410 FGEnvironment::_recalc_hdgspd ()
411 {
412   double angle_rad;
413
414   if (wind_from_east_fps == 0) {
415     angle_rad = (wind_from_north_fps >= 0 ? SGD_PI/2 : -SGD_PI/2);
416   } else {
417     angle_rad = atan(wind_from_north_fps/wind_from_east_fps);
418   }
419   wind_from_heading_deg = angle_rad * SGD_RADIANS_TO_DEGREES;
420   if (wind_from_east_fps >= 0)
421     wind_from_heading_deg = 90 - wind_from_heading_deg;
422   else
423     wind_from_heading_deg = 270 - wind_from_heading_deg;
424   if (angle_rad == 0)
425     wind_speed_kt = fabs(wind_from_east_fps
426                          * SG_METER_TO_NM * SG_FEET_TO_METER * 3600);
427   else
428     wind_speed_kt = (wind_from_north_fps / sin(angle_rad))
429       * SG_METER_TO_NM * SG_FEET_TO_METER * 3600;
430 }
431
432 void
433 FGEnvironment::_recalc_ne ()
434 {
435   double speed_fps =
436     wind_speed_kt * SG_NM_TO_METER * SG_METER_TO_FEET * (1.0/3600);
437
438   wind_from_north_fps = speed_fps *
439     cos(wind_from_heading_deg * SGD_DEGREES_TO_RADIANS);
440   wind_from_east_fps = speed_fps *
441     sin(wind_from_heading_deg * SGD_DEGREES_TO_RADIANS);
442 }
443
444 void
445 FGEnvironment::_recalc_sl_temperature ()
446 {
447   // If we're in the stratosphere, leave sea-level temp alone
448   if (elevation_ft < 38000) {
449     temperature_sea_level_degc =
450       (temperature_degc + 273.15)
451       /_temperature_degc_table->interpolate(elevation_ft)
452       - 273.15;
453   }
454 }
455
456 void
457 FGEnvironment::_recalc_alt_temperature ()
458 {
459   if (elevation_ft < 38000) {
460     temperature_degc =
461       (temperature_sea_level_degc + 273.15) *
462       _temperature_degc_table->interpolate(elevation_ft) - 273.15;
463   } else {
464     temperature_degc = -56.49;  // Stratosphere is constant
465   }
466 }
467
468 void
469 FGEnvironment::_recalc_sl_dewpoint ()
470 {
471                                 // 0.2degC/1000ft
472                                 // FIXME: this will work only for low
473                                 // elevations
474   dewpoint_sea_level_degc = dewpoint_degc + (elevation_ft * .0002);
475   if (dewpoint_sea_level_degc > temperature_sea_level_degc)
476     dewpoint_sea_level_degc = temperature_sea_level_degc;
477 }
478
479 void
480 FGEnvironment::_recalc_alt_dewpoint ()
481 {
482                                 // 0.2degC/1000ft
483                                 // FIXME: this will work only for low
484                                 // elevations
485   dewpoint_degc = dewpoint_sea_level_degc + (elevation_ft * .0002);
486   if (dewpoint_degc > temperature_degc)
487     dewpoint_degc = temperature_degc;
488 }
489
490 void
491 FGEnvironment::_recalc_sl_pressure ()
492 {
493   pressure_sea_level_inhg =
494     pressure_inhg / _pressure_inhg_table->interpolate(elevation_ft);
495 }
496
497 void
498 FGEnvironment::_recalc_alt_pressure ()
499 {
500   pressure_inhg =
501     pressure_sea_level_inhg * _pressure_inhg_table->interpolate(elevation_ft);
502 }
503
504 void
505 FGEnvironment::_recalc_density ()
506 {
507   double pressure_psf = pressure_inhg * 70.7487;
508   
509   // adjust for humidity
510   // calculations taken from USA Today (oops!) at
511   // http://www.usatoday.com/weather/basics/density-calculations.htm
512   double temperature_degk = temperature_degc + 273.15;
513   double pressure_mb = pressure_inhg * 33.86;
514   double vapor_pressure_mb =
515     6.11 * pow(10.0, 7.5 * dewpoint_degc / (237.7 + dewpoint_degc));
516   double virtual_temperature_degk = temperature_degk / (1 - (vapor_pressure_mb / pressure_mb) * (1.0 - 0.622));
517   double virtual_temperature_degr = virtual_temperature_degk * 1.8;
518
519   density_slugft3 = pressure_psf / (virtual_temperature_degr * 1718);
520 }
521
522
523 \f
524 ////////////////////////////////////////////////////////////////////////
525 // Functions.
526 ////////////////////////////////////////////////////////////////////////
527
528 static inline double
529 do_interp (double a, double b, double fraction)
530 {
531     double retval = (a + ((b - a) * fraction));
532     return retval;
533 }
534
535 static inline double
536 do_interp_deg (double a, double b, double fraction)
537 {
538     a = fmod(a, 360);
539     b = fmod(b, 360);
540     if (fabs(b-a) > 180) {
541         if (a < b)
542             a += 360;
543         else
544             b += 360;
545     }
546     return fmod(do_interp(a, b, fraction), 360);
547 }
548
549 void
550 interpolate (const FGEnvironment * env1, const FGEnvironment * env2,
551              double fraction, FGEnvironment * result)
552 {
553     result->set_visibility_m
554         (do_interp(env1->get_visibility_m(),
555                    env2->get_visibility_m(),
556                    fraction));
557
558     result->set_temperature_sea_level_degc
559         (do_interp(env1->get_temperature_sea_level_degc(),
560                    env2->get_temperature_sea_level_degc(),
561                    fraction));
562
563     result->set_dewpoint_sea_level_degc
564         (do_interp(env1->get_dewpoint_sea_level_degc(),
565                    env2->get_dewpoint_sea_level_degc(),
566                    fraction));
567
568     result->set_pressure_sea_level_inhg
569         (do_interp(env1->get_pressure_sea_level_inhg(),
570                    env2->get_pressure_sea_level_inhg(),
571                    fraction));
572
573     result->set_wind_from_heading_deg
574         (do_interp_deg(env1->get_wind_from_heading_deg(),
575                        env2->get_wind_from_heading_deg(),
576                        fraction));
577
578     result->set_wind_speed_kt
579         (do_interp(env1->get_wind_speed_kt(),
580                    env2->get_wind_speed_kt(),
581                    fraction));
582
583     result->set_elevation_ft
584         (do_interp(env1->get_elevation_ft(),
585                    env2->get_elevation_ft(),
586                    fraction));
587
588     result->set_turbulence_norm
589         (do_interp(env1->get_turbulence_norm(),
590                    env2->get_turbulence_norm(),
591                    fraction));
592 }
593
594 // end of environment.cxx