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