]> git.mxchange.org Git - flightgear.git/blob - src/Environment/environment.cxx
c51752c5544e501021675d19a1cab0aaf6d746be
[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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 //
21 // $Id$
22
23
24 #ifdef HAVE_CONFIG_H
25 #  include <config.h>
26 #endif
27
28 #include <math.h>
29
30 #include <plib/sg.h>
31
32 #include <simgear/constants.h>
33 #include <simgear/debug/logstream.hxx>
34 #include <simgear/math/interpolater.hxx>
35 #include <simgear/props/props.hxx>
36 #include <simgear/environment/visual_enviro.hxx>
37
38 #include <Main/fg_props.hxx>
39
40 #include "environment.hxx"
41
42
43 \f
44 ////////////////////////////////////////////////////////////////////////
45 // Atmosphere model.
46 ////////////////////////////////////////////////////////////////////////
47
48 // Calculated based on the ISA standard day, as found at e.g.
49 // http://www.av8n.com/physics/altimetry.htm
50
51 // Each line of data has 3 elements:
52 //   Elevation (ft), 
53 //   temperature factor (dimensionless ratio of absolute temp), 
54 //   pressure factor (dimensionless ratio)
55 static double atmosphere_data[][3] = {
56  {  -3000.00,   1.021,  1.1133 },
57  {      0.00,   1.000,  1.0000 },
58  {   2952.76,   0.980,  0.8978 },
59  {   5905.51,   0.959,  0.8042 },
60  {   8858.27,   0.939,  0.7187 },
61  {  11811.02,   0.919,  0.6407 },
62  {  14763.78,   0.898,  0.5697 },
63  {  17716.54,   0.878,  0.5052 },
64  {  20669.29,   0.858,  0.4468 },
65  {  23622.05,   0.838,  0.3940 },
66  {  26574.80,   0.817,  0.3463 },
67  {  29527.56,   0.797,  0.3034 },
68  {  32480.31,   0.777,  0.2649 },
69  {  35433.07,   0.756,  0.2305 },
70  {  38385.83,   0.752,  0.2000 },
71  {  41338.58,   0.752,  0.1736 },
72  {  44291.34,   0.752,  0.1506 },
73  {  47244.09,   0.752,  0.1307 },
74  {  50196.85,   0.752,  0.1134 },
75  {  53149.61,   0.752,  0.0984 },
76  {  56102.36,   0.752,  0.0854 },
77  {  59055.12,   0.752,  0.0741 },
78  {  62007.87,   0.752,  0.0643 },
79  {  65000.00,   0.752,  0.0557 },
80  {  68000.00,   0.754,  0.0482 },
81  {  71000.00,   0.758,  0.0418 },
82  {  74000.00,   0.761,  0.0362 },
83  {  77000.00,   0.764,  0.0314 },
84  {  80000.00,   0.767,  0.0273 },
85  {  83000.00,   0.770,  0.0237 },
86  {  86000.00,   0.773,  0.0206 },
87  {  89000.00,   0.777,  0.0179 },
88  {  92000.00,   0.780,  0.0156 },
89  {  95000.00,   0.783,  0.0135 },
90  {  98000.00,   0.786,  0.0118 },
91  { 101000.00,   0.789,  0.0103 },
92  { -1, -1, -1 }
93 };
94
95 static SGInterpTable * _temperature_degc_table = 0;
96 static SGInterpTable * _pressure_inhg_table = 0;
97
98 static void
99 _setup_tables ()
100 {
101   if (_temperature_degc_table != 0)
102       return;
103
104   _temperature_degc_table = new SGInterpTable;
105   _pressure_inhg_table = new SGInterpTable;
106
107   for (int i = 0; atmosphere_data[i][0] != -1; i++) {
108     _temperature_degc_table->addEntry(atmosphere_data[i][0],
109                                       atmosphere_data[i][1]);
110     _pressure_inhg_table->addEntry(atmosphere_data[i][0],
111                                    atmosphere_data[i][2]);
112   }
113 }
114
115
116 \f
117 ////////////////////////////////////////////////////////////////////////
118 // Implementation of FGEnvironment.
119 ////////////////////////////////////////////////////////////////////////
120
121 void FGEnvironment::_init()
122 {
123     elevation_ft = 0;
124     visibility_m = 32000;
125     temperature_sea_level_degc = 15;
126     temperature_degc = 15;
127     dewpoint_sea_level_degc = 5; // guess
128     dewpoint_degc = 5;
129     pressure_sea_level_inhg = 29.92;
130     pressure_inhg = 29.92;
131     turbulence_magnitude_norm = 0;
132     turbulence_rate_hz = 1;
133     wind_from_heading_deg = 0;
134     wind_speed_kt = 0;
135     wind_from_north_fps = 0;
136     wind_from_east_fps = 0;
137     wind_from_down_fps = 0;
138     altitude_half_to_sun_m = 1000;
139     altitude_tropo_top_m = 10000;
140     _setup_tables();
141     _recalc_density();
142     _recalc_relative_humidity();
143     
144 }
145
146 FGEnvironment::FGEnvironment()
147 {
148     _init();
149 }
150
151 FGEnvironment::FGEnvironment (const FGEnvironment &env)
152 {
153     _init();
154     copy(env);
155 }
156
157 FGEnvironment::~FGEnvironment()
158 {
159 }
160
161 void
162 FGEnvironment::copy (const FGEnvironment &env)
163 {
164     elevation_ft = env.elevation_ft;
165     visibility_m = env.visibility_m;
166     temperature_sea_level_degc = env.temperature_sea_level_degc;
167     temperature_degc = env.temperature_degc;
168     dewpoint_sea_level_degc = env.dewpoint_sea_level_degc;
169     dewpoint_degc = env.dewpoint_degc;
170     pressure_sea_level_inhg = env.pressure_sea_level_inhg;
171     wind_from_heading_deg = env.wind_from_heading_deg;
172     wind_speed_kt = env.wind_speed_kt;
173     wind_from_north_fps = env.wind_from_north_fps;
174     wind_from_east_fps = env.wind_from_east_fps;
175     wind_from_down_fps = env.wind_from_down_fps;
176     turbulence_magnitude_norm = env.turbulence_magnitude_norm;
177     turbulence_rate_hz = env.turbulence_rate_hz;
178 }
179
180 static inline bool
181 maybe_copy_value (FGEnvironment * env, const SGPropertyNode * node,
182                   const char * name, void (FGEnvironment::*setter)(double))
183 {
184     const SGPropertyNode * child = node->getNode(name);
185                                 // fragile: depends on not being typed
186                                 // as a number
187     if (child != 0 && child->hasValue() &&
188         child->getStringValue()[0] != '\0') {
189         (env->*setter)(child->getDoubleValue());
190         return true;
191     } else {
192         return false;
193     }
194 }
195
196 void
197 FGEnvironment::read (const SGPropertyNode * node)
198 {
199     maybe_copy_value(this, node, "visibility-m",
200                      &FGEnvironment::set_visibility_m);
201
202     if (!maybe_copy_value(this, node, "temperature-sea-level-degc",
203                           &FGEnvironment::set_temperature_sea_level_degc))
204         maybe_copy_value(this, node, "temperature-degc",
205                          &FGEnvironment::set_temperature_degc);
206
207     if (!maybe_copy_value(this, node, "dewpoint-sea-level-degc",
208                           &FGEnvironment::set_dewpoint_sea_level_degc))
209         maybe_copy_value(this, node, "dewpoint-degc",
210                          &FGEnvironment::set_dewpoint_degc);
211
212     if (!maybe_copy_value(this, node, "pressure-sea-level-inhg",
213                           &FGEnvironment::set_pressure_sea_level_inhg))
214         maybe_copy_value(this, node, "pressure-inhg",
215                          &FGEnvironment::set_pressure_inhg);
216
217     maybe_copy_value(this, node, "wind-from-heading-deg",
218                      &FGEnvironment::set_wind_from_heading_deg);
219
220     maybe_copy_value(this, node, "wind-speed-kt",
221                      &FGEnvironment::set_wind_speed_kt);
222
223     maybe_copy_value(this, node, "elevation-ft",
224                      &FGEnvironment::set_elevation_ft);
225
226     maybe_copy_value(this, node, "turbulence/magnitude-norm",
227                      &FGEnvironment::set_turbulence_magnitude_norm);
228
229     maybe_copy_value(this, node, "turbulence/rate-hz",
230                      &FGEnvironment::set_turbulence_rate_hz);
231 }
232
233
234 double
235 FGEnvironment::get_visibility_m () const
236 {
237   return visibility_m;
238 }
239
240 double
241 FGEnvironment::get_temperature_sea_level_degc () const
242 {
243   return temperature_sea_level_degc;
244 }
245
246 double
247 FGEnvironment::get_temperature_degc () const
248 {
249   return temperature_degc;
250 }
251
252 double
253 FGEnvironment::get_temperature_degf () const
254 {
255   return (temperature_degc * 9.0 / 5.0) + 32.0;
256 }
257
258 double
259 FGEnvironment::get_dewpoint_sea_level_degc () const
260 {
261   return dewpoint_sea_level_degc;
262 }
263
264 double
265 FGEnvironment::get_dewpoint_degc () const
266 {
267   return dewpoint_degc;
268 }
269
270 double
271 FGEnvironment::get_pressure_sea_level_inhg () const
272 {
273   return pressure_sea_level_inhg;
274 }
275
276 double
277 FGEnvironment::get_pressure_inhg () const
278 {
279   return pressure_inhg;
280 }
281
282 double
283 FGEnvironment::get_density_slugft3 () const
284 {
285   return density_slugft3;
286 }
287
288 double
289 FGEnvironment::get_relative_humidity () const
290 {
291   return relative_humidity;
292 }
293
294 double
295 FGEnvironment::get_density_tropo_avg_kgm3 () const
296 {
297   return density_tropo_avg_kgm3;
298 }
299
300 double
301 FGEnvironment::get_altitude_half_to_sun_m () const
302 {
303   return altitude_half_to_sun_m;
304 }
305
306 double
307 FGEnvironment::get_altitude_tropo_top_m () const
308 {
309   return altitude_tropo_top_m;
310 }
311
312 double
313 FGEnvironment::get_wind_from_heading_deg () const
314 {
315   return wind_from_heading_deg;
316 }
317
318 double
319 FGEnvironment::get_wind_speed_kt () const
320 {
321   return wind_speed_kt;
322 }
323
324 double
325 FGEnvironment::get_wind_from_north_fps () const
326 {
327   return wind_from_north_fps;
328 }
329
330 double
331 FGEnvironment::get_wind_from_east_fps () const
332 {
333   return wind_from_east_fps;
334 }
335
336 double
337 FGEnvironment::get_wind_from_down_fps () const
338 {
339   return wind_from_down_fps;
340 }
341
342 double
343 FGEnvironment::get_turbulence_magnitude_norm () const
344 {
345   if( sgEnviro.get_turbulence_enable_state() )
346     if (fgGetBool("/environment/params/real-world-weather-fetch") == true)
347       return sgEnviro.get_cloud_turbulence();
348   return turbulence_magnitude_norm;
349 }
350
351 double
352 FGEnvironment::get_turbulence_rate_hz () const
353 {
354   return turbulence_rate_hz;
355 }
356
357 double
358 FGEnvironment::get_elevation_ft () const
359 {
360   return elevation_ft;
361 }
362
363 void
364 FGEnvironment::set_visibility_m (double v)
365 {
366   visibility_m = v;
367 }
368
369 void
370 FGEnvironment::set_temperature_sea_level_degc (double t)
371 {
372   temperature_sea_level_degc = t;
373   if (dewpoint_sea_level_degc > t)
374       dewpoint_sea_level_degc = t;
375   _recalc_alt_temperature();
376   _recalc_density();
377 }
378
379 void
380 FGEnvironment::set_temperature_degc (double t)
381 {
382   temperature_degc = t;
383   _recalc_sl_temperature();
384   _recalc_density();
385   _recalc_relative_humidity();
386 }
387
388 void
389 FGEnvironment::set_dewpoint_sea_level_degc (double t)
390 {
391   dewpoint_sea_level_degc = t;
392   if (temperature_sea_level_degc < t)
393       temperature_sea_level_degc = t;
394   _recalc_alt_dewpoint();
395   _recalc_density();
396 }
397
398 void
399 FGEnvironment::set_dewpoint_degc (double t)
400 {
401   dewpoint_degc = t;
402   _recalc_sl_dewpoint();
403   _recalc_density();
404   _recalc_relative_humidity();
405 }
406
407 void
408 FGEnvironment::set_pressure_sea_level_inhg (double p)
409 {
410   pressure_sea_level_inhg = p;
411   _recalc_alt_pressure();
412   _recalc_density();
413 }
414
415 void
416 FGEnvironment::set_pressure_inhg (double p)
417 {
418   pressure_inhg = p;
419   _recalc_sl_pressure();
420   _recalc_density();
421 }
422
423 void
424 FGEnvironment::set_wind_from_heading_deg (double h)
425 {
426   wind_from_heading_deg = h;
427   _recalc_ne();
428 }
429
430 void
431 FGEnvironment::set_wind_speed_kt (double s)
432 {
433   wind_speed_kt = s;
434   _recalc_ne();
435 }
436
437 void
438 FGEnvironment::set_wind_from_north_fps (double n)
439 {
440   wind_from_north_fps = n;
441   _recalc_hdgspd();
442 }
443
444 void
445 FGEnvironment::set_wind_from_east_fps (double e)
446 {
447   wind_from_east_fps = e;
448   _recalc_hdgspd();
449 }
450
451 void
452 FGEnvironment::set_wind_from_down_fps (double d)
453 {
454   wind_from_down_fps = d;
455   _recalc_hdgspd();
456 }
457
458 void
459 FGEnvironment::set_turbulence_magnitude_norm (double t)
460 {
461   turbulence_magnitude_norm = t;
462 }
463
464 void
465 FGEnvironment::set_turbulence_rate_hz (double r)
466 {
467   turbulence_rate_hz = r;
468 }
469
470 void
471 FGEnvironment::set_elevation_ft (double e)
472 {
473   elevation_ft = e;
474   _recalc_alt_temperature();
475   _recalc_alt_dewpoint();
476   _recalc_alt_pressure();
477   _recalc_density();
478   _recalc_relative_humidity();
479 }
480
481 void
482 FGEnvironment::set_altitude_half_to_sun_m (double alt)
483 {
484  altitude_half_to_sun_m = alt;
485  _recalc_density_tropo_avg_kgm3();
486 }
487
488 void
489 FGEnvironment::set_altitude_tropo_top_m (double alt)
490 {
491  altitude_tropo_top_m = alt;
492  _recalc_density_tropo_avg_kgm3();
493 }
494
495
496 void
497 FGEnvironment::_recalc_hdgspd ()
498 {
499   double angle_rad;
500
501   if (wind_from_east_fps == 0) {
502     angle_rad = (wind_from_north_fps >= 0 ? SGD_PI_2 : -SGD_PI_2);
503   } else {
504     angle_rad = atan(wind_from_north_fps/wind_from_east_fps);
505   }
506   wind_from_heading_deg = angle_rad * SGD_RADIANS_TO_DEGREES;
507   if (wind_from_east_fps >= 0)
508     wind_from_heading_deg = 90 - wind_from_heading_deg;
509   else
510     wind_from_heading_deg = 270 - wind_from_heading_deg;
511
512 #if 0
513   // FIXME: Windspeed can become negative with these formulas.
514   //        which can cause problems for animations that rely
515   //        on the windspeed property.
516   if (angle_rad == 0)
517     wind_speed_kt = fabs(wind_from_east_fps
518                          * SG_METER_TO_NM * SG_FEET_TO_METER * 3600);
519   else
520     wind_speed_kt = (wind_from_north_fps / sin(angle_rad))
521       * SG_METER_TO_NM * SG_FEET_TO_METER * 3600;
522 #else
523   wind_speed_kt = sqrt(wind_from_north_fps * wind_from_north_fps +
524                        wind_from_east_fps * wind_from_east_fps) 
525                   * SG_METER_TO_NM * SG_FEET_TO_METER * 3600;
526 #endif
527 }
528
529 void
530 FGEnvironment::_recalc_ne ()
531 {
532   double speed_fps =
533     wind_speed_kt * SG_NM_TO_METER * SG_METER_TO_FEET * (1.0/3600);
534
535   wind_from_north_fps = speed_fps *
536     cos(wind_from_heading_deg * SGD_DEGREES_TO_RADIANS);
537   wind_from_east_fps = speed_fps *
538     sin(wind_from_heading_deg * SGD_DEGREES_TO_RADIANS);
539 }
540
541 void
542 FGEnvironment::_recalc_sl_temperature ()
543 {
544   // If we're in the stratosphere, leave sea-level temp alone
545   if (elevation_ft < 38000) {
546     temperature_sea_level_degc = (temperature_degc + 273.15)
547         / _temperature_degc_table->interpolate(elevation_ft)
548       - 273.15;
549   }
550 }
551
552 void
553 FGEnvironment::_recalc_alt_temperature ()
554 {
555   if (elevation_ft < 38000) {
556     temperature_degc = (temperature_sea_level_degc + 273.15) *
557         _temperature_degc_table->interpolate(elevation_ft) - 273.15;
558   } else {
559     temperature_degc = -56.49;  // Stratosphere is constant
560   }
561 }
562
563 void
564 FGEnvironment::_recalc_sl_dewpoint ()
565 {
566                                 // 0.2degC/1000ft
567                                 // FIXME: this will work only for low
568                                 // elevations
569   dewpoint_sea_level_degc = dewpoint_degc + (elevation_ft * .0002);
570   if (dewpoint_sea_level_degc > temperature_sea_level_degc)
571     dewpoint_sea_level_degc = temperature_sea_level_degc;
572 }
573
574 void
575 FGEnvironment::_recalc_alt_dewpoint ()
576 {
577                                 // 0.2degC/1000ft
578                                 // FIXME: this will work only for low
579                                 // elevations
580   dewpoint_degc = dewpoint_sea_level_degc + (elevation_ft * .0002);
581   if (dewpoint_degc > temperature_degc)
582     dewpoint_degc = temperature_degc;
583 }
584
585 void
586 FGEnvironment::_recalc_sl_pressure ()
587 {
588   pressure_sea_level_inhg =
589     pressure_inhg / _pressure_inhg_table->interpolate(elevation_ft);
590 }
591
592 void
593 FGEnvironment::_recalc_alt_pressure ()
594 {
595   pressure_inhg =
596     pressure_sea_level_inhg * _pressure_inhg_table->interpolate(elevation_ft);
597 }
598
599 void
600 FGEnvironment::_recalc_density ()
601 {
602   double pressure_psf = pressure_inhg * 70.7487;
603   
604   // adjust for humidity
605   // calculations taken from USA Today (oops!) at
606   // http://www.usatoday.com/weather/basics/density-calculations.htm
607   double temperature_degk = temperature_degc + 273.15;
608   double pressure_mb = pressure_inhg * 33.86;
609   double vapor_pressure_mb =
610     6.11 * pow(10.0, 7.5 * dewpoint_degc / (237.7 + dewpoint_degc));
611   double virtual_temperature_degk = temperature_degk / (1 - (vapor_pressure_mb / pressure_mb) * (1.0 - 0.622));
612   double virtual_temperature_degr = virtual_temperature_degk * 1.8;
613
614   density_slugft3 = pressure_psf / (virtual_temperature_degr * 1718);
615   _recalc_density_tropo_avg_kgm3();
616 }
617
618 // This is used to calculate the average density on the path 
619 // of sunlight to the observer for calculating sun-color
620 void
621 FGEnvironment::_recalc_density_tropo_avg_kgm3 ()
622 {
623   double pressure_mb = pressure_inhg * 33.86;
624   double vaporpressure = 6.11 * pow(10.0, ((7.5 * dewpoint_degc) / (237.7 + dewpoint_degc)));
625
626   double virtual_temp = (temperature_degc + 273.15) / (1 - 0.379 * (vaporpressure/pressure_mb));
627
628   double density_half = (100 * pressure_mb * exp(-altitude_half_to_sun_m / 8000))
629       / (287.05 * virtual_temp);
630   double density_tropo = (100 * pressure_mb * exp((-1 * altitude_tropo_top_m) / 8000))
631       / ( 287.05 * virtual_temp);
632
633   density_tropo_avg_kgm3 = ((density_slugft3 * 515.379) + density_half + density_tropo) / 3;
634 }
635
636 void
637 FGEnvironment::_recalc_relative_humidity ()
638 {
639   double vaporpressure = 6.11 * pow(10.0, ((7.5 * dewpoint_degc) / ( 237.7 + dewpoint_degc)));
640   double sat_vaporpressure = 6.11 * pow(10.0, ((7.5 * temperature_degc)
641       / ( 237.7 + temperature_degc)) );
642   relative_humidity = 100 * vaporpressure / sat_vaporpressure ;
643 }
644
645
646
647 ////////////////////////////////////////////////////////////////////////
648 // Functions.
649 ////////////////////////////////////////////////////////////////////////
650
651 static inline double
652 do_interp (double a, double b, double fraction)
653 {
654     double retval = (a + ((b - a) * fraction));
655     return retval;
656 }
657
658 static inline double
659 do_interp_deg (double a, double b, double fraction)
660 {
661     a = fmod(a, 360);
662     b = fmod(b, 360);
663     if (fabs(b-a) > 180) {
664         if (a < b)
665             a += 360;
666         else
667             b += 360;
668     }
669     return fmod(do_interp(a, b, fraction), 360);
670 }
671
672 void
673 interpolate (const FGEnvironment * env1, const FGEnvironment * env2,
674              double fraction, FGEnvironment * result)
675 {
676     result->set_visibility_m
677         (do_interp(env1->get_visibility_m(),
678                    env2->get_visibility_m(),
679                    fraction));
680
681     result->set_temperature_sea_level_degc
682         (do_interp(env1->get_temperature_sea_level_degc(),
683                    env2->get_temperature_sea_level_degc(),
684                    fraction));
685
686     result->set_dewpoint_degc
687         (do_interp(env1->get_dewpoint_sea_level_degc(),
688                    env2->get_dewpoint_sea_level_degc(),
689                    fraction));
690
691     result->set_pressure_sea_level_inhg
692         (do_interp(env1->get_pressure_sea_level_inhg(),
693                    env2->get_pressure_sea_level_inhg(),
694                    fraction));
695
696     result->set_wind_from_heading_deg
697         (do_interp_deg(env1->get_wind_from_heading_deg(),
698                        env2->get_wind_from_heading_deg(),
699                        fraction));
700
701     result->set_wind_speed_kt
702         (do_interp(env1->get_wind_speed_kt(),
703                    env2->get_wind_speed_kt(),
704                    fraction));
705
706     result->set_elevation_ft
707         (do_interp(env1->get_elevation_ft(),
708                    env2->get_elevation_ft(),
709                    fraction));
710
711     result->set_turbulence_magnitude_norm
712         (do_interp(env1->get_turbulence_magnitude_norm(),
713                    env2->get_turbulence_magnitude_norm(),
714                    fraction));
715
716     result->set_turbulence_rate_hz
717         (do_interp(env1->get_turbulence_rate_hz(),
718                    env2->get_turbulence_rate_hz(),
719                    fraction));
720 }
721
722 // end of environment.cxx