-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;
- 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(),
- metar_only );
- if ( a ) {
- if ( !last_apt || 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 {
- 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
- 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
+FGMetarCtrl::update(double dt)
+{
+ if( dt <= 0 || !metar_valid ||!enabled)
+ return;
+
+ windModulator->update(dt);
+ // Interpolate the current configuration closer to the actual METAR
+
+ bool reinit_required = false;
+ bool layer_rebuild_required = false;
+
+ if (first_update) {
+ double dir = base_wind_dir_n->getDoubleValue()+magnetic_variation_n->getDoubleValue();
+ double speed = base_wind_speed_n->getDoubleValue();
+ double gust = gust_wind_speed_n->getDoubleValue();
+ setupWind(setup_winds_aloft, dir, speed, gust);
+
+ double metarvis = min_visibility_n->getDoubleValue();
+ fgDefaultWeatherValue("visibility-m", metarvis);
+
+ double metarpressure = pressure_n->getDoubleValue();
+ fgDefaultWeatherValue("pressure-sea-level-inhg", metarpressure);
+
+ // We haven't already loaded a METAR, so apply it immediately.
+ vector<SGPropertyNode_ptr> layers = clouds_n->getChildren("layer");
+ vector<SGPropertyNode_ptr>::const_iterator layer;
+ vector<SGPropertyNode_ptr>::const_iterator layers_end = layers.end();
+
+ int i;
+ for (i = 0, layer = layers.begin(); layer != layers_end; ++layer, i++) {
+ SGPropertyNode *target = environment_clouds_n->getChild("layer", i, true);
+
+ target->setStringValue("coverage",
+ (*layer)->getStringValue("coverage", "clear"));
+ target->setDoubleValue("elevation-ft",
+ (*layer)->getDoubleValue("elevation-ft"));
+ target->setDoubleValue("thickness-ft",
+ (*layer)->getDoubleValue("thickness-ft"));
+ target->setDoubleValue("span-m", 40000.0);
+ }
+
+ first_update = false;
+ reinit_required = true;
+ layer_rebuild_required = true;
+
+ } else {
+ if( wind_interpolation_required ) {
+ // 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 = base_wind_speed_n->getDoubleValue();
+ double metar_heading = base_wind_dir_n->getDoubleValue()+magnetic_variation_n->getDoubleValue();
+
+ metar[0] = metar_speed * sin(metar_heading * SG_DEGREES_TO_RADIANS );
+ metar[1] = metar_speed * cos(metar_heading * SG_DEGREES_TO_RADIANS);
+
+ // Convert the current wind values and convert them into a vector
+ double current[2];
+ double speed = boundary_wind_speed_n->getDoubleValue();
+ double dir_from = boundary_wind_from_heading_n->getDoubleValue();;
+
+ current[0] = speed * sin(dir_from * SG_DEGREES_TO_RADIANS );
+ current[1] = speed * cos(dir_from * SG_DEGREES_TO_RADIANS );
+
+ // 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.01 ) {
+ 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 ((fabs(current[0]) > 0.1) || (fabs(current[1]) > 0.1)) {
+ // 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]) * SG_RADIANS_TO_DEGREES );
+
+ // Normalize the direction.
+ if (dir_from < 0.0)
+ dir_from += 360.0;
+
+ SG_LOG( SG_GENERAL, SG_DEBUG, "Wind : " << dir_from << "@" << speed);
+ } else {
+ // Special case where there is no wind (otherwise atan2 barfs)
+ speed = 0.0;
+ }
+ double gust = gust_wind_speed_n->getDoubleValue();
+ setupWind(setup_winds_aloft, dir_from, speed, gust);
+ reinit_required = true;
+ } else {
+ wind_interpolation_required = false;
+ }
+ } else { // if(wind_interpolation_required)
+ // interpolation of wind vector is finished, apply wind
+ // variations and gusts for the boundary layer only
+
+
+ bool wind_modulated = false;
+
+ // start with the main wind direction
+ double wind_dir = base_wind_dir_n->getDoubleValue()+magnetic_variation_n->getDoubleValue();
+ double min = convert_to_180(base_wind_range_from_n->getDoubleValue()+magnetic_variation_n->getDoubleValue());
+ double max = convert_to_180(base_wind_range_to_n->getDoubleValue()+magnetic_variation_n->getDoubleValue());
+ if( max > min ) {
+ // if variable winds configured, modulate the wind direction
+ double f = windModulator->get_direction_offset_norm();
+ wind_dir = min+(max-min)*f;
+ double old = convert_to_180(boundary_wind_from_heading_n->getDoubleValue());
+ wind_dir = convert_to_360(fgGetLowPass(old, wind_dir, dt ));
+ wind_modulated = true;
+ }
+
+ // start with main wind speed
+ double wind_speed = base_wind_speed_n->getDoubleValue();
+ max = gust_wind_speed_n->getDoubleValue();
+ if( max > wind_speed ) {
+ // if gusts are configured, modulate wind magnitude
+ double f = windModulator->get_magnitude_factor_norm();
+ wind_speed = wind_speed+(max-wind_speed)*f;
+ wind_speed = fgGetLowPass(boundary_wind_speed_n->getDoubleValue(), wind_speed, dt );
+ wind_modulated = true;
+ }
+ if( wind_modulated ) {
+ setupWind(false, wind_dir, wind_speed, max);
+ reinit_required = true;
+ }
+ }
+
+ // 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 vis = boundary_visibility_n->getDoubleValue();;
+ double metarvis = min_visibility_n->getDoubleValue();
+ if( vis != metarvis ) {
+ double currentxval = log(1000.0 + vis);
+ 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;
+ fgDefaultWeatherValue("visibility-m", vis);
+ reinit_required = true;
+ }
+
+ double pressure = boundary_sea_level_pressure_n->getDoubleValue();
+ double metarpressure = pressure_n->getDoubleValue();
+ if( pressure != metarpressure ) {
+ pressure = interpolate_val( pressure, metarpressure, MaxPressureChangeInHgSec );
+ fgDefaultWeatherValue("pressure-sea-level-inhg", pressure);
+ reinit_required = true;
+ }
+
+ // Set the cloud layers by interpolating over the METAR versions.
+ vector<SGPropertyNode_ptr> layers = clouds_n->getChildren("layer");
+ vector<SGPropertyNode_ptr>::const_iterator layer;
+ vector<SGPropertyNode_ptr>::const_iterator layers_end = layers.end();
+
+ double aircraft_alt = fgGetDouble("/position/altitude-ft");
+ int i;
+
+ for (i = 0, layer = layers.begin(); layer != layers_end; ++layer, i++) {
+ SGPropertyNode *target = environment_clouds_n->getChild("layer", i, true);
+
+ // 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.
+ const char *coverage = (*layer)->getStringValue("coverage", "clear");
+ SGPropertyNode *cov = target->getNode("coverage", true);
+ if (strcmp(cov->getStringValue(), coverage) != 0) {
+ cov->setStringValue(coverage);
+ layer_rebuild_required = true;
+ }
+
+ double required_alt = (*layer)->getDoubleValue("elevation-ft");
+ double current_alt = target->getDoubleValue("elevation-ft");
+ double required_thickness = (*layer)->getDoubleValue("thickness-ft");
+ SGPropertyNode *thickness = target->getNode("thickness-ft", true);
+
+ 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,
+ if (current_alt != required_alt)
+ target->setDoubleValue("elevation-ft", required_alt);
+
+ if (thickness->getDoubleValue() != required_thickness)
+ thickness->setDoubleValue(required_thickness);
+
+ } else {
+ // Interpolate the other values in the usual way
+ if (current_alt != required_alt) {
+ current_alt = interpolate_val(current_alt, required_alt, MaxCloudAltitudeChangeFtSec);
+ target->setDoubleValue("elevation-ft", current_alt);
+ }
+
+ double current_thickness = thickness->getDoubleValue();
+
+ if (current_thickness != required_thickness) {
+ current_thickness = interpolate_val(current_thickness,
+ required_thickness,
+ MaxCloudThicknessChangeFtSec);
+ thickness->setDoubleValue(current_thickness);
+ }
+ }
+ }
+ }
+ {
+ double station_elevation_ft = station_elevation_n->getDoubleValue();
+ 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 );