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