#include <Main/fg_props.hxx>
#include <Main/util.hxx>
+#include "atmosphere.hxx"
#include "fgmetar.hxx"
#include "environment_ctrl.hxx"
virtual bool passAirport(FGAirport* aApt) const {
return aApt->getMetar();
}
+
+ // permit heliports and seaports too
+ virtual FGPositioned::Type maxType() const
+ { return FGPositioned::SEAPORT; }
};
static AirportWithMetar airportWithMetarFilter;
////////////////////////////////////////////////////////////////////////
FGMetarCtrl::FGMetarCtrl( SGSubsystem * environmentCtrl )
- : _environmentCtrl(environmentCtrl),
- station_elevation_ft(0.0),
+ :
metar_valid(false),
setup_winds_aloft(true),
wind_interpolation_required(true),
+ metar_sealevel_temperature(15.0),
+ metar_sealevel_dewpoint(5.0),
// Interpolation constant definitions.
- EnvironmentUpdatePeriodSec( 0.2 ),
MaxWindChangeKtsSec( 0.2 ),
MaxVisChangePercentSec( 0.05 ),
- MaxPressureChangeInHgSec( 0.0033 ),
+ MaxPressureChangeInHgSec( 0.0005 ), // approx 1hpa/min
+ MaxTemperatureChangeDegcSec(10.0/60.0), // approx 10degc/min
MaxCloudAltitudeChangeFtSec( 20.0 ),
MaxCloudThicknessChangeFtSec( 50.0 ),
MaxCloudInterpolationHeightFt( 5000.0 ),
- MaxCloudInterpolationDeltaFt( 4000.0 )
+ MaxCloudInterpolationDeltaFt( 4000.0 ),
+ _environmentCtrl(environmentCtrl)
{
windModulator = new FGBasicWindModulator();
latitude_n = fgGetNode( "/position/latitude-deg", true );
environment_clouds_n = fgGetNode("/environment/clouds");
- boundary_wind_speed_n = fgGetNode("/environment/config/boundary/entry/wind-speed-kt");
- boundary_wind_from_heading_n = fgGetNode("/environment/config/boundary/entry/wind-from-heading-deg");
- boundary_visibility_n = fgGetNode("/environment/config/boundary/entry/visibility-m");
- boundary_sea_level_pressure_n = fgGetNode("/environment/config/boundary/entry/pressure-sea-level-inhg");
+ boundary_wind_speed_n = fgGetNode("/environment/config/boundary/entry/wind-speed-kt", true );
+ boundary_wind_from_heading_n = fgGetNode("/environment/config/boundary/entry/wind-from-heading-deg", true );
+ boundary_visibility_n = fgGetNode("/environment/config/boundary/entry/visibility-m", true );
+ boundary_sea_level_pressure_n = fgGetNode("/environment/config/boundary/entry/pressure-sea-level-inhg", true );
+ boundary_sea_level_temperature_n = fgGetNode("/environment/config/boundary/entry/temperature-sea-level-degc", true );
+ boundary_sea_level_dewpoint_n = fgGetNode("/environment/config/boundary/entry/dewpoint-sea-level-degc", true );
}
FGMetarCtrl::~FGMetarCtrl ()
}
// use a "command" to set station temp at station elevation
-static void set_temp_at_altitude( float temp_degc, float altitude_ft ) {
+static void set_temp_at_altitude( double temp_degc, double altitude_ft ) {
SGPropertyNode args;
SGPropertyNode *node = args.getNode("temp-degc", 0, true);
- node->setFloatValue( temp_degc );
+ node->setDoubleValue( temp_degc );
node = args.getNode("altitude-ft", 0, true);
- node->setFloatValue( altitude_ft );
- globals->get_commands()->execute("set-outside-air-temp-degc", &args);
+ node->setDoubleValue( altitude_ft );
+ globals->get_commands()->execute( altitude_ft == 0.0 ?
+ "set-sea-level-air-temp-degc" :
+ "set-outside-air-temp-degc", &args);
}
-static void set_dewpoint_at_altitude( float dewpoint_degc, float altitude_ft ) {
+static void set_dewpoint_at_altitude( double dewpoint_degc, double altitude_ft ) {
SGPropertyNode args;
SGPropertyNode *node = args.getNode("dewpoint-degc", 0, true);
- node->setFloatValue( dewpoint_degc );
+ node->setDoubleValue( dewpoint_degc );
node = args.getNode("altitude-ft", 0, true);
- node->setFloatValue( altitude_ft );
- globals->get_commands()->execute("set-dewpoint-temp-degc", &args);
+ node->setDoubleValue( altitude_ft );
+ globals->get_commands()->execute( altitude_ft == 0.0 ?
+ "set-dewpoint-sea-level-air-temp-degc" :
+ "set-dewpoint-temp-degc", &args);
}
/*
setupWindBranch( "aloft", dir, speed, gust );
}
-double FGMetarCtrl::interpolate_val(double currentval, double requiredval, double dt)
+double FGMetarCtrl::interpolate_val(double currentval, double requiredval, double dval )
{
- double dval = EnvironmentUpdatePeriodSec * dt;
-
if (fabs(currentval - requiredval) < dval) return requiredval;
if (currentval < requiredval) return (currentval + dval);
if (currentval > requiredval) return (currentval - dval);
return d > 180.0 ? d - 360.0 : d;
}
+// Return the sea level pressure for a metar observation, in inHg.
+// This is different from QNH because it accounts for the current
+// temperature at the observation point.
+// metarPressure in inHg
+// fieldHt in ft
+// fieldTemp in C
+
+static double reducePressureSl(double metarPressure, double fieldHt,
+ double fieldTemp)
+{
+ double elev = fieldHt * SG_FEET_TO_METER;
+ double fieldPressure
+ = FGAtmo::fieldPressure(elev, metarPressure * atmodel::inHg);
+ double slPressure = P_layer(0, elev, fieldPressure,
+ fieldTemp + atmodel::freezing, atmodel::ISA::lam0);
+ return slPressure / atmodel::inHg;
+}
+
void
FGMetarCtrl::update(double dt)
{
bool reinit_required = false;
bool layer_rebuild_required = false;
+ double station_elevation_ft = station_elevation_n->getDoubleValue();
if (first_update) {
double dir = base_wind_dir_n->getDoubleValue()+magnetic_variation_n->getDoubleValue();
double metarvis = min_visibility_n->getDoubleValue();
fgDefaultWeatherValue("visibility-m", metarvis);
+ set_temp_at_altitude(temperature_n->getDoubleValue(), station_elevation_ft);
+ set_dewpoint_at_altitude(dewpoint_n->getDoubleValue(), station_elevation_ft);
+
double metarpressure = pressure_n->getDoubleValue();
- fgDefaultWeatherValue("pressure-sea-level-inhg", metarpressure);
+ fgDefaultWeatherValue("pressure-sea-level-inhg",
+ reducePressureSl(metarpressure,
+ station_elevation_ft,
+ temperature_n->getDoubleValue()));
// We haven't already loaded a METAR, so apply it immediately.
vector<SGPropertyNode_ptr> layers = clouds_n->getChildren("layer");
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);
+ current[0] = interpolate_val(current[0], metar[0], maxdx*dt);
+ current[1] = interpolate_val(current[1], metar[1], maxdy*dt);
// Now convert back to polar coordinates.
if ((fabs(current[0]) > 0.1) || (fabs(current[1]) > 0.1)) {
double currentxval = log(1000.0 + vis);
double metarxval = log(1000.0 + metarvis);
- currentxval = interpolate_val(currentxval, metarxval, MaxVisChangePercentSec);
+ currentxval = interpolate_val(currentxval, metarxval, MaxVisChangePercentSec*dt);
// Now convert back from an X-value to a straightforward visibility.
vis = exp(currentxval) - 1000.0;
double pressure = boundary_sea_level_pressure_n->getDoubleValue();
double metarpressure = pressure_n->getDoubleValue();
- if( pressure != metarpressure ) {
- pressure = interpolate_val( pressure, metarpressure, MaxPressureChangeInHgSec );
+ double newpressure = reducePressureSl(metarpressure,
+ station_elevation_ft,
+ temperature_n->getDoubleValue());
+ if( pressure != newpressure ) {
+ pressure = interpolate_val( pressure, newpressure, MaxPressureChangeInHgSec*dt );
fgDefaultWeatherValue("pressure-sea-level-inhg", pressure);
reinit_required = true;
}
+ {
+ double temperature = boundary_sea_level_temperature_n->getDoubleValue();
+ double dewpoint = boundary_sea_level_dewpoint_n->getDoubleValue();
+ if( metar_sealevel_temperature != temperature ) {
+ temperature = interpolate_val( temperature, metar_sealevel_temperature, MaxTemperatureChangeDegcSec*dt );
+ set_temp_at_altitude( temperature, 0.0 );
+ }
+ if( metar_sealevel_dewpoint != dewpoint ) {
+ dewpoint = interpolate_val( dewpoint, metar_sealevel_dewpoint, MaxTemperatureChangeDegcSec*dt );
+ set_dewpoint_at_altitude( dewpoint, 0.0 );
+ }
+ }
+
// Set the cloud layers by interpolating over the METAR versions.
vector<SGPropertyNode_ptr> layers = clouds_n->getChildren("layer");
vector<SGPropertyNode_ptr>::const_iterator layer;
} else {
// Interpolate the other values in the usual way
if (current_alt != required_alt) {
- current_alt = interpolate_val(current_alt, required_alt, MaxCloudAltitudeChangeFtSec);
+ current_alt = interpolate_val(current_alt, required_alt, MaxCloudAltitudeChangeFtSec*dt);
target->setDoubleValue("elevation-ft", current_alt);
}
if (current_thickness != required_thickness) {
current_thickness = interpolate_val(current_thickness,
required_thickness,
- MaxCloudThicknessChangeFtSec);
+ MaxCloudThicknessChangeFtSec*dt);
thickness->setDoubleValue(current_thickness);
}
}
}
}
- set_temp_at_altitude(temperature_n->getDoubleValue(), station_elevation_ft);
- set_dewpoint_at_altitude(dewpoint_n->getDoubleValue(), station_elevation_ft);
- //TODO: check if temperature/dewpoint have changed. This requires reinit.
-
// Force an update of the 3D clouds
if( layer_rebuild_required )
fgSetInt("/environment/rebuild-layers", 1 );
station_elevation_n->setDoubleValue( station_elevation_ft );
+ { // calculate sea level temperature and dewpoint
+ FGEnvironment dummy; // instantiate a dummy so we can leech a method
+ dummy.set_elevation_ft( station_elevation_ft );
+ dummy.set_temperature_degc( temperature_n->getDoubleValue() );
+ dummy.set_dewpoint_degc( dewpoint_n->getDoubleValue() );
+ metar_sealevel_temperature = dummy.get_temperature_sea_level_degc();
+ metar_sealevel_dewpoint = dummy.get_dewpoint_sea_level_degc();
+ }
+
vector<SGMetarCloud> cv = m->getClouds();
vector<SGMetarCloud>::const_iterator cloud, cloud_end = cv.end();
break;
}
- metar_fetcher->fetch( airport_id );
+ metar_fetcher->fetch( airport_id );
}
}
#endif
*/
const char * startup_airport = fgGetString("/sim/startup/options/airport");
if( *startup_airport ) {
- current_airport_id = startup_airport;
- fetch( current_airport_id );
+ FGAirport * a = FGAirport::getByIdent( startup_airport );
+ if( a ) {
+ SGGeod pos = SGGeod::fromDeg(a->getLongitude(), a->getLatitude());
+ a = FGAirport::findClosest(pos, 10000.0, &airportWithMetarFilter);
+ current_airport_id = a->getId();
+ fetch( current_airport_id );
+ }
}
}
} catch (const sg_io_exception& e) {
SG_LOG( SG_GENERAL, SG_WARN, "Error fetching live weather data: " << e.getFormattedMessage().c_str() );
result = NULL;
+ // remove METAR flag from the airport
+ FGAirport * a = FGAirport::findByIdent( id );
+ if( a ) a->setMetar( false );
+ // immediately schedule a new search
+ search_timer = 0.0;
}
// write the metar to the property node, the rest is done by the methods tied to this property