# include "config.h"
#endif
-#include <stdlib.h>
-#include <math.h>
#include <algorithm>
#include <simgear/debug/logstream.hxx>
#include <Main/fg_props.hxx>
#include <Main/util.hxx>
+#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();
+ }
+};
\f
////////////////////////////////////////////////////////////////////////
// Implementation of FGEnvironmentCtrl abstract base class.
FGMetarEnvironmentCtrl::FGMetarEnvironmentCtrl ()
: env( new FGInterpolateEnvironmentCtrl ),
- _icao( "" ),
metar_loaded( false ),
search_interval_sec( 60.0 ), // 1 minute
same_station_interval_sec( 900.0 ), // 15 minutes
MaxCloudAltitudeChangeFtSec( 20.0 ),
MaxCloudThicknessChangeFtSec( 50.0 ),
MaxCloudInterpolationHeightFt( 5000.0 ),
+ MaxCloudInterpolationDeltaFt( 4000.0 ),
_error_count( 0 ),
_stale_count( 0 ),
{
#if defined(ENABLE_THREADS)
thread = new MetarThread(this);
- thread->start( 1 );
+ thread->setProcessorAffinity(1);
+ thread->start();
#endif // ENABLE_THREADS
}
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
// factor by the maximum wind change.
double x = fabs(current[0] - metar[0]);
double y = fabs(current[1] - metar[1]);
- double dx = x / (x + y);
- double dy = 1 - dx;
- double maxdx = dx * MaxWindChangeKtsSec;
- double maxdy = dy * MaxWindChangeKtsSec;
+ // 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);
+ // 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)) {
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) {
- // We don't interpolate any values that are too high above us,
- // or too far below us to be visible. Nor do we interpolate
- // values to or from -9999, which is used as a placeholder
- // when there isn't actually a cloud layer present.
+ 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)
dewpoint = fgGetDouble("/environment/metar/dewpoint-degc");
// Set the cloud layers by copying over the METAR versions.
- SGPropertyNode * clouds = fgGetNode("/environment/metar/clouds");
+ SGPropertyNode * clouds = fgGetNode("/environment/metar/clouds", true);
vector<SGPropertyNode_ptr> layers = clouds->getChildren("layer");
vector<SGPropertyNode_ptr>::const_iterator layer;
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);
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;
metar_max_age->setLongValue(0);
while ( !found_metar && (_error_count < 3) ) {
- 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() );
- }
+ 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.
+ 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);
}
{
_error_count = 0;
_error_dt = 0.0;
-
-#if 0
- update_env_config();
-#endif
+ metar_loaded = false;
env->reinit();
}
void
FGMetarEnvironmentCtrl::update(double delta_time_sec)
{
-
_dt += delta_time_sec;
if (_error_count >= 3)
return;
= fgGetNode( "/position/longitude-deg", true );
static const SGPropertyNode *latitude
= fgGetNode( "/position/latitude-deg", true );
+ 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 ) {
- const FGAirport* a = globals->get_airports()
- ->search( longitude->getDoubleValue(),
- latitude->getDoubleValue(),
- true );
- if ( a ) {
- if ( !last_apt || last_apt->getId() != a->getId()
+ 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->getId());
- request_queue.push( a->getId() );
+ << a->ident());
+ request_queue.push(a);
last_apt = a;
- _icao = a->getId();
search_elapsed = 0.0;
fetch_elapsed = 0.0;
} else {
SG_LOG( SG_GENERAL, SG_INFO, "same station, waiting = "
<< same_station_interval_sec - fetch_elapsed );
}
+
} else {
- SG_LOG( SG_GENERAL, SG_WARN,
+ SG_LOG( SG_GENERAL, SG_WARN,
"Unable to find any airports with metar" );
}
} else if ( interpolate_elapsed > EnvironmentUpdatePeriodSec ) {
#if !defined(ENABLE_THREADS)
// No loader thread running so manually fetch the data
- string id = "";
+ FGAirport* apt = NULL;
while ( !request_queue.empty() ) {
- id = request_queue.front();
+ apt = request_queue.front();
request_queue.pop();
}
- if ( !id.empty() ) {
- SG_LOG( SG_GENERAL, SG_INFO, "inline fetching = " << id );
- result = fetch_data( id );
+ if (apt) {
+ SG_LOG( SG_GENERAL, SG_INFO, "inline fetching = " << apt->ident() );
+ result = fetch_data( apt );
result_queue.push( result );
}
#endif // ENABLE_THREADS
// 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 );
+ "no metar at station = " << result.airport->ident() );
+ result.airport->setMetar(false);
search_elapsed = 9999.0;
}
}
}
FGMetarResult
-FGMetarEnvironmentCtrl::fetch_data( const string &icao )
+FGMetarEnvironmentCtrl::fetch_data(FGAirport* apt)
{
FGMetarResult result;
- result.icao = icao;
+ result.airport = apt;
// if the last error was more than three seconds ago,
// then pretent nothing happened.
_error_count = 0;
}
- // fetch station elevation if exists
- const FGAirport* a = globals->get_airports()->search( icao );
- if ( a ) {
- station_elevation_ft = a->getElevation();
- }
+ station_elevation_ft = apt->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);
+ result.m = new FGMetar( apt->ident(), host, port, auth);
long max_age = metar_max_age->getLongValue();
long age = result.m->getAge_min();
void
FGMetarEnvironmentCtrl::thread_stop()
{
- request_queue.push( string() ); // ask thread to terminate
+ request_queue.push(NULL); // ask thread to terminate
thread->join();
}
{
while ( true )
{
- string icao = fetcher->request_queue.pop();
- if (icao.empty())
+ FGAirport* apt = fetcher->request_queue.pop();
+ if (!apt)
return;
- SG_LOG( SG_GENERAL, SG_INFO, "Thread: fetch metar data = " << icao );
- FGMetarResult result = fetcher->fetch_data( icao );
+ SG_LOG( SG_GENERAL, SG_INFO, "Thread: fetch metar data = " << apt->ident() );
+ FGMetarResult result = fetcher->fetch_data( apt );
fetcher->result_queue.push( result );
}
}