1
0
Fork 0
flightgear/src/Environment/environment_ctrl.cxx

731 lines
22 KiB
C++
Raw Normal View History

// environment_ctrl.cxx -- manager for natural environment information.
//
// Written by David Megginson, started February 2002.
//
// Copyright (C) 2002 David Megginson - david@megginson.com
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
// $Id$
#include <simgear/debug/logstream.hxx>
#include <stdlib.h>
2003-06-10 12:03:07 +00:00
#include <algorithm>
#include <simgear/structure/commands.hxx>
#include <simgear/structure/exception.hxx>
#include <Airports/simple.hxx>
#include <Main/fg_props.hxx>
#include <Main/util.hxx>
#include "environment_mgr.hxx"
#include "environment_ctrl.hxx"
2003-06-09 08:44:59 +00:00
SG_USING_STD(sort);
////////////////////////////////////////////////////////////////////////
// Implementation of FGEnvironmentCtrl abstract base class.
////////////////////////////////////////////////////////////////////////
FGEnvironmentCtrl::FGEnvironmentCtrl ()
: _environment(0),
_lon_deg(0),
_lat_deg(0),
_elev_ft(0)
{
}
FGEnvironmentCtrl::~FGEnvironmentCtrl ()
{
}
void
FGEnvironmentCtrl::setEnvironment (FGEnvironment * environment)
{
_environment = environment;
}
void
FGEnvironmentCtrl::setLongitudeDeg (double lon_deg)
{
_lon_deg = lon_deg;
}
void
FGEnvironmentCtrl::setLatitudeDeg (double lat_deg)
{
_lat_deg = lat_deg;
}
void
FGEnvironmentCtrl::setElevationFt (double elev_ft)
{
_elev_ft = elev_ft;
}
void
FGEnvironmentCtrl::setPosition (double lon_deg, double lat_deg, double elev_ft)
{
_lon_deg = lon_deg;
_lat_deg = lat_deg;
_elev_ft = elev_ft;
}
////////////////////////////////////////////////////////////////////////
// Implementation of FGUserDefEnvironmentCtrl.
////////////////////////////////////////////////////////////////////////
FGUserDefEnvironmentCtrl::FGUserDefEnvironmentCtrl ()
: _base_wind_speed_node(0),
_gust_wind_speed_node(0),
_current_wind_speed_kt(0),
_delta_wind_speed_kt(0)
{
}
FGUserDefEnvironmentCtrl::~FGUserDefEnvironmentCtrl ()
{
}
void
FGUserDefEnvironmentCtrl::init ()
{
// Fill in some defaults.
if (!fgHasNode("/environment/params/base-wind-speed-kt"))
fgSetDouble("/environment/params/base-wind-speed-kt",
fgGetDouble("/environment/wind-speed-kt"));
if (!fgHasNode("/environment/params/gust-wind-speed-kt"))
fgSetDouble("/environment/params/gust-wind-speed-kt",
fgGetDouble("/environment/params/base-wind-speed-kt"));
_base_wind_speed_node =
fgGetNode("/environment/params/base-wind-speed-kt", true);
_gust_wind_speed_node =
fgGetNode("/environment/params/gust-wind-speed-kt", true);
_current_wind_speed_kt = _base_wind_speed_node->getDoubleValue();
_delta_wind_speed_kt = 0.1;
}
void
FGUserDefEnvironmentCtrl::update (double dt)
{
double base_wind_speed = _base_wind_speed_node->getDoubleValue();
double gust_wind_speed = _gust_wind_speed_node->getDoubleValue();
if (gust_wind_speed < base_wind_speed) {
gust_wind_speed = base_wind_speed;
_gust_wind_speed_node->setDoubleValue(gust_wind_speed);
}
if (base_wind_speed == gust_wind_speed) {
_current_wind_speed_kt = base_wind_speed;
} else {
int rn = rand() % 128;
int sign = (_delta_wind_speed_kt < 0 ? -1 : 1);
double gust = _current_wind_speed_kt - base_wind_speed;
double incr = gust / 50;
if (rn == 0)
_delta_wind_speed_kt = - _delta_wind_speed_kt;
else if (rn < 4)
_delta_wind_speed_kt -= incr * sign;
else if (rn < 16)
_delta_wind_speed_kt += incr * sign;
_current_wind_speed_kt += _delta_wind_speed_kt;
if (_current_wind_speed_kt < base_wind_speed) {
_current_wind_speed_kt = base_wind_speed;
_delta_wind_speed_kt = 0.01;
} else if (_current_wind_speed_kt > gust_wind_speed) {
_current_wind_speed_kt = gust_wind_speed;
_delta_wind_speed_kt = -0.01;
}
}
if (_environment != 0)
_environment->set_wind_speed_kt(_current_wind_speed_kt);
}
////////////////////////////////////////////////////////////////////////
// Implementation of FGInterpolateEnvironmentCtrl.
////////////////////////////////////////////////////////////////////////
FGInterpolateEnvironmentCtrl::FGInterpolateEnvironmentCtrl ()
{
}
FGInterpolateEnvironmentCtrl::~FGInterpolateEnvironmentCtrl ()
{
2003-10-16 12:53:10 +00:00
unsigned int i;
2003-06-09 08:44:59 +00:00
for (i = 0; i < _boundary_table.size(); i++)
delete _boundary_table[i];
2003-06-09 08:44:59 +00:00
for (i = 0; i < _aloft_table.size(); i++)
delete _aloft_table[i];
}
void
FGInterpolateEnvironmentCtrl::init ()
{
read_table(fgGetNode("/environment/config/boundary", true),
_boundary_table);
read_table(fgGetNode("/environment/config/aloft", true),
_aloft_table);
}
void
FGInterpolateEnvironmentCtrl::reinit ()
{
2003-10-16 12:53:10 +00:00
unsigned int i;
2003-06-09 08:44:59 +00:00
for (i = 0; i < _boundary_table.size(); i++)
delete _boundary_table[i];
2003-06-09 08:44:59 +00:00
for (i = 0; i < _aloft_table.size(); i++)
delete _aloft_table[i];
_boundary_table.clear();
_aloft_table.clear();
init();
}
void
FGInterpolateEnvironmentCtrl::read_table (const SGPropertyNode * node,
vector<bucket *> &table)
{
for (int i = 0; i < node->nChildren(); i++) {
const SGPropertyNode * child = node->getChild(i);
if ( strcmp(child->getName(), "entry") == 0
&& child->getStringValue("elevation-ft", "")[0] != '\0'
&& ( child->getDoubleValue("elevation-ft") > 0.1 || i == 0 ) )
{
bucket * b = new bucket;
if (i > 0)
b->environment.copy(table[i-1]->environment);
b->environment.read(child);
b->altitude_ft = b->environment.get_elevation_ft();
table.push_back(b);
}
}
sort(table.begin(), table.end());
}
void
FGInterpolateEnvironmentCtrl::update (double delta_time_sec)
{
// FIXME
double altitude_ft = fgGetDouble("/position/altitude-ft");
double altitude_agl_ft = fgGetDouble("/position/altitude-agl-ft");
double boundary_transition =
fgGetDouble("/environment/config/boundary-transition-ft", 500);
2003-10-16 12:53:10 +00:00
// double ground_elevation_ft = altitude_ft - altitude_agl_ft;
int length = _boundary_table.size();
if (length > 0) {
// boundary table
double boundary_limit = _boundary_table[length-1]->altitude_ft;
if (boundary_limit >= altitude_agl_ft) {
do_interpolate(_boundary_table, altitude_agl_ft,
_environment);
return;
} else if ((boundary_limit + boundary_transition) >= altitude_agl_ft) {
// both tables
do_interpolate(_boundary_table, altitude_agl_ft, &env1);
do_interpolate(_aloft_table, altitude_ft, &env2);
double fraction =
(altitude_agl_ft - boundary_limit) / boundary_transition;
interpolate(&env1, &env2, fraction, _environment);
return;
}
}
// aloft table
do_interpolate(_aloft_table, altitude_ft, _environment);
}
void
FGInterpolateEnvironmentCtrl::do_interpolate (vector<bucket *> &table,
double altitude_ft,
FGEnvironment * environment)
{
int length = table.size();
if (length == 0)
return;
// Boundary conditions
if ((length == 1) || (table[0]->altitude_ft >= altitude_ft)) {
environment->copy(table[0]->environment);
return;
} else if (table[length-1]->altitude_ft <= altitude_ft) {
environment->copy(table[length-1]->environment);
return;
}
// Search the interpolation table
for (int i = 0; i < length - 1; i++) {
if ((i == length - 1) || (table[i]->altitude_ft <= altitude_ft)) {
FGEnvironment * env1 = &(table[i]->environment);
FGEnvironment * env2 = &(table[i+1]->environment);
double fraction;
if (table[i]->altitude_ft == table[i+1]->altitude_ft)
fraction = 1.0;
else
fraction =
((altitude_ft - table[i]->altitude_ft) /
(table[i+1]->altitude_ft - table[i]->altitude_ft));
interpolate(env1, env2, fraction, environment);
return;
}
}
}
bool
FGInterpolateEnvironmentCtrl::bucket::operator< (const bucket &b) const
{
return (altitude_ft < b.altitude_ft);
}
////////////////////////////////////////////////////////////////////////
// Implementation of FGMetarEnvironmentCtrl.
////////////////////////////////////////////////////////////////////////
FGMetarEnvironmentCtrl::FGMetarEnvironmentCtrl ()
: env( new FGInterpolateEnvironmentCtrl ),
_icao( "" ),
search_interval_sec( 60.0 ), // 1 minute
same_station_interval_sec( 900.0 ), // 15 minutes
search_elapsed( 9999.0 ),
fetch_elapsed( 9999.0 ),
proxy_host( fgGetNode("/sim/presets/proxy/host", true) ),
proxy_port( fgGetNode("/sim/presets/proxy/port", true) ),
proxy_auth( fgGetNode("/sim/presets/proxy/authentication", true) ),
metar_max_age( fgGetNode("/environment/params/metar-max-age-min", true) ),
_error_count( 0 ),
_dt( 0.0 ),
_error_dt( 0.0 )
{
#if defined(ENABLE_THREADS) && ENABLE_THREADS
thread = new MetarThread(this);
thread->start( 1 );
#endif // ENABLE_THREADS
}
FGMetarEnvironmentCtrl::~FGMetarEnvironmentCtrl ()
{
#if defined(ENABLE_THREADS) && ENABLE_THREADS
thread->cancel();
thread->join();
#endif // ENABLE_THREADS
delete env;
env = NULL;
}
// use a "command" to set station temp at station elevation
static void set_temp_at_altitude( float temp_degc, float altitude_ft ) {
SGPropertyNode args;
SGPropertyNode *node = args.getNode("temp-degc", 0, true);
node->setFloatValue( temp_degc );
node = args.getNode("altitude-ft", 0, true);
node->setFloatValue( altitude_ft );
globals->get_commands()->execute("set-outside-air-temp-degc", &args);
}
static void set_dewpoint_at_altitude( float dewpoint_degc, float altitude_ft ) {
SGPropertyNode args;
SGPropertyNode *node = args.getNode("dewpoint-degc", 0, true);
node->setFloatValue( dewpoint_degc );
node = args.getNode("altitude-ft", 0, true);
node->setFloatValue( altitude_ft );
globals->get_commands()->execute("set-dewpoint-temp-degc", &args);
}
void
FGMetarEnvironmentCtrl::update_env_config ()
{
fgSetupWind( fgGetDouble("/environment/metar/base-wind-range-from"),
fgGetDouble("/environment/metar/base-wind-range-to"),
fgGetDouble("/environment/metar/base-wind-speed-kt"),
fgGetDouble("/environment/metar/gust-wind-speed-kt") );
fgDefaultWeatherValue( "visibility-m",
fgGetDouble("/environment/metar/min-visibility-m") );
set_temp_at_altitude( fgGetDouble("/environment/metar/temperature-degc"),
station_elevation_ft );
set_dewpoint_at_altitude( fgGetDouble("/environment/metar/dewpoint-degc"),
station_elevation_ft );
fgDefaultWeatherValue( "pressure-sea-level-inhg",
fgGetDouble("/environment/metar/pressure-inhg") );
}
void
FGMetarEnvironmentCtrl::init ()
{
const SGPropertyNode *longitude
= fgGetNode( "/position/longitude-deg", true );
const SGPropertyNode *latitude
= fgGetNode( "/position/latitude-deg", true );
bool found_metar = false;
long max_age = metar_max_age->getLongValue();
// Don't check max age during init so that we don't loop over a lot
// of airports metar if there is a problem.
// The update() calls will find a correct metar if things went wrong here
metar_max_age->setLongValue(60 * 24 * 7);
while ( !found_metar && (_error_count < 3) ) {
David Luff: Attached is a patch to the airport data storage that I would like committed after review if acceptable. Currently the storage of airports mapped by ID is by locally created objects - about 12 Meg or so created on the stack if I am not mistaken. I've changed this to creating the airports on the heap, and storing pointers to them - see FGAirportList.add(...) in src/Airports/simple.cxx. I believe that this is probably better practice, and it's certainly cured some strange problems I was seeing when accessing the airport data with some gps unit code. Changes resulting from this have cascaded through a few files which access the data - 11 files are modified in all. Melchior and Durk - you might want to test this and shout if there are problems since the metar and traffic code are probably the biggest users of the airport data. I've also added a fuzzy search function that returns the next matching airport code in ASCII sequence in order to support gps units that have autocompletion of partially entered codes. More generally, the simple airport class seems to have grown a lot with the fairly recent addition of the parking, runway preference and schedule time code. It is no longer just an encapsulation of the global airport data file, and has grown to 552 bytes in size when unpopulated (about 1/2 a K!). My personal opinion is that we should look to just store the basic data in apt.dat for all global airports in a simple airport class, plus globally needed data (metar available?), and then have the traffic, AI and ATC subsystems create more advanced airports for themselves as needed in the area of interest. Once a significant number of airports worldwide have ground networks and parking defined, it will be impractical and unnecessary to store them all in memory. That's just a thought for the future though.
2005-09-20 20:26:57 +00:00
const FGAirport* a = globals->get_airports()
->search( longitude->getDoubleValue(),
latitude->getDoubleValue(),
true );
if ( a ) {
FGMetarResult result = fetch_data( a->getId() );
if ( result.m != NULL ) {
SG_LOG( SG_GENERAL, SG_INFO, "closest station w/ metar = "
<< a->getId());
last_apt = *a;
_icao = a->getId();
search_elapsed = 0.0;
fetch_elapsed = 0.0;
update_metar_properties( result.m );
update_env_config();
env->init();
found_metar = true;
} else {
// mark as no metar so it doesn't show up in subsequent
// searches.
SG_LOG( SG_GENERAL, SG_INFO, "no metar at metar = "
<< a->getId() );
globals->get_airports()->no_metar( a->getId() );
}
}
}
metar_max_age->setLongValue(max_age);
}
void
FGMetarEnvironmentCtrl::reinit ()
{
_error_count = 0;
_error_dt = 0.0;
#if 0
update_env_config();
#endif
env->reinit();
}
void
FGMetarEnvironmentCtrl::update(double delta_time_sec)
{
_dt += delta_time_sec;
if (_error_count >= 3)
return;
FGMetarResult result;
static const SGPropertyNode *longitude
= fgGetNode( "/position/longitude-deg", true );
static const SGPropertyNode *latitude
= fgGetNode( "/position/latitude-deg", true );
search_elapsed += delta_time_sec;
fetch_elapsed += delta_time_sec;
// if time for a new search request, push it onto the request
// queue
if ( search_elapsed > search_interval_sec ) {
David Luff: Attached is a patch to the airport data storage that I would like committed after review if acceptable. Currently the storage of airports mapped by ID is by locally created objects - about 12 Meg or so created on the stack if I am not mistaken. I've changed this to creating the airports on the heap, and storing pointers to them - see FGAirportList.add(...) in src/Airports/simple.cxx. I believe that this is probably better practice, and it's certainly cured some strange problems I was seeing when accessing the airport data with some gps unit code. Changes resulting from this have cascaded through a few files which access the data - 11 files are modified in all. Melchior and Durk - you might want to test this and shout if there are problems since the metar and traffic code are probably the biggest users of the airport data. I've also added a fuzzy search function that returns the next matching airport code in ASCII sequence in order to support gps units that have autocompletion of partially entered codes. More generally, the simple airport class seems to have grown a lot with the fairly recent addition of the parking, runway preference and schedule time code. It is no longer just an encapsulation of the global airport data file, and has grown to 552 bytes in size when unpopulated (about 1/2 a K!). My personal opinion is that we should look to just store the basic data in apt.dat for all global airports in a simple airport class, plus globally needed data (metar available?), and then have the traffic, AI and ATC subsystems create more advanced airports for themselves as needed in the area of interest. Once a significant number of airports worldwide have ground networks and parking defined, it will be impractical and unnecessary to store them all in memory. That's just a thought for the future though.
2005-09-20 20:26:57 +00:00
const FGAirport* a = globals->get_airports()
->search( longitude->getDoubleValue(),
latitude->getDoubleValue(),
true );
if ( a ) {
if ( last_apt.getId() != a->getId()
|| fetch_elapsed > same_station_interval_sec )
{
SG_LOG( SG_GENERAL, SG_INFO, "closest station w/ metar = "
<< a->getId());
request_queue.push( a->getId() );
last_apt = *a;
_icao = a->getId();
search_elapsed = 0.0;
fetch_elapsed = 0.0;
} else {
search_elapsed = 0.0;
SG_LOG( SG_GENERAL, SG_INFO, "same station, waiting = "
<< same_station_interval_sec - fetch_elapsed );
}
} else {
David Luff: Attached is a patch to the airport data storage that I would like committed after review if acceptable. Currently the storage of airports mapped by ID is by locally created objects - about 12 Meg or so created on the stack if I am not mistaken. I've changed this to creating the airports on the heap, and storing pointers to them - see FGAirportList.add(...) in src/Airports/simple.cxx. I believe that this is probably better practice, and it's certainly cured some strange problems I was seeing when accessing the airport data with some gps unit code. Changes resulting from this have cascaded through a few files which access the data - 11 files are modified in all. Melchior and Durk - you might want to test this and shout if there are problems since the metar and traffic code are probably the biggest users of the airport data. I've also added a fuzzy search function that returns the next matching airport code in ASCII sequence in order to support gps units that have autocompletion of partially entered codes. More generally, the simple airport class seems to have grown a lot with the fairly recent addition of the parking, runway preference and schedule time code. It is no longer just an encapsulation of the global airport data file, and has grown to 552 bytes in size when unpopulated (about 1/2 a K!). My personal opinion is that we should look to just store the basic data in apt.dat for all global airports in a simple airport class, plus globally needed data (metar available?), and then have the traffic, AI and ATC subsystems create more advanced airports for themselves as needed in the area of interest. Once a significant number of airports worldwide have ground networks and parking defined, it will be impractical and unnecessary to store them all in memory. That's just a thought for the future though.
2005-09-20 20:26:57 +00:00
SG_LOG( SG_GENERAL, SG_WARN,
"Unable to find any airports with metar" );
}
}
#if defined(ENABLE_THREADS) && ENABLE_THREADS
// No loader thread running so manually fetch the data
string id = "";
while ( !request_queue.empty() ) {
id = request_queue.front();
request_queue.pop();
}
if ( !id.empty() ) {
SG_LOG( SG_GENERAL, SG_INFO, "inline fetching = " << id );
result = fetch_data( id );
result_queue.push( result );
}
#endif // ENABLE_THREADS
// process any results from the loader.
while ( !result_queue.empty() ) {
result = result_queue.front();
result_queue.pop();
if ( result.m != NULL ) {
update_metar_properties( result.m );
delete result.m;
update_env_config();
env->reinit();
} else {
// mark as no metar so it doesn't show up in subsequent
// searches, and signal an immediate re-search.
SG_LOG( SG_GENERAL, SG_WARN,
"no metar at station = " << result.icao );
globals->get_airports()->no_metar( result.icao );
search_elapsed = 9999.0;
}
}
env->update(delta_time_sec);
}
void
FGMetarEnvironmentCtrl::setEnvironment (FGEnvironment * environment)
{
env->setEnvironment(environment);
}
FGMetarResult
FGMetarEnvironmentCtrl::fetch_data( const string &icao )
{
FGMetarResult result;
result.icao = icao;
// if the last error was more than three seconds ago,
// then pretent nothing happened.
if (_error_dt < 3) {
_error_dt += _dt;
} else {
_error_dt = 0.0;
_error_count = 0;
}
// fetch station elevation if exists
David Luff: Attached is a patch to the airport data storage that I would like committed after review if acceptable. Currently the storage of airports mapped by ID is by locally created objects - about 12 Meg or so created on the stack if I am not mistaken. I've changed this to creating the airports on the heap, and storing pointers to them - see FGAirportList.add(...) in src/Airports/simple.cxx. I believe that this is probably better practice, and it's certainly cured some strange problems I was seeing when accessing the airport data with some gps unit code. Changes resulting from this have cascaded through a few files which access the data - 11 files are modified in all. Melchior and Durk - you might want to test this and shout if there are problems since the metar and traffic code are probably the biggest users of the airport data. I've also added a fuzzy search function that returns the next matching airport code in ASCII sequence in order to support gps units that have autocompletion of partially entered codes. More generally, the simple airport class seems to have grown a lot with the fairly recent addition of the parking, runway preference and schedule time code. It is no longer just an encapsulation of the global airport data file, and has grown to 552 bytes in size when unpopulated (about 1/2 a K!). My personal opinion is that we should look to just store the basic data in apt.dat for all global airports in a simple airport class, plus globally needed data (metar available?), and then have the traffic, AI and ATC subsystems create more advanced airports for themselves as needed in the area of interest. Once a significant number of airports worldwide have ground networks and parking defined, it will be impractical and unnecessary to store them all in memory. That's just a thought for the future though.
2005-09-20 20:26:57 +00:00
const FGAirport* a = globals->get_airports()->search( icao );
if ( a ) {
station_elevation_ft = a->getElevation();
}
// fetch current metar data
try {
string host = proxy_host->getStringValue();
string auth = proxy_auth->getStringValue();
string port = proxy_port->getStringValue();
result.m = new FGMetar( icao, host, port, auth);
long max_age = metar_max_age->getLongValue();
if (max_age && result.m->getAge_min() > max_age) {
SG_LOG( SG_GENERAL, SG_WARN, "METAR data too old");
delete result.m;
result.m = NULL;
}
} catch (const sg_io_exception& e) {
SG_LOG( SG_GENERAL, SG_WARN, "Error fetching live weather data: "
<< e.getFormattedMessage().c_str() );
#if defined(ENABLE_THREADS) && ENABLE_THREADS
if (_error_count++ >= 3) {
SG_LOG( SG_GENERAL, SG_WARN, "Stop fetching data permanently.");
thread->cancel();
thread->join();
}
#endif
result.m = NULL;
}
_dt = 0;
return result;
}
void
FGMetarEnvironmentCtrl::update_metar_properties( const FGMetar *m )
{
int i;
double d;
char s[128];
fgSetString("/environment/metar/real-metar", m->getData());
// don't update with real weather when we use a custom weather scenario
const char *current_scenario = fgGetString("/environment/weather-scenario", "METAR");
if( strcmp(current_scenario, "METAR") && strcmp(current_scenario, "none"))
return;
fgSetString("/environment/metar/last-metar", m->getData());
fgSetString("/environment/metar/station-id", m->getId());
fgSetDouble("/environment/metar/min-visibility-m",
m->getMinVisibility().getVisibility_m() );
fgSetDouble("/environment/metar/max-visibility-m",
m->getMaxVisibility().getVisibility_m() );
const SGMetarVisibility *dirvis = m->getDirVisibility();
for (i = 0; i < 8; i++, dirvis++) {
const char *min = "/environment/metar/visibility[%d]/min-m";
const char *max = "/environment/metar/visibility[%d]/max-m";
d = dirvis->getVisibility_m();
snprintf(s, 128, min, i);
fgSetDouble(s, d);
snprintf(s, 128, max, i);
fgSetDouble(s, d);
}
fgSetInt("/environment/metar/base-wind-range-from",
m->getWindRangeFrom() );
fgSetInt("/environment/metar/base-wind-range-to",
m->getWindRangeTo() );
fgSetDouble("/environment/metar/base-wind-speed-kt",
m->getWindSpeed_kt() );
fgSetDouble("/environment/metar/gust-wind-speed-kt",
m->getGustSpeed_kt() );
fgSetDouble("/environment/metar/temperature-degc",
m->getTemperature_C() );
fgSetDouble("/environment/metar/dewpoint-degc",
m->getDewpoint_C() );
fgSetDouble("/environment/metar/rel-humidity-norm",
m->getRelHumidity() );
fgSetDouble("/environment/metar/pressure-inhg",
m->getPressure_inHg() );
vector<SGMetarCloud> cv = m->getClouds();
vector<SGMetarCloud>::const_iterator cloud;
const char *cl = "/environment/clouds/layer[%i]";
for (i = 0, cloud = cv.begin(); cloud != cv.end(); cloud++, i++) {
const char *coverage_string[5] =
{ "clear", "few", "scattered", "broken", "overcast" };
const double thickness[5] = { 0, 65, 600,750, 1000};
int q;
snprintf(s, 128, cl, i);
strncat(s, "/coverage", 128);
q = cloud->getCoverage();
fgSetString(s, coverage_string[q] );
snprintf(s, 128, cl, i);
strncat(s, "/elevation-ft", 128);
fgSetDouble(s, cloud->getAltitude_ft() + station_elevation_ft);
snprintf(s, 128, cl, i);
strncat(s, "/thickness-ft", 128);
fgSetDouble(s, thickness[q]);
snprintf(s, 128, cl, i);
strncat(s, "/span-m", 128);
fgSetDouble(s, 40000.0);
}
for (; i < FGEnvironmentMgr::MAX_CLOUD_LAYERS; i++) {
snprintf(s, 128, cl, i);
strncat(s, "/coverage", 128);
fgSetString(s, "clear");
snprintf(s, 128, cl, i);
strncat(s, "/elevation-ft", 128);
fgSetDouble(s, -9999);
snprintf(s, 128, cl, i);
strncat(s, "/thickness-ft", 128);
fgSetDouble(s, 0);
snprintf(s, 128, cl, i);
strncat(s, "/span-m", 128);
fgSetDouble(s, 40000.0);
}
fgSetDouble("/environment/metar/rain-norm", m->getRain());
fgSetDouble("/environment/metar/hail-norm", m->getHail());
fgSetDouble("/environment/metar/snow-norm", m->getSnow());
fgSetBool("/environment/metar/snow-cover", m->getSnowCover());
}
#if defined(ENABLE_THREADS) && ENABLE_THREADS
/**
*
*/
void
FGMetarEnvironmentCtrl::MetarThread::run()
{
pthread_cleanup_push( metar_cleanup_handler, fetcher );
while ( true )
{
set_cancel( SGThread::CANCEL_DISABLE );
string icao = fetcher->request_queue.pop();
SG_LOG( SG_GENERAL, SG_INFO, "Thread: fetch metar data = " << icao );
FGMetarResult result = fetcher->fetch_data( icao );
set_cancel( SGThread::CANCEL_DEFERRED );
fetcher->result_queue.push( result );
}
pthread_cleanup_pop(1);
}
/**
* Ensure mutex is unlocked.
*/
void
metar_cleanup_handler( void* arg )
{
FGMetarEnvironmentCtrl* fetcher = (FGMetarEnvironmentCtrl*) arg;
fetcher->mutex.unlock();
}
#endif // ENABLE_THREADS
// end of environment_ctrl.cxx