1 // environment_ctrl.cxx -- manager for natural environment information.
3 // Written by David Megginson, started February 2002.
5 // Copyright (C) 2002 David Megginson - david@megginson.com
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.
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.
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 #include <simgear/debug/logstream.hxx>
28 #include <simgear/structure/exception.hxx>
30 #include <Main/fg_props.hxx>
31 #include <Main/util.hxx>
33 #include "environment_mgr.hxx"
34 #include "environment_ctrl.hxx"
38 // FIXME, from options.cxx
39 extern void fgSetupWind (double min_hdg, double max_hdg, double speed, double gust);
43 ////////////////////////////////////////////////////////////////////////
44 // Implementation of FGEnvironmentCtrl abstract base class.
45 ////////////////////////////////////////////////////////////////////////
47 FGEnvironmentCtrl::FGEnvironmentCtrl ()
55 FGEnvironmentCtrl::~FGEnvironmentCtrl ()
60 FGEnvironmentCtrl::setEnvironment (FGEnvironment * environment)
62 _environment = environment;
66 FGEnvironmentCtrl::setLongitudeDeg (double lon_deg)
72 FGEnvironmentCtrl::setLatitudeDeg (double lat_deg)
78 FGEnvironmentCtrl::setElevationFt (double elev_ft)
84 FGEnvironmentCtrl::setPosition (double lon_deg, double lat_deg, double elev_ft)
93 ////////////////////////////////////////////////////////////////////////
94 // Implementation of FGUserDefEnvironmentCtrl.
95 ////////////////////////////////////////////////////////////////////////
97 FGUserDefEnvironmentCtrl::FGUserDefEnvironmentCtrl ()
98 : _base_wind_speed_node(0),
99 _gust_wind_speed_node(0),
100 _current_wind_speed_kt(0),
101 _delta_wind_speed_kt(0)
105 FGUserDefEnvironmentCtrl::~FGUserDefEnvironmentCtrl ()
110 FGUserDefEnvironmentCtrl::init ()
112 // Fill in some defaults.
113 if (!fgHasNode("/environment/params/base-wind-speed-kt"))
114 fgSetDouble("/environment/params/base-wind-speed-kt",
115 fgGetDouble("/environment/wind-speed-kt"));
116 if (!fgHasNode("/environment/params/gust-wind-speed-kt"))
117 fgSetDouble("/environment/params/gust-wind-speed-kt",
118 fgGetDouble("/environment/params/base-wind-speed-kt"));
120 _base_wind_speed_node =
121 fgGetNode("/environment/params/base-wind-speed-kt", true);
122 _gust_wind_speed_node =
123 fgGetNode("/environment/params/gust-wind-speed-kt", true);
125 _current_wind_speed_kt = _base_wind_speed_node->getDoubleValue();
126 _delta_wind_speed_kt = 0.1;
130 FGUserDefEnvironmentCtrl::update (double dt)
132 double base_wind_speed = _base_wind_speed_node->getDoubleValue();
133 double gust_wind_speed = _gust_wind_speed_node->getDoubleValue();
135 if (gust_wind_speed < base_wind_speed) {
136 gust_wind_speed = base_wind_speed;
137 _gust_wind_speed_node->setDoubleValue(gust_wind_speed);
140 if (base_wind_speed == gust_wind_speed) {
141 _current_wind_speed_kt = base_wind_speed;
143 int rn = rand() % 128;
144 int sign = (_delta_wind_speed_kt < 0 ? -1 : 1);
145 double gust = _current_wind_speed_kt - base_wind_speed;
146 double incr = gust / 50;
149 _delta_wind_speed_kt = - _delta_wind_speed_kt;
151 _delta_wind_speed_kt -= incr * sign;
153 _delta_wind_speed_kt += incr * sign;
155 _current_wind_speed_kt += _delta_wind_speed_kt;
157 if (_current_wind_speed_kt < base_wind_speed) {
158 _current_wind_speed_kt = base_wind_speed;
159 _delta_wind_speed_kt = 0.01;
160 } else if (_current_wind_speed_kt > gust_wind_speed) {
161 _current_wind_speed_kt = gust_wind_speed;
162 _delta_wind_speed_kt = -0.01;
166 if (_environment != 0)
167 _environment->set_wind_speed_kt(_current_wind_speed_kt);
172 ////////////////////////////////////////////////////////////////////////
173 // Implementation of FGInterpolateEnvironmentCtrl.
174 ////////////////////////////////////////////////////////////////////////
176 FGInterpolateEnvironmentCtrl::FGInterpolateEnvironmentCtrl ()
180 FGInterpolateEnvironmentCtrl::~FGInterpolateEnvironmentCtrl ()
183 for (i = 0; i < _boundary_table.size(); i++)
184 delete _boundary_table[i];
185 for (i = 0; i < _aloft_table.size(); i++)
186 delete _aloft_table[i];
192 FGInterpolateEnvironmentCtrl::init ()
194 read_table(fgGetNode("/environment/config/boundary", true),
196 read_table(fgGetNode("/environment/config/aloft", true),
201 FGInterpolateEnvironmentCtrl::reinit ()
204 for (i = 0; i < _boundary_table.size(); i++)
205 delete _boundary_table[i];
206 for (i = 0; i < _aloft_table.size(); i++)
207 delete _aloft_table[i];
208 _boundary_table.clear();
209 _aloft_table.clear();
214 FGInterpolateEnvironmentCtrl::read_table (const SGPropertyNode * node,
215 vector<bucket *> &table)
217 for (int i = 0; i < node->nChildren(); i++) {
218 const SGPropertyNode * child = node->getChild(i);
219 if ( strcmp(child->getName(), "entry") == 0
220 && child->getStringValue("elevation-ft", "")[0] != '\0'
221 && ( child->getDoubleValue("elevation-ft") > 0.1 || i == 0 ) )
223 bucket * b = new bucket;
225 b->environment.copy(table[i-1]->environment);
226 b->environment.read(child);
227 b->altitude_ft = b->environment.get_elevation_ft();
231 sort(table.begin(), table.end());
235 FGInterpolateEnvironmentCtrl::update (double delta_time_sec)
238 double altitude_ft = fgGetDouble("/position/altitude-ft");
239 double altitude_agl_ft = fgGetDouble("/position/altitude-agl-ft");
240 double boundary_transition =
241 fgGetDouble("/environment/config/boundary-transition-ft", 500);
243 // double ground_elevation_ft = altitude_ft - altitude_agl_ft;
245 int length = _boundary_table.size();
249 double boundary_limit = _boundary_table[length-1]->altitude_ft;
250 if (boundary_limit >= altitude_agl_ft) {
251 do_interpolate(_boundary_table, altitude_agl_ft,
254 } else if ((boundary_limit + boundary_transition) >= altitude_agl_ft) {
256 do_interpolate(_boundary_table, altitude_agl_ft, &env1);
257 do_interpolate(_aloft_table, altitude_ft, &env2);
259 (altitude_agl_ft - boundary_limit) / boundary_transition;
260 interpolate(&env1, &env2, fraction, _environment);
266 do_interpolate(_aloft_table, altitude_ft, _environment);
270 FGInterpolateEnvironmentCtrl::do_interpolate (vector<bucket *> &table,
272 FGEnvironment * environment)
274 int length = table.size();
278 // Boundary conditions
279 if ((length == 1) || (table[0]->altitude_ft >= altitude_ft)) {
280 environment->copy(table[0]->environment);
282 } else if (table[length-1]->altitude_ft <= altitude_ft) {
283 environment->copy(table[length-1]->environment);
287 // Search the interpolation table
288 for (int i = 0; i < length - 1; i++) {
289 if ((i == length - 1) || (table[i]->altitude_ft <= altitude_ft)) {
290 FGEnvironment * env1 = &(table[i]->environment);
291 FGEnvironment * env2 = &(table[i+1]->environment);
293 if (table[i]->altitude_ft == table[i+1]->altitude_ft)
297 ((altitude_ft - table[i]->altitude_ft) /
298 (table[i+1]->altitude_ft - table[i]->altitude_ft));
299 interpolate(env1, env2, fraction, environment);
307 FGInterpolateEnvironmentCtrl::bucket::operator< (const bucket &b) const
309 return (altitude_ft < b.altitude_ft);
314 ////////////////////////////////////////////////////////////////////////
315 // Implementation of FGMetarEnvironmentCtrl.
316 ////////////////////////////////////////////////////////////////////////
318 FGMetarEnvironmentCtrl::FGMetarEnvironmentCtrl ()
319 : env( new FGInterpolateEnvironmentCtrl ),
320 _icao( strdup( fgGetString("/sim/presets/airport-id") ) )
324 FGMetarEnvironmentCtrl::~FGMetarEnvironmentCtrl ()
337 FGMetarEnvironmentCtrl::init ()
345 fgSetupWind( fgGetDouble("/environment/metar/base-wind-range-from"),
346 fgGetDouble("/environment/metar/base-wind-range-to"),
347 fgGetDouble("/environment/metar/base-wind-speed-kt"),
348 fgGetDouble("/environment/metar/gust-wind-speed-kt") );
350 fgDefaultWeatherValue( "visibility-m",
351 fgGetDouble("/environment/metar/min-visibility-m") );
353 // FIXME: this isn't the correct place in the property tree to set
354 // the weather and it doesn't account for weather station
356 fgSetDouble("/environment/temperature-degc",
357 fgGetDouble("/environment/metar/temperature-degc"));
358 fgSetDouble("/environment/dewpoint-degc",
359 fgGetDouble("/environment/metar/dewpoint-degc"));
361 fgDefaultWeatherValue( "pressure-sea-level-inhg",
362 fgGetDouble("/environment/metar/pressure-inhg") );
368 FGMetarEnvironmentCtrl::reinit ()
377 fgSetupWind( fgGetDouble("/environment/metar/base-wind-range-from"),
378 fgGetDouble("/environment/metar/base-wind-range-to"),
379 fgGetDouble("/environment/metar/base-wind-speed-kt"),
380 fgGetDouble("/environment/metar/gust-wind-speed-kt") );
382 fgDefaultWeatherValue( "visibility-m",
383 fgGetDouble("/environment/metar/min-visibility-m") );
385 // FIXME: this isn't the correct place in the property tree to set
386 // the weather and it doesn't account for weather station
388 fgSetDouble("/environment/temperature-degc",
389 fgGetDouble("/environment/metar/temperature-degc"));
390 fgSetDouble("/environment/dewpoint-degc",
391 fgGetDouble("/environment/metar/dewpoint-degc"));
393 fgDefaultWeatherValue( "pressure-sea-level-inhg",
394 fgGetDouble("/environment/metar/pressure-inhg") );
401 FGMetarEnvironmentCtrl::update(double delta_time_sec)
403 env->update(delta_time_sec);
407 FGMetarEnvironmentCtrl::setEnvironment (FGEnvironment * environment)
409 env->setEnvironment(environment);
413 FGMetarEnvironmentCtrl::fetch_data (const char *icao)
419 if ((icao == NULL) && (_icao == NULL)) {
420 _icao = strdup( fgGetString("/sim/presets/airport-id") );
422 } else if (icao != NULL) {
426 _icao = strdup(icao);
431 m = new SGMetar(_icao);
432 } catch (const sg_io_exception& e) {
433 SG_LOG( SG_GENERAL, SG_WARN, "Error fetching live weather data: "
434 << e.getFormattedMessage().c_str() );
438 d = m->getMinVisibility().getVisibility_m();
439 d = (d != SGMetarNaN) ? d : 10000;
440 fgSetDouble("/environment/metar/min-visibility-m", d);
442 dt = m->getMaxVisibility().getVisibility_m();
443 d = (dt != SGMetarNaN) ? dt : d;
444 fgSetDouble("/environment/metar/max-visibility-m", d);
446 SGMetarVisibility *dirvis = m->getDirVisibility();
447 for (i = 0; i < 8; i++, dirvis++) {
448 const char *min = "/environment/metar/visibility[%d]/min-m";
449 const char *max = "/environment/metar/visibility[%d]/max-m";
452 d = dirvis->getVisibility_m();
453 d = (d != SGMetarNaN) ? d : 10000;
455 snprintf(s, 128, min, i);
457 snprintf(s, 128, max, i);
463 fgSetInt("/environment/metar/base-wind-range-from",
464 m->getWindRangeFrom() );
465 fgSetInt("/environment/metar/base-wind-range-to",
466 m->getWindRangeTo() );
468 fgSetInt("/environment/metar/base-wind-range-from", i);
469 fgSetInt("/environment/metar/base-wind-range-to", i);
471 fgSetDouble("/environment/metar/base-wind-speed-kt",
472 m->getWindSpeed_kt() );
474 d = m->getGustSpeed_kt();
475 d = (d != SGMetarNaN) ? d : 0.0;
476 fgSetDouble("/environment/metar/gust-wind-speed-kt", d);
478 d = m->getTemperature_C();
479 if (d != SGMetarNaN) {
480 dt = m->getDewpoint_C();
481 dt = (dt != SGMetarNaN) ? dt : 0.0;
482 fgSetDouble("/environment/metar/dewpoint-degc", dt);
483 fgSetDouble("/environment/metar/rel-humidity-norm",
484 m->getRelHumidity() );
486 d = (d != SGMetarNaN) ? d : 15.0;
487 fgSetDouble("/environment/metar/temperature-degc", d);
489 d = m->getPressure_inHg();
490 d = (d != SGMetarNaN) ? d : 30.0;
491 fgSetDouble("/environment/metar/pressure-inhg", d);
493 vector<SGMetarCloud> cv = m->getClouds();
494 vector<SGMetarCloud>::iterator cloud;
496 const char *cl = "/environment/clouds/layer[%i]";
497 for (i = 0, cloud = cv.begin(); cloud != cv.end(); cloud++, i++) {
498 const char *coverage_string[5] =
499 { "clear", "few", "scattered", "broken", "overcast" };
500 const double thickness[5] = { 0, 65, 600,750, 1000};
503 snprintf(s, 128, cl, i);
504 strncat(s, "/coverage", 128);
505 q = cloud->getCoverage();
506 q = (q != -1 ) ? q : 0;
507 fgSetString(s, coverage_string[q] );
509 snprintf(s, 128, cl, i);
510 strncat(s, "/elevation-ft", 128);
511 d = cloud->getAltitude_ft();
512 d = (d != SGMetarNaN) ? d : -9999;
515 snprintf(s, 128, cl, i);
516 strncat(s, "/thickness-ft", 128);
517 fgSetDouble(s, thickness[q]);
519 snprintf(s, 128, cl, i);
520 strncat(s, "/span-m", 128);
521 fgSetDouble(s, 40000.0);
523 for (; i < FGEnvironmentMgr::MAX_CLOUD_LAYERS; i++) {
524 snprintf(s, 128, cl, i);
525 strncat(s, "/coverage", 128);
526 fgSetString(s, "clear");
528 snprintf(s, 128, cl, i);
529 strncat(s, "/elevation-ft", 128);
530 fgSetDouble(s, -9999);
532 snprintf(s, 128, cl, i);
533 strncat(s, "/thickness-ft", 128);
536 snprintf(s, 128, cl, i);
537 strncat(s, "/span-m", 128);
538 fgSetDouble(s, 40000.0);
545 // end of environment_ctrl.cxx