]> git.mxchange.org Git - flightgear.git/blob - src/Environment/environment.cxx
Merge commit 'refs/merge-requests/6' of git@gitorious.org:fg/flightgear into next
[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 <boost/tuple/tuple.hpp>
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 #include <signal.h>
40
41 #include "environment.hxx"
42 #include "atmosphere.hxx"
43
44 \f
45 ////////////////////////////////////////////////////////////////////////
46 // Atmosphere model.
47 ////////////////////////////////////////////////////////////////////////
48
49 #ifdef USING_TABLES
50
51 // Calculated based on the ISA standard day, as found at e.g.
52 // http://www.av8n.com/physics/altimetry.htm
53
54 // Each line of data has 3 elements:
55 //   Elevation (ft), 
56 //   temperature factor (dimensionless ratio of absolute temp), 
57 //   pressure factor (dimensionless ratio)
58 static double atmosphere_data[][3] = {
59  {  -3000.00,   1.021,  1.1133 },
60  {      0.00,   1.000,  1.0000 },
61  {   2952.76,   0.980,  0.8978 },
62  {   5905.51,   0.959,  0.8042 },
63  {   8858.27,   0.939,  0.7187 },
64  {  11811.02,   0.919,  0.6407 },
65  {  14763.78,   0.898,  0.5697 },
66  {  17716.54,   0.878,  0.5052 },
67  {  20669.29,   0.858,  0.4468 },
68  {  23622.05,   0.838,  0.3940 },
69  {  26574.80,   0.817,  0.3463 },
70  {  29527.56,   0.797,  0.3034 },
71  {  32480.31,   0.777,  0.2649 },
72  {  35433.07,   0.756,  0.2305 },
73  {  38385.83,   0.752,  0.2000 },
74  {  41338.58,   0.752,  0.1736 },
75  {  44291.34,   0.752,  0.1506 },
76  {  47244.09,   0.752,  0.1307 },
77  {  50196.85,   0.752,  0.1134 },
78  {  53149.61,   0.752,  0.0984 },
79  {  56102.36,   0.752,  0.0854 },
80  {  59055.12,   0.752,  0.0741 },
81  {  62007.87,   0.752,  0.0643 },
82  {  65000.00,   0.752,  0.0557 },
83  {  68000.00,   0.754,  0.0482 },
84  {  71000.00,   0.758,  0.0418 },
85  {  74000.00,   0.761,  0.0362 },
86  {  77000.00,   0.764,  0.0314 },
87  {  80000.00,   0.767,  0.0273 },
88  {  83000.00,   0.770,  0.0237 },
89  {  86000.00,   0.773,  0.0206 },
90  {  89000.00,   0.777,  0.0179 },
91  {  92000.00,   0.780,  0.0156 },
92  {  95000.00,   0.783,  0.0135 },
93  {  98000.00,   0.786,  0.0118 },
94  { 101000.00,   0.789,  0.0103 },
95  { -1, -1, -1 }
96 };
97
98 static SGInterpTable * _temperature_degc_table = 0;
99 static SGInterpTable * _pressure_inhg_table = 0;
100
101 static void
102 _setup_tables ()
103 {
104   if (_temperature_degc_table != 0)
105       return;
106
107   _temperature_degc_table = new SGInterpTable;
108   _pressure_inhg_table = new SGInterpTable;
109
110   for (int i = 0; atmosphere_data[i][0] != -1; i++) {
111     _temperature_degc_table->addEntry(atmosphere_data[i][0],
112                                       atmosphere_data[i][1]);
113     _pressure_inhg_table->addEntry(atmosphere_data[i][0],
114                                    atmosphere_data[i][2]);
115   }
116 }
117 #endif
118
119 \f
120 ////////////////////////////////////////////////////////////////////////
121 // Implementation of FGEnvironment.
122 ////////////////////////////////////////////////////////////////////////
123
124 void FGEnvironment::_init()
125 {
126     elevation_ft = 0;
127     visibility_m = 32000;
128     temperature_sea_level_degc = 15;
129     temperature_degc = 15;
130     dewpoint_sea_level_degc = 5; // guess
131     dewpoint_degc = 5;
132     pressure_sea_level_inhg = 29.92;
133     pressure_inhg = 29.92;
134     turbulence_magnitude_norm = 0;
135     turbulence_rate_hz = 1;
136     wind_from_heading_deg = 0;
137     wind_speed_kt = 0;
138     wind_from_north_fps = 0;
139     wind_from_east_fps = 0;
140     wind_from_down_fps = 0;
141     thermal_lift_fps = 0;
142     ridge_lift_fps= 0;
143     local_weather_lift_fps=0;
144     altitude_half_to_sun_m = 1000;
145     altitude_tropo_top_m = 10000;
146 #ifdef USING_TABLES
147     _setup_tables();
148 #endif
149     _recalc_density();
150     _recalc_relative_humidity();
151     live_update = true;
152 }
153
154 FGEnvironment::FGEnvironment()
155 {
156     _init();
157 }
158
159 FGEnvironment::FGEnvironment (const FGEnvironment &env)
160 {
161     _init();
162     copy(env);
163 }
164
165 FGEnvironment::~FGEnvironment()
166 {
167 }
168
169 void
170 FGEnvironment::copy (const FGEnvironment &env)
171 {
172     elevation_ft = env.elevation_ft;
173     visibility_m = env.visibility_m;
174     temperature_sea_level_degc = env.temperature_sea_level_degc;
175     temperature_degc = env.temperature_degc;
176     dewpoint_sea_level_degc = env.dewpoint_sea_level_degc;
177     dewpoint_degc = env.dewpoint_degc;
178     pressure_sea_level_inhg = env.pressure_sea_level_inhg;
179     wind_from_heading_deg = env.wind_from_heading_deg;
180     wind_speed_kt = env.wind_speed_kt;
181     wind_from_north_fps = env.wind_from_north_fps;
182     wind_from_east_fps = env.wind_from_east_fps;
183     wind_from_down_fps = env.wind_from_down_fps;
184     thermal_lift_fps = env.thermal_lift_fps;
185     ridge_lift_fps= env.ridge_lift_fps;
186     local_weather_lift_fps = env.local_weather_lift_fps;
187     turbulence_magnitude_norm = env.turbulence_magnitude_norm;
188     turbulence_rate_hz = env.turbulence_rate_hz;
189 }
190
191 static inline bool
192 maybe_copy_value (FGEnvironment * env, const SGPropertyNode * node,
193                   const char * name, void (FGEnvironment::*setter)(double))
194 {
195     const SGPropertyNode * child = node->getNode(name);
196                                 // fragile: depends on not being typed
197                                 // as a number
198     if (child != 0 && child->hasValue() &&
199         child->getStringValue()[0] != '\0') {
200         (env->*setter)(child->getDoubleValue());
201         return true;
202     } else {
203         return false;
204     }
205 }
206
207 void
208 FGEnvironment::read (const SGPropertyNode * node)
209 {
210     bool live_update = set_live_update( false );
211     maybe_copy_value(this, node, "visibility-m",
212                      &FGEnvironment::set_visibility_m);
213
214     maybe_copy_value(this, node, "elevation-ft",
215                      &FGEnvironment::set_elevation_ft);
216
217     if (!maybe_copy_value(this, node, "temperature-sea-level-degc",
218                           &FGEnvironment::set_temperature_sea_level_degc)) {
219         if( maybe_copy_value(this, node, "temperature-degc",
220                          &FGEnvironment::set_temperature_degc)) {
221             _recalc_sl_temperature();
222         }
223     }
224
225     if (!maybe_copy_value(this, node, "dewpoint-sea-level-degc",
226                           &FGEnvironment::set_dewpoint_sea_level_degc)) {
227         if( maybe_copy_value(this, node, "dewpoint-degc",
228                          &FGEnvironment::set_dewpoint_degc)) {
229             _recalc_sl_dewpoint();
230         }
231     }
232
233     if (!maybe_copy_value(this, node, "pressure-sea-level-inhg",
234                           &FGEnvironment::set_pressure_sea_level_inhg)) {
235         if( maybe_copy_value(this, node, "pressure-inhg",
236                          &FGEnvironment::set_pressure_inhg)) {
237             _recalc_sl_pressure();
238         }
239    }
240
241     maybe_copy_value(this, node, "wind-from-heading-deg",
242                      &FGEnvironment::set_wind_from_heading_deg);
243
244     maybe_copy_value(this, node, "wind-speed-kt",
245                      &FGEnvironment::set_wind_speed_kt);
246
247     maybe_copy_value(this, node, "turbulence/magnitude-norm",
248                      &FGEnvironment::set_turbulence_magnitude_norm);
249
250     maybe_copy_value(this, node, "turbulence/rate-hz",
251                      &FGEnvironment::set_turbulence_rate_hz);
252
253     // calculate derived properties here to avoid duplicate expensive computations
254     _recalc_ne();
255     _recalc_alt_pt();
256     _recalc_alt_dewpoint();
257     _recalc_density();
258     _recalc_relative_humidity();
259
260     set_live_update(live_update);
261 }
262
263
264 double
265 FGEnvironment::get_visibility_m () const
266 {
267   return visibility_m;
268 }
269
270 double
271 FGEnvironment::get_temperature_sea_level_degc () const
272 {
273   return temperature_sea_level_degc;
274 }
275
276 double
277 FGEnvironment::get_temperature_degc () const
278 {
279   return temperature_degc;
280 }
281
282 double
283 FGEnvironment::get_temperature_degf () const
284 {
285   return (temperature_degc * 9.0 / 5.0) + 32.0;
286 }
287
288 double
289 FGEnvironment::get_dewpoint_sea_level_degc () const
290 {
291   return dewpoint_sea_level_degc;
292 }
293
294 double
295 FGEnvironment::get_dewpoint_degc () const
296 {
297   return dewpoint_degc;
298 }
299
300 double
301 FGEnvironment::get_pressure_sea_level_inhg () const
302 {
303   return pressure_sea_level_inhg;
304 }
305
306 double
307 FGEnvironment::get_pressure_inhg () const
308 {
309   return pressure_inhg;
310 }
311
312 double
313 FGEnvironment::get_density_slugft3 () const
314 {
315   return density_slugft3;
316 }
317
318 double
319 FGEnvironment::get_relative_humidity () const
320 {
321   return relative_humidity;
322 }
323
324 double
325 FGEnvironment::get_density_tropo_avg_kgm3 () const
326 {
327   return density_tropo_avg_kgm3;
328 }
329
330 double
331 FGEnvironment::get_altitude_half_to_sun_m () const
332 {
333   return altitude_half_to_sun_m;
334 }
335
336 double
337 FGEnvironment::get_altitude_tropo_top_m () const
338 {
339   return altitude_tropo_top_m;
340 }
341
342 double
343 FGEnvironment::get_wind_from_heading_deg () const
344 {
345   return wind_from_heading_deg;
346 }
347
348 double
349 FGEnvironment::get_wind_speed_kt () const
350 {
351   return wind_speed_kt;
352 }
353
354 double
355 FGEnvironment::get_wind_from_north_fps () const
356 {
357   return wind_from_north_fps;
358 }
359
360 double
361 FGEnvironment::get_wind_from_east_fps () const
362 {
363   return wind_from_east_fps;
364 }
365
366 double
367 FGEnvironment::get_wind_from_down_fps () const
368 {
369   return wind_from_down_fps;
370 }
371
372 double
373 FGEnvironment::get_thermal_lift_fps () const
374 {
375   return thermal_lift_fps;
376 }
377
378 double
379 FGEnvironment::get_ridge_lift_fps () const
380 {
381   return ridge_lift_fps;
382 }
383
384 double
385 FGEnvironment::get_local_weather_lift_fps () const
386 {
387   return local_weather_lift_fps;
388 }
389
390 double
391 FGEnvironment::get_turbulence_magnitude_norm () const
392 {
393   if( sgEnviro.get_turbulence_enable_state() )
394     if (fgGetBool("/environment/params/real-world-weather-fetch") == true)
395       return sgEnviro.get_cloud_turbulence();
396   return turbulence_magnitude_norm;
397 }
398
399 double
400 FGEnvironment::get_turbulence_rate_hz () const
401 {
402   return turbulence_rate_hz;
403 }
404
405 double
406 FGEnvironment::get_elevation_ft () const
407 {
408   return elevation_ft;
409 }
410
411 void
412 FGEnvironment::set_visibility_m (double v)
413 {
414   visibility_m = v;
415 }
416
417 void
418 FGEnvironment::set_temperature_sea_level_degc (double t)
419 {
420   temperature_sea_level_degc = t;
421   if (dewpoint_sea_level_degc > t)
422       dewpoint_sea_level_degc = t;
423   if( live_update ) {
424     _recalc_alt_pt();
425     _recalc_density();
426   }
427 }
428
429 void
430 FGEnvironment::set_temperature_degc (double t)
431 {
432   temperature_degc = t;
433   if( live_update ) {
434     _recalc_sl_temperature();
435     _recalc_sl_pressure();
436     _recalc_alt_pt();
437     _recalc_density();
438     _recalc_relative_humidity();
439   }
440 }
441
442 void
443 FGEnvironment::set_dewpoint_sea_level_degc (double t)
444 {
445   dewpoint_sea_level_degc = t;
446   if (temperature_sea_level_degc < t)
447       temperature_sea_level_degc = t;
448   if( live_update ) {
449     _recalc_alt_dewpoint();
450     _recalc_density();
451   }
452 }
453
454 void
455 FGEnvironment::set_dewpoint_degc (double t)
456 {
457   dewpoint_degc = t;
458   if( live_update ) {
459     _recalc_sl_dewpoint();
460     _recalc_density();
461     _recalc_relative_humidity();
462   }
463 }
464
465 void
466 FGEnvironment::set_pressure_sea_level_inhg (double p)
467 {
468   pressure_sea_level_inhg = p;
469   if( live_update ) {
470     _recalc_alt_pt();
471     _recalc_density();
472   }
473 }
474
475 void
476 FGEnvironment::set_pressure_inhg (double p)
477 {
478   pressure_inhg = p;
479   if( live_update ) {
480     _recalc_sl_pressure();
481     _recalc_density();
482   }
483 }
484
485 void
486 FGEnvironment::set_wind_from_heading_deg (double h)
487 {
488   wind_from_heading_deg = h;
489   if( live_update ) {
490     _recalc_ne();
491   }
492 }
493
494 void
495 FGEnvironment::set_wind_speed_kt (double s)
496 {
497   wind_speed_kt = s;
498   if( live_update ) {
499     _recalc_ne();
500   }
501 }
502
503 void
504 FGEnvironment::set_wind_from_north_fps (double n)
505 {
506   wind_from_north_fps = n;
507   if( live_update ) {
508     _recalc_hdgspd();
509   }
510 }
511
512 void
513 FGEnvironment::set_wind_from_east_fps (double e)
514 {
515   wind_from_east_fps = e;
516   if( live_update ) {
517     _recalc_hdgspd();
518   }
519 }
520
521 void
522 FGEnvironment::set_wind_from_down_fps (double d)
523 {
524   wind_from_down_fps = d;
525   if( live_update ) {
526     _recalc_hdgspd();
527   }
528 }
529
530 void
531 FGEnvironment::set_thermal_lift_fps (double th)
532 {
533   thermal_lift_fps = th;
534   if( live_update ) {
535     _recalc_updraft();
536   }
537 }
538
539 void
540 FGEnvironment::set_ridge_lift_fps (double ri)
541 {
542   ridge_lift_fps = ri;
543   if( live_update ) {
544     _recalc_updraft();
545   }
546 }
547
548 void
549 FGEnvironment::set_local_weather_lift_fps (double lwl)
550 {
551   local_weather_lift_fps = lwl;
552   if( live_update ) {
553     _recalc_updraft();
554   }
555 }
556
557 void
558 FGEnvironment::set_turbulence_magnitude_norm (double t)
559 {
560   turbulence_magnitude_norm = t;
561 }
562
563 void
564 FGEnvironment::set_turbulence_rate_hz (double r)
565 {
566   turbulence_rate_hz = r;
567 }
568
569 void
570 FGEnvironment::set_elevation_ft (double e)
571 {
572   elevation_ft = e;
573   if( live_update ) {
574     _recalc_alt_pt();
575     _recalc_alt_dewpoint();
576     _recalc_density();
577     _recalc_relative_humidity();
578   }
579 }
580
581 void
582 FGEnvironment::set_altitude_half_to_sun_m (double alt)
583 {
584   altitude_half_to_sun_m = alt;
585   if( live_update ) {
586     _recalc_density_tropo_avg_kgm3();
587   }
588 }
589
590 void
591 FGEnvironment::set_altitude_tropo_top_m (double alt)
592 {
593   altitude_tropo_top_m = alt;
594   if( live_update ) {
595     _recalc_density_tropo_avg_kgm3();
596   }
597 }
598
599
600 void
601 FGEnvironment::_recalc_hdgspd ()
602 {
603   double angle_rad;
604
605   if (wind_from_east_fps == 0) {
606     angle_rad = (wind_from_north_fps >= 0 ? SGD_PI_2 : -SGD_PI_2);
607   } else {
608     angle_rad = atan(wind_from_north_fps/wind_from_east_fps);
609   }
610   wind_from_heading_deg = angle_rad * SGD_RADIANS_TO_DEGREES;
611   if (wind_from_east_fps >= 0)
612     wind_from_heading_deg = 90 - wind_from_heading_deg;
613   else
614     wind_from_heading_deg = 270 - wind_from_heading_deg;
615
616 #if 0
617   // FIXME: Windspeed can become negative with these formulas.
618   //        which can cause problems for animations that rely
619   //        on the windspeed property.
620   if (angle_rad == 0)
621     wind_speed_kt = fabs(wind_from_east_fps
622                          * SG_METER_TO_NM * SG_FEET_TO_METER * 3600);
623   else
624     wind_speed_kt = (wind_from_north_fps / sin(angle_rad))
625       * SG_METER_TO_NM * SG_FEET_TO_METER * 3600;
626 #else
627   wind_speed_kt = sqrt(wind_from_north_fps * wind_from_north_fps +
628                        wind_from_east_fps * wind_from_east_fps) 
629                   * SG_METER_TO_NM * SG_FEET_TO_METER * 3600;
630 #endif
631 }
632
633 void
634 FGEnvironment::_recalc_ne ()
635 {
636   double speed_fps =
637     wind_speed_kt * SG_NM_TO_METER * SG_METER_TO_FEET * (1.0/3600);
638
639   wind_from_north_fps = speed_fps *
640     cos(wind_from_heading_deg * SGD_DEGREES_TO_RADIANS);
641   wind_from_east_fps = speed_fps *
642     sin(wind_from_heading_deg * SGD_DEGREES_TO_RADIANS);
643 }
644
645 void
646 FGEnvironment::_recalc_updraft ()
647 {
648   wind_from_down_fps = thermal_lift_fps + ridge_lift_fps + local_weather_lift_fps ;
649 }
650
651 // Intended to help with the interpretation of METAR data,
652 // not for random in-flight outside-air temperatures.
653 void
654 FGEnvironment::_recalc_sl_temperature ()
655 {
656
657 #if 0
658   {
659     SG_LOG(SG_GENERAL, SG_DEBUG, "recalc_sl_temperature: using "
660       << temperature_degc << " @ " << elevation_ft << " :: " << this);
661   }
662 #endif
663
664   if (elevation_ft * atmodel::foot >= ISA_def[1].height) {
665     SG_LOG(SG_GENERAL, SG_ALERT, "recalc_sl_temperature: "
666         << "valid only in troposphere, not " << elevation_ft);
667     return;
668   }
669
670 // Clamp: temperature of the stratosphere, in degrees C:
671   double t_strato = ISA_def[1].temp - atmodel::freezing;
672   if (temperature_degc < t_strato) temperature_sea_level_degc = t_strato;
673   else temperature_sea_level_degc = 
674       temperature_degc + elevation_ft * atmodel::foot * ISA_def[0].lapse;
675
676 // Alternative implemenation:
677 //  else temperature_sea_level_inhg = T_layer(0., elevation_ft * foot,
678 //      pressure_inhg * inHg, temperature_degc + freezing, ISA_def[0].lapse) - freezing;
679 }
680
681 void
682 FGEnvironment::_recalc_sl_dewpoint ()
683 {
684                                 // 0.2degC/1000ft
685                                 // FIXME: this will work only for low
686                                 // elevations
687   dewpoint_sea_level_degc = dewpoint_degc + (elevation_ft * .0002);
688   if (dewpoint_sea_level_degc > temperature_sea_level_degc)
689     dewpoint_sea_level_degc = temperature_sea_level_degc;
690 }
691
692 void
693 FGEnvironment::_recalc_alt_dewpoint ()
694 {
695                                 // 0.2degC/1000ft
696                                 // FIXME: this will work only for low
697                                 // elevations
698   dewpoint_degc = dewpoint_sea_level_degc + (elevation_ft * .0002);
699   if (dewpoint_degc > temperature_degc)
700     dewpoint_degc = temperature_degc;
701 }
702
703 void
704 FGEnvironment::_recalc_sl_pressure ()
705 {
706   using namespace atmodel;
707 #if 0
708   {
709     SG_LOG(SG_GENERAL, SG_ALERT, "recalc_sl_pressure: using "
710       << pressure_inhg << " and "
711       << temperature_degc << " @ " << elevation_ft << " :: " << this);
712   }
713 #endif
714   pressure_sea_level_inhg = P_layer(0., elevation_ft * foot,
715       pressure_inhg * inHg, temperature_degc + freezing, ISA_def[0].lapse) / inHg;
716 }
717
718 // This gets called at frame rate, to account for the aircraft's
719 // changing altitude. 
720 // Called by set_elevation_ft() which is called by FGEnvironmentMgr::update
721
722 void
723 FGEnvironment::_recalc_alt_pt ()
724 {
725   using namespace atmodel;
726 #if 0
727   {
728     static int count(0);
729     if (++count % 1000 == 0) {
730       SG_LOG(SG_GENERAL, SG_ALERT, 
731            "recalc_alt_pt for: " << elevation_ft
732         << "  using "  << pressure_sea_level_inhg 
733         << "  and "  << temperature_sea_level_degc
734         << " :: " << this
735         << "  # " << count);
736     }
737   }
738 #endif
739   double press, temp;
740   boost::tie(press, temp) = PT_vs_hpt(elevation_ft * foot, 
741         pressure_sea_level_inhg * inHg, temperature_sea_level_degc + freezing);
742   temperature_degc = temp - freezing;
743   pressure_inhg = press / inHg;
744 }
745
746 void
747 FGEnvironment::_recalc_density ()
748 {
749   double pressure_psf = pressure_inhg * 70.7487;
750   
751   // adjust for humidity
752   // calculations taken from USA Today (oops!) at
753   // http://www.usatoday.com/weather/basics/density-calculations.htm
754   double temperature_degk = temperature_degc + 273.15;
755   double pressure_mb = pressure_inhg * 33.86;
756   double vapor_pressure_mb =
757     6.11 * pow(10.0, 7.5 * dewpoint_degc / (237.7 + dewpoint_degc));
758   double virtual_temperature_degk = temperature_degk / (1 - (vapor_pressure_mb / pressure_mb) * (1.0 - 0.622));
759   double virtual_temperature_degr = virtual_temperature_degk * 1.8;
760
761   density_slugft3 = pressure_psf / (virtual_temperature_degr * 1718);
762   _recalc_density_tropo_avg_kgm3();
763 }
764
765 // This is used to calculate the average density on the path 
766 // of sunlight to the observer for calculating sun-color
767 void
768 FGEnvironment::_recalc_density_tropo_avg_kgm3 ()
769 {
770   double pressure_mb = pressure_inhg * 33.86;
771   double vaporpressure = 6.11 * pow(10.0, ((7.5 * dewpoint_degc) / (237.7 + dewpoint_degc)));
772
773   double virtual_temp = (temperature_degc + 273.15) / (1 - 0.379 * (vaporpressure/pressure_mb));
774
775   double density_half = (100 * pressure_mb * exp(-altitude_half_to_sun_m / 8000))
776       / (287.05 * virtual_temp);
777   double density_tropo = (100 * pressure_mb * exp((-1 * altitude_tropo_top_m) / 8000))
778       / ( 287.05 * virtual_temp);
779
780   density_tropo_avg_kgm3 = ((density_slugft3 * 515.379) + density_half + density_tropo) / 3;
781 }
782
783 void
784 FGEnvironment::_recalc_relative_humidity ()
785 {
786 /*
787   double vaporpressure = 6.11 * pow(10.0, ((7.5 * dewpoint_degc) / ( 237.7 + dewpoint_degc)));
788   double sat_vaporpressure = 6.11 * pow(10.0, ((7.5 * temperature_degc)
789       / ( 237.7 + temperature_degc)) );
790   relative_humidity = 100 * vaporpressure / sat_vaporpressure ;
791
792   with a little algebra, this gets the same result and spares two multiplications and one pow()
793 */
794   double a = (7.5 * dewpoint_degc)    / ( 237.7 + dewpoint_degc);
795   double b = (7.5 * temperature_degc) / ( 237.7 + temperature_degc);
796   relative_humidity = 100 * pow(10.0,a-b); 
797 }
798
799 bool
800 FGEnvironment::set_live_update( bool _live_update )
801 {
802   bool b = live_update;
803   live_update = _live_update;
804   return b;
805 }
806
807
808 ////////////////////////////////////////////////////////////////////////
809 // Functions.
810 ////////////////////////////////////////////////////////////////////////
811
812 static inline double
813 do_interp (double a, double b, double fraction)
814 {
815     double retval = (a + ((b - a) * fraction));
816     return retval;
817 }
818
819 static inline double
820 do_interp_deg (double a, double b, double fraction)
821 {
822     a = fmod(a, 360);
823     b = fmod(b, 360);
824     if (fabs(b-a) > 180) {
825         if (a < b)
826             a += 360;
827         else
828             b += 360;
829     }
830     return fmod(do_interp(a, b, fraction), 360);
831 }
832
833 void
834 interpolate (const FGEnvironment * env1, const FGEnvironment * env2,
835              double fraction, FGEnvironment * result)
836 {
837     // don't calculate each internal property every time we set a single value
838     // we trigger that at the end of the interpolation process
839     bool live_update = result->set_live_update( false );
840
841     result->set_visibility_m
842         (do_interp(env1->get_visibility_m(),
843                    env2->get_visibility_m(),
844                    fraction));
845
846     result->set_temperature_sea_level_degc
847         (do_interp(env1->get_temperature_sea_level_degc(),
848                    env2->get_temperature_sea_level_degc(),
849                    fraction));
850
851     result->set_dewpoint_sea_level_degc
852         (do_interp(env1->get_dewpoint_sea_level_degc(),
853                    env2->get_dewpoint_sea_level_degc(),
854                    fraction));
855
856     result->set_pressure_sea_level_inhg
857         (do_interp(env1->get_pressure_sea_level_inhg(),
858                    env2->get_pressure_sea_level_inhg(),
859                    fraction));
860
861     result->set_wind_from_heading_deg
862         (do_interp_deg(env1->get_wind_from_heading_deg(),
863                        env2->get_wind_from_heading_deg(),
864                        fraction));
865
866     result->set_wind_speed_kt
867         (do_interp(env1->get_wind_speed_kt(),
868                    env2->get_wind_speed_kt(),
869                    fraction));
870
871     result->set_elevation_ft
872         (do_interp(env1->get_elevation_ft(),
873                    env2->get_elevation_ft(),
874                    fraction));
875
876     result->set_turbulence_magnitude_norm
877         (do_interp(env1->get_turbulence_magnitude_norm(),
878                    env2->get_turbulence_magnitude_norm(),
879                    fraction));
880
881     result->set_turbulence_rate_hz
882         (do_interp(env1->get_turbulence_rate_hz(),
883                    env2->get_turbulence_rate_hz(),
884                    fraction));
885
886     // calculate derived properties here to avoid duplicate expensive computations
887     result->_recalc_ne();
888     result->_recalc_alt_pt();
889     result->_recalc_alt_dewpoint();
890     result->_recalc_density();
891     result->_recalc_relative_humidity();
892
893     result->set_live_update(live_update);
894 }
895
896 // end of environment.cxx