#endif
#include <algorithm>
+#include <cstring>
#include <simgear/debug/logstream.hxx>
#include <simgear/structure/commands.hxx>
FGInterpolateEnvironmentCtrl::init ()
{
read_table( boundary_n, _boundary_table);
- read_table( aloft_n, _aloft_table);
+ // pass in a pointer to the environment of the last bondary layer as
+ // a starting point
+ read_table( aloft_n, _aloft_table, &(*(_boundary_table.end()-1))->environment);
}
void
}
void
-FGInterpolateEnvironmentCtrl::read_table (const SGPropertyNode * node, vector<bucket *> &table)
+FGInterpolateEnvironmentCtrl::read_table (const SGPropertyNode * node, vector<bucket *> &table, FGEnvironment * parent )
{
double last_altitude_ft = 0.0;
double sort_required = false;
b = new bucket;
table.push_back(b);
}
+ if (i == 0 && parent != NULL )
+ b->environment.copy( *parent );
if (i > 0)
b->environment.copy(table[i-1]->environment);
+
b->environment.read(child);
b->altitude_ft = b->environment.get_elevation_ft();
if( sort_required )
sort(table.begin(), table.end(), bucket::lessThan);
+
+ // cleanup entries with (almost)same altitude
+ for( vector<bucket *>::size_type n = 1; n < table.size(); n++ ) {
+ if( fabs(table[n]->altitude_ft - table[n-1]->altitude_ft ) < 1 ) {
+ SG_LOG( SG_GENERAL, SG_ALERT, "Removing duplicate altitude entry in environment config for altitude " << table[n]->altitude_ft );
+ table.erase( table.begin() + n );
+ }
+ }
}
void
int length = _boundary_table.size();
if (length > 0) {
- // boundary table
+ // 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);
} else if ((boundary_limit + boundary_transition) >= altitude_agl_ft) {
//TODO: this is 500ft above the top altitude of boundary layer
//shouldn't this be +/-250 ft off of the top altitude?
- // both tables
+ // 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;
+ double fraction = boundary_transition > SGLimitsd::min() ?
+ (altitude_agl_ft - boundary_limit) / boundary_transition : 1.0;
interpolate(&env1, &env2, fraction, _environment);
return;
}
}
- // aloft table
+ // aloft table
do_interpolate(_aloft_table, altitude_ft, _environment);
}
if (length == 0)
return;
- // Boundary conditions
+ // Boundary conditions
if ((length == 1) || (table[0]->altitude_ft >= altitude_ft)) {
- environment->copy(table[0]->environment);
+ environment->copy(table[0]->environment); // below bottom of table
return;
} else if (table[length-1]->altitude_ft <= altitude_ft) {
- environment->copy(table[length-1]->environment);
+ environment->copy(table[length-1]->environment); // above top of table
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;
- }
- }
+ }
+
+ // Search the interpolation table
+ int layer;
+ for ( layer = 1; // can't be below bottom layer, handled above
+ layer < length && table[layer]->altitude_ft <= altitude_ft;
+ layer++);
+ FGEnvironment * env1 = &(table[layer-1]->environment);
+ FGEnvironment * env2 = &(table[layer]->environment);
+ // two layers of same altitude were sorted out in read_table
+ double fraction = ((altitude_ft - table[layer-1]->altitude_ft) /
+ (table[layer]->altitude_ft - table[layer-1]->altitude_ft));
+ interpolate(env1, env2, fraction, environment);
}
bool
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 ),
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);
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;
+ 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
bool reinit_required = false;
bool layer_rebuild_required = false;
- double station_elevation_ft = station_elevation_n->getDoubleValue();
+ 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",
- reducePressureSl(metarpressure,
- station_elevation_ft,
- temperature_n->getDoubleValue()));
+ 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();
- double newpressure = reducePressureSl(metarpressure,
- station_elevation_ft,
- temperature_n->getDoubleValue());
+ double newpressure = reducePressureSl(metarpressure,
+ station_elevation_ft,
+ temperature_n->getDoubleValue());
if( pressure != newpressure ) {
- pressure = interpolate_val( pressure, newpressure, MaxPressureChangeInHgSec );
+ 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 )
m = new FGMetar( metar_string );
}
catch( sg_io_exception ) {
- fprintf( stderr, "can't get metar: %s\n", metar_string );
+ SG_LOG( SG_GENERAL, SG_WARN, "Can't get metar: " << metar_string );
metar_valid = false;
return;
}
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();