X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FEnvironment%2Fenvironment_ctrl.cxx;h=cf9c6505faefd68dff5f2cdfabb23f86666f19d2;hb=c6f88e5b9be767d93fd34f3b01c0e949fbd0044b;hp=46278edc28699e74cc468a7a7430a8cfaa356c5a;hpb=bca8c29795d02e317d56d25a9adc527faa835f3f;p=flightgear.git diff --git a/src/Environment/environment_ctrl.cxx b/src/Environment/environment_ctrl.cxx index 46278edc2..cf9c6505f 100644 --- a/src/Environment/environment_ctrl.cxx +++ b/src/Environment/environment_ctrl.cxx @@ -16,15 +16,17 @@ // // 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. +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // // $Id$ -#include +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif -#include #include +#include #include #include @@ -32,12 +34,20 @@ #include
#include
+#include "Environment/fgmetar.hxx" #include "environment_mgr.hxx" #include "environment_ctrl.hxx" -SG_USING_STD(sort); - +using std::sort; +class AirportWithMetar : public FGAirport::AirportFilter +{ +public: + virtual bool passAirport(FGAirport* aApt) const + { + return aApt->getMetar(); + } +}; //////////////////////////////////////////////////////////////////////// // Implementation of FGEnvironmentCtrl abstract base class. @@ -227,7 +237,7 @@ FGInterpolateEnvironmentCtrl::read_table (const SGPropertyNode * node, table.push_back(b); } } - sort(table.begin(), table.end()); + sort(table.begin(), table.end(), bucket::lessThan); } void @@ -308,6 +318,11 @@ FGInterpolateEnvironmentCtrl::bucket::operator< (const bucket &b) const return (altitude_ft < b.altitude_ft); } +bool +FGInterpolateEnvironmentCtrl::bucket::lessThan(bucket *a, bucket *b) +{ + return (a->altitude_ft) < (b->altitude_ft); +} //////////////////////////////////////////////////////////////////////// @@ -316,21 +331,43 @@ FGInterpolateEnvironmentCtrl::bucket::operator< (const bucket &b) const FGMetarEnvironmentCtrl::FGMetarEnvironmentCtrl () : env( new FGInterpolateEnvironmentCtrl ), - _icao( fgGetString("/sim/presets/airport-id") ), - update_interval_sec( 60.0 ), - elapsed( 60.0 ) + metar_loaded( false ), + search_interval_sec( 60.0 ), // 1 minute + same_station_interval_sec( 900.0 ), // 15 minutes + search_elapsed( 9999.0 ), + fetch_elapsed( 9999.0 ), + last_apt( 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) ), + + // Interpolation constant definitions. + EnvironmentUpdatePeriodSec( 0.2 ), + MaxWindChangeKtsSec( 0.2 ), + MaxVisChangePercentSec( 0.05 ), + MaxPressureChangeInHgSec( 0.0033 ), + MaxCloudAltitudeChangeFtSec( 20.0 ), + MaxCloudThicknessChangeFtSec( 50.0 ), + MaxCloudInterpolationHeightFt( 5000.0 ), + MaxCloudInterpolationDeltaFt( 4000.0 ), + + _error_count( 0 ), + _stale_count( 0 ), + _dt( 0.0 ), + _error_dt( 0.0 ) { -#ifdef ENABLE_THREADS +#if defined(ENABLE_THREADS) thread = new MetarThread(this); + thread->setProcessorAffinity(1); thread->start(); #endif // ENABLE_THREADS } FGMetarEnvironmentCtrl::~FGMetarEnvironmentCtrl () { -#ifdef ENABLE_THREADS - thread->cancel(); - thread->join(); +#if defined(ENABLE_THREADS) + thread_stop(); #endif // ENABLE_THREADS delete env; @@ -362,57 +399,305 @@ static void set_dewpoint_at_altitude( float dewpoint_degc, float altitude_ft ) { 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") ); + double dir_from; + double dir_to; + double speed; + double gust; + double vis; + double pressure; + double temp; + double dewpoint; + + // If we aren't in the METAR scenario, don't attempt to interpolate. + if (strcmp(fgGetString("/environment/weather-scenario", "METAR"), "METAR")) return; + + if (metar_loaded) { + // Generate interpolated values between the METAR and the current + // configuration. + + // Pick up the METAR wind values and convert them into a vector. + double metar[2]; + double metar_speed = fgGetDouble("/environment/metar/base-wind-speed-kt"); + double metar_heading = fgGetDouble("/environment/metar/base-wind-range-from"); + + metar[0] = metar_speed * sin((metar_heading / 180.0) * M_PI); + metar[1] = metar_speed * cos((metar_heading / 180.0) * M_PI); + + // Convert the current wind values and convert them into a vector + double current[2]; + double current_speed = + fgGetDouble("/environment/config/boundary/entry/wind-speed-kt"); + double current_heading = fgGetDouble( + "/environment/config/boundary/entry/wind-from-heading-deg"); + + current[0] = current_speed * sin((current_heading / 180.0) * M_PI); + current[1] = current_speed * cos((current_heading / 180.0) * M_PI); + + // Determine the maximum component-wise value that the wind can change. + // First we determine the fraction in the X and Y component, then + // factor by the maximum wind change. + double x = fabs(current[0] - metar[0]); + double y = fabs(current[1] - metar[1]); + + // only interpolate if we have a difference + if (x + y > 0) { + double dx = x / (x + y); + double dy = 1 - dx; + + double maxdx = dx * MaxWindChangeKtsSec; + double maxdy = dy * MaxWindChangeKtsSec; + + // Interpolate each component separately. + current[0] = interpolate_val(current[0], metar[0], maxdx); + current[1] = interpolate_val(current[1], metar[1], maxdy); + } + + // Now convert back to polar coordinates. + if ((current[0] == 0.0) && (current[1] == 0.0)) { + // Special case where there is no wind (otherwise atan2 barfs) + speed = 0.0; + dir_from = current_heading; + + } else { + // Some real wind to convert back from. Work out the speed + // and direction value in degrees. + speed = sqrt((current[0] * current[0]) + (current[1] * current[1])); + dir_from = (atan2(current[0], current[1]) * 180.0 / M_PI); + + // Normalize the direction. + if (dir_from < 0.0) + dir_from += 360.0; + + SG_LOG( SG_GENERAL, SG_DEBUG, "Wind : " << dir_from << "@" << speed); + } + + // Now handle the visibility. We convert both visibility values + // to X-values, then interpolate from there, then back to real values. + // The length_scale is fixed to 1000m, so the visibility changes by + // by MaxVisChangePercentSec or 1000m X MaxVisChangePercentSec, + // whichever is more. + double currentvis = + fgGetDouble("/environment/config/boundary/entry/visibility-m"); + double metarvis = fgGetDouble("/environment/metar/min-visibility-m"); + double currentxval = log(1000.0 + currentvis); + double metarxval = log(1000.0 + metarvis); + + currentxval = interpolate_val(currentxval, metarxval, MaxVisChangePercentSec); + + // Now convert back from an X-value to a straightforward visibility. + vis = exp(currentxval) - 1000.0; + + pressure = interpolate_prop( + "/environment/config/boundary/entry/pressure-sea-level-inhg", + "/environment/metar/pressure-inhg", + MaxPressureChangeInHgSec); + + dir_to = fgGetDouble("/environment/metar/base-wind-range-to"); + gust = fgGetDouble("/environment/metar/gust-wind-speed-kt"); + temp = fgGetDouble("/environment/metar/temperature-degc"); + dewpoint = fgGetDouble("/environment/metar/dewpoint-degc"); + + // Set the cloud layers by interpolating over the METAR versions. + SGPropertyNode * clouds = fgGetNode("/environment/metar/clouds"); + + vector layers = clouds->getChildren("layer"); + vector::const_iterator layer; + vector::const_iterator layers_end = layers.end(); + + const char *cl = "/environment/clouds/layer[%i]"; + double aircraft_alt = fgGetDouble("/position/altitude-ft"); + char s[128]; + int i; + + for (i = 0, layer = layers.begin(); layer != layers_end; ++layer, i++) { + double currentval; + double requiredval; + + // In the case of clouds, we want to avoid writing if nothing has + // changed, as these properties are tied to the renderer and will + // cause the clouds to be updated, reseting the texture locations. + + // We don't interpolate the coverage values as no-matter how we + // do it, it will be quite a sudden change of texture. Better to + // have a single change than four or five. + snprintf(s, 128, cl, i); + strncat(s, "/coverage", 128); + const char* coverage = (*layer)->getStringValue("coverage", "clear"); + if (strncmp(fgGetString(s), coverage, 128) != 0) + fgSetString(s, coverage); + + snprintf(s, 128, cl, i); + strncat(s, "/elevation-ft", 128); + double current_alt = fgGetDouble(s); + double required_alt = (*layer)->getDoubleValue("elevation-ft"); + + if (current_alt < -9000 || required_alt < -9000 || + fabs(aircraft_alt - required_alt) > MaxCloudInterpolationHeightFt || + fabs(current_alt - required_alt) > MaxCloudInterpolationDeltaFt) { + // We don't interpolate any layers that are + // - too far above us to be visible + // - too far below us to be visible + // - with too large a difference to make interpolation sensible + // - to or from -9999 (used as a placeholder) + // - any values that are too high above us, + snprintf(s, 128, cl, i); + strncat(s, "/elevation-ft", 128); + if (current_alt != required_alt) + fgSetDouble(s, required_alt); + + snprintf(s, 128, cl, i); + strncat(s, "/thickness-ft", 128); + if (fgGetDouble(s) != (*layer)->getDoubleValue("thickness-ft")) + fgSetDouble(s, (*layer)->getDoubleValue("thickness-ft")); + + } else { + // Interpolate the other values in the usual way + if (current_alt != required_alt) { + current_alt = interpolate_val(current_alt, + required_alt, + MaxCloudAltitudeChangeFtSec); + fgSetDouble(s, current_alt); + } + + snprintf(s, 128, cl, i); + strncat(s, "/thickness-ft", 128); + currentval = fgGetDouble(s); + requiredval = (*layer)->getDoubleValue("thickness-ft"); + + if (currentval != requiredval) { + currentval = interpolate_val(currentval, + requiredval, + MaxCloudThicknessChangeFtSec); + fgSetDouble(s, currentval); + } + } + } + + } else { + // We haven't already loaded a METAR, so apply it immediately. + dir_from = fgGetDouble("/environment/metar/base-wind-range-from"); + dir_to = fgGetDouble("/environment/metar/base-wind-range-to"); + speed = fgGetDouble("/environment/metar/base-wind-speed-kt"); + gust = fgGetDouble("/environment/metar/gust-wind-speed-kt"); + vis = fgGetDouble("/environment/metar/min-visibility-m"); + pressure = fgGetDouble("/environment/metar/pressure-inhg"); + temp = fgGetDouble("/environment/metar/temperature-degc"); + dewpoint = fgGetDouble("/environment/metar/dewpoint-degc"); + + // Set the cloud layers by copying over the METAR versions. + SGPropertyNode * clouds = fgGetNode("/environment/metar/clouds", true); + + vector layers = clouds->getChildren("layer"); + vector::const_iterator layer; + vector::const_iterator layers_end = layers.end(); + + const char *cl = "/environment/clouds/layer[%i]"; + char s[128]; + int i; + + for (i = 0, layer = layers.begin(); layer != layers_end; ++layer, i++) { + snprintf(s, 128, cl, i); + strncat(s, "/coverage", 128); + fgSetString(s, (*layer)->getStringValue("coverage", "clear")); + + snprintf(s, 128, cl, i); + strncat(s, "/elevation-ft", 128); + fgSetDouble(s, (*layer)->getDoubleValue("elevation-ft")); + + snprintf(s, 128, cl, i); + strncat(s, "/thickness-ft", 128); + fgSetDouble(s, (*layer)->getDoubleValue("thickness-ft")); - 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") ); + snprintf(s, 128, cl, i); + strncat(s, "/span-m", 128); + fgSetDouble(s, 40000.0); + } + + // Force an update of the 3D clouds + fgSetDouble("/environment/rebuild-layers", 1.0); + } + + fgSetupWind(dir_from, dir_to, speed, gust); + fgDefaultWeatherValue("visibility-m", vis); + set_temp_at_altitude(temp, station_elevation_ft); + set_dewpoint_at_altitude(dewpoint, station_elevation_ft); + fgDefaultWeatherValue("pressure-sea-level-inhg", pressure); + + // We've now successfully loaded a METAR into the configuration + metar_loaded = true; +} + +double FGMetarEnvironmentCtrl::interpolate_prop(const char * currentname, + const char * requiredname, + double dt) +{ + double currentval = fgGetDouble(currentname); + double requiredval = fgGetDouble(requiredname); + return interpolate_val(currentval, requiredval, dt); +} + +double FGMetarEnvironmentCtrl::interpolate_val(double currentval, + double requiredval, + double dt) +{ + double dval = EnvironmentUpdatePeriodSec * dt; + + if (fabs(currentval - requiredval) < dval) return requiredval; + if (currentval < requiredval) return (currentval + dval); + if (currentval > requiredval) return (currentval - dval); + return requiredval; } void FGMetarEnvironmentCtrl::init () { - const SGPropertyNode *longitude - = fgGetNode( "/position/longitude-deg", true ); - const SGPropertyNode *latitude - = fgGetNode( "/position/latitude-deg", true ); + SGGeod pos = SGGeod::fromDeg( + fgGetDouble("/position/longitude-deg", true), + fgGetDouble( "/position/latitude-deg", true)); + metar_loaded = false; bool found_metar = false; - while ( !found_metar ) { - FGAirport a = globals->get_airports() - ->search( longitude->getDoubleValue(), - latitude->getDoubleValue(), - true ); - if ( fetch_data( a.id ) ) { - cout << "closest station w/ metar = " << a.id << endl; - _icao = a.id; - elapsed = 0.0; + 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(0); + + while ( !found_metar && (_error_count < 3) ) { + AirportWithMetar filter; + FGAirport* a = FGAirport::findClosest(pos, 10000.0, &filter); + if (!a) { + break; + } + + FGMetarResult result = fetch_data(a); + if ( result.m != NULL ) { + SG_LOG( SG_GENERAL, SG_INFO, "closest station w/ metar = " + << a->ident()); + last_apt = a; + 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. - cout << "no metar at metar = " << a.id << endl; - globals->get_airports()->no_metar( a.id ); + SG_LOG( SG_GENERAL, SG_INFO, "no metar at metar = " << a->ident() ); + a->setMetar(false); } - } + } // of airprot-with-metar search iteration + + metar_max_age->setLongValue(max_age); } void FGMetarEnvironmentCtrl::reinit () { -#if 0 - update_env_config(); -#endif + _error_count = 0; + _error_dt = 0.0; + metar_loaded = false; env->reinit(); } @@ -420,103 +705,183 @@ FGMetarEnvironmentCtrl::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 ); - elapsed += delta_time_sec; - if ( elapsed > update_interval_sec ) { - FGAirport a = globals->get_airports() - ->search( longitude->getDoubleValue(), - latitude->getDoubleValue(), - true ); - if ( fetch_data( a.id ) ) { - cout << "closest station w/ metar = " << a.id << endl; - _icao = a.id; - elapsed = 0.0; + SGGeod pos = SGGeod::fromDeg(longitude->getDoubleValue(), + latitude->getDoubleValue()); + + search_elapsed += delta_time_sec; + fetch_elapsed += delta_time_sec; + interpolate_elapsed += delta_time_sec; + + // if time for a new search request, push it onto the request + // queue + if ( search_elapsed > search_interval_sec ) { + AirportWithMetar filter; + FGAirport* a = FGAirport::findClosest(pos, 10000.0, &filter); + if (a) { + if ( !last_apt || last_apt->ident() != a->ident() + || fetch_elapsed > same_station_interval_sec ) + { + SG_LOG( SG_GENERAL, SG_INFO, "closest station w/ metar = " + << a->ident()); + request_queue.push(a); + last_apt = a; + 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 { + SG_LOG( SG_GENERAL, SG_WARN, + "Unable to find any airports with metar" ); + } + } else if ( interpolate_elapsed > EnvironmentUpdatePeriodSec ) { + // Interpolate the current configuration closer to the actual METAR + update_env_config(); + env->reinit(); + interpolate_elapsed = 0.0; + } + +#if !defined(ENABLE_THREADS) + // No loader thread running so manually fetch the data + FGAirport* apt = NULL; + while ( !request_queue.empty() ) { + apt = request_queue.front(); + request_queue.pop(); + } + + if (apt) { + SG_LOG( SG_GENERAL, SG_INFO, "inline fetching = " << apt->ident() ); + result = fetch_data( apt ); + 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. - cout << "no metar at metar = " << a.id << endl; - globals->get_airports()->no_metar( a.id ); + // searches, and signal an immediate re-search. + SG_LOG( SG_GENERAL, SG_WARN, + "no metar at station = " << result.airport->ident() ); + result.airport->setMetar(false); + search_elapsed = 9999.0; } } env->update(delta_time_sec); } + void FGMetarEnvironmentCtrl::setEnvironment (FGEnvironment * environment) { env->setEnvironment(environment); } -bool -FGMetarEnvironmentCtrl::fetch_data (const string &icao) +FGMetarResult +FGMetarEnvironmentCtrl::fetch_data(FGAirport* apt) { - char s[128]; - double d, dt; - int i; + FGMetarResult result; + result.airport = apt; - if ((icao == "") && (_icao == "")) { - _icao = fgGetString("/sim/presets/airport-id"); + // if the last error was more than three seconds ago, + // then pretent nothing happened. + if (_error_dt < 3) { + _error_dt += _dt; - } else if (icao != "") { - _icao = icao; + } else { + _error_dt = 0.0; + _error_count = 0; } - // fetch station elevation if exists - FGAirport a = globals->get_airports()->search( _icao ); - station_elevation_ft = a.elevation; + station_elevation_ft = apt->getElevation(); // fetch current metar data - SGMetar *m = NULL; -#ifdef ENABLE_THREADS + try { + string host = proxy_host->getStringValue(); + string auth = proxy_auth->getStringValue(); + string port = proxy_port->getStringValue(); + result.m = new FGMetar( apt->ident(), host, port, auth); + + long max_age = metar_max_age->getLongValue(); + long age = result.m->getAge_min(); + if (max_age && age > max_age) { + SG_LOG( SG_GENERAL, SG_WARN, "METAR data too old (" << age << " min)."); + delete result.m; + result.m = NULL; + + if (++_stale_count > 10) { + _error_count = 1000; + throw sg_io_exception("More than 10 stale METAR messages in a row." + " Check your system time!"); + } + } else + _stale_count = 0; - bool valid_data = false; - if (!metar_queue.empty()) - { - m = metar_queue.pop(); + } 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) + if (_error_count++ >= 3) { + SG_LOG( SG_GENERAL, SG_WARN, "Stop fetching data permanently."); + thread_stop(); + } +#endif - if (m != NULL) - valid_data = true; + result.m = NULL; } - if ( valid_data == false ) { - mutex.lock(); - metar_cond.signal(); - mutex.unlock(); + _dt = 0; - return false; - } -#else - try { - m = new SGMetar( _icao.c_str() ); - } catch (const sg_io_exception& e) { - SG_LOG( SG_GENERAL, SG_WARN, "Error fetching live weather data: " - << e.getFormattedMessage().c_str() ); - return false; - } -#endif // ENABLE_THREADS + return result; +} - d = m->getMinVisibility().getVisibility_m(); - d = (d != SGMetarNaN) ? d : 10000; - fgSetDouble("/environment/metar/min-visibility-m", d); - dt = m->getMaxVisibility().getVisibility_m(); - d = (dt != SGMetarNaN) ? dt : d; - fgSetDouble("/environment/metar/max-visibility-m", d); +void +FGMetarEnvironmentCtrl::update_metar_properties( const FGMetar *m ) +{ + int i; + double d; + char s[128]; - SGMetarVisibility *dirvis = m->getDirVisibility(); + 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"; - char s[128]; d = dirvis->getVisibility_m(); - d = (d != SGMetarNaN) ? d : 10000; snprintf(s, 128, min, i); fgSetDouble(s, d); @@ -524,42 +889,27 @@ FGMetarEnvironmentCtrl::fetch_data (const string &icao) fgSetDouble(s, d); } - i = m->getWindDir(); - if ( i == -1 ) { - fgSetInt("/environment/metar/base-wind-range-from", - m->getWindRangeFrom() ); - fgSetInt("/environment/metar/base-wind-range-to", - m->getWindRangeTo() ); - } else { - fgSetInt("/environment/metar/base-wind-range-from", i); - fgSetInt("/environment/metar/base-wind-range-to", i); - } + 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() ); - - d = m->getGustSpeed_kt(); - d = (d != SGMetarNaN) ? d : 0.0; - fgSetDouble("/environment/metar/gust-wind-speed-kt", d); - - d = m->getTemperature_C(); - if (d != SGMetarNaN) { - dt = m->getDewpoint_C(); - dt = (dt != SGMetarNaN) ? dt : 0.0; - fgSetDouble("/environment/metar/dewpoint-degc", dt); - fgSetDouble("/environment/metar/rel-humidity-norm", - m->getRelHumidity() ); - } - d = (d != SGMetarNaN) ? d : 15.0; - fgSetDouble("/environment/metar/temperature-degc", d); - - d = m->getPressure_inHg(); - d = (d != SGMetarNaN) ? d : 30.0; - fgSetDouble("/environment/metar/pressure-inhg", d); + 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 cv = m->getClouds(); - vector::iterator cloud; + vector::const_iterator cloud; - const char *cl = "/environment/clouds/layer[%i]"; + const char *cl = "/environment/metar/clouds/layer[%i]"; for (i = 0, cloud = cv.begin(); cloud != cv.end(); cloud++, i++) { const char *coverage_string[5] = { "clear", "few", "scattered", "broken", "overcast" }; @@ -569,14 +919,11 @@ FGMetarEnvironmentCtrl::fetch_data (const string &icao) snprintf(s, 128, cl, i); strncat(s, "/coverage", 128); q = cloud->getCoverage(); - q = (q != -1 ) ? q : 0; fgSetString(s, coverage_string[q] ); snprintf(s, 128, cl, i); strncat(s, "/elevation-ft", 128); - d = cloud->getAltitude_ft(); - d = (d != SGMetarNaN) ? d : -9999; - fgSetDouble(s, d + station_elevation_ft); + fgSetDouble(s, cloud->getAltitude_ft() + station_elevation_ft); snprintf(s, 128, cl, i); strncat(s, "/thickness-ft", 128); @@ -586,6 +933,7 @@ FGMetarEnvironmentCtrl::fetch_data (const string &icao) 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); @@ -604,61 +952,33 @@ FGMetarEnvironmentCtrl::fetch_data (const string &icao) fgSetDouble(s, 40000.0); } - delete m; + 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()); +} -#ifdef ENABLE_THREADS - mutex.lock(); - metar_cond.signal(); - mutex.unlock(); -#endif // ENABLE_THREADS - return true; +#if defined(ENABLE_THREADS) +void +FGMetarEnvironmentCtrl::thread_stop() +{ + request_queue.push(NULL); // ask thread to terminate + thread->join(); } -#ifdef ENABLE_THREADS -/** - * - */ void FGMetarEnvironmentCtrl::MetarThread::run() { - SGMetar *m = NULL; - - // pthread_cleanup_push( metar_cleanup_handler, fetcher ); while ( true ) { - set_cancel( SGThread::CANCEL_DISABLE ); - try - { - cout << "Fetching ..." << endl; - // if (m != NULL) m = NULL; - m = new SGMetar( fetcher->_icao.c_str() ); - - } catch (const sg_io_exception& e) { - // SG_LOG( SG_GENERAL, SG_WARN, "Error fetching live weather data: " - // << e.getFormattedMessage().c_str() ); - m = NULL; - } - set_cancel( SGThread::CANCEL_DEFERRED ); - - fetcher->metar_queue.push( m ); - - // Wait for the next frame signal before we fetch the next metar data - fetcher->mutex.lock(); - fetcher->metar_cond.wait( fetcher->mutex ); - fetcher->mutex.unlock(); + FGAirport* apt = fetcher->request_queue.pop(); + if (!apt) + return; + SG_LOG( SG_GENERAL, SG_INFO, "Thread: fetch metar data = " << apt->ident() ); + FGMetarResult result = fetcher->fetch_data( apt ); + 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