1
0
Fork 0

Add variable winds (direction and gusts) for the boundary layer if defined in METAR.

This commit is contained in:
torsten 2009-06-03 16:29:58 +00:00 committed by Tim Moore
parent 1873346d18
commit ab4928f7a9
6 changed files with 338 additions and 189 deletions

View file

@ -9,6 +9,7 @@ libEnvironment_a_SOURCES = \
environment_mgr.cxx environment_mgr.hxx \ environment_mgr.cxx environment_mgr.hxx \
environment_ctrl.cxx environment_ctrl.hxx \ environment_ctrl.cxx environment_ctrl.hxx \
fgmetar.cxx fgmetar.hxx fgclouds.cxx fgclouds.hxx \ fgmetar.cxx fgmetar.hxx fgclouds.cxx fgclouds.hxx \
fgwind.cxx fgwind.hxx \
atmosphere.cxx atmosphere.hxx \ atmosphere.cxx atmosphere.hxx \
precipitation_mgr.cxx precipitation_mgr.hxx \ precipitation_mgr.cxx precipitation_mgr.hxx \
ridge_lift.cxx ridge_lift.hxx ridge_lift.cxx ridge_lift.hxx

View file

@ -133,34 +133,48 @@ FGInterpolateEnvironmentCtrl::init ()
void void
FGInterpolateEnvironmentCtrl::reinit () FGInterpolateEnvironmentCtrl::reinit ()
{ {
// TODO: do we really need to throw away the old tables on reinit? Better recycle
unsigned int i;
for (i = 0; i < _boundary_table.size(); i++)
delete _boundary_table[i];
for (i = 0; i < _aloft_table.size(); i++)
delete _aloft_table[i];
_boundary_table.clear();
_aloft_table.clear();
init(); init();
} }
void void
FGInterpolateEnvironmentCtrl::read_table (const SGPropertyNode * node, vector<bucket *> &table) FGInterpolateEnvironmentCtrl::read_table (const SGPropertyNode * node, vector<bucket *> &table)
{ {
for (int i = 0; i < node->nChildren(); i++) { double last_altitude_ft = 0.0;
double sort_required = false;
int i;
for (i = 0; i < node->nChildren(); i++) {
const SGPropertyNode * child = node->getChild(i); const SGPropertyNode * child = node->getChild(i);
if ( strcmp(child->getName(), "entry") == 0 if ( strcmp(child->getName(), "entry") == 0
&& child->getStringValue("elevation-ft", "")[0] != '\0' && child->getStringValue("elevation-ft", "")[0] != '\0'
&& ( child->getDoubleValue("elevation-ft") > 0.1 || i == 0 ) ) && ( child->getDoubleValue("elevation-ft") > 0.1 || i == 0 ) )
{ {
bucket * b = new bucket; bucket * b;
if( i < table.size() ) {
// recycle existing bucket
b = table[i];
} else {
// more nodes than buckets in table, add a new one
b = new bucket;
table.push_back(b);
}
if (i > 0) if (i > 0)
b->environment.copy(table[i-1]->environment); b->environment.copy(table[i-1]->environment);
b->environment.read(child); b->environment.read(child);
b->altitude_ft = b->environment.get_elevation_ft(); b->altitude_ft = b->environment.get_elevation_ft();
table.push_back(b);
// check, if altitudes are in ascending order
if( b->altitude_ft < last_altitude_ft )
sort_required = true;
last_altitude_ft = b->altitude_ft;
} }
} }
// remove leftover buckets
vector<bucket*>::iterator it = table.begin() + i;
while( it != table.end() )
table.erase( it );
if( sort_required )
sort(table.begin(), table.end(), bucket::lessThan); sort(table.begin(), table.end(), bucket::lessThan);
} }
@ -181,6 +195,8 @@ FGInterpolateEnvironmentCtrl::update (double delta_time_sec)
do_interpolate(_boundary_table, altitude_agl_ft, _environment); do_interpolate(_boundary_table, altitude_agl_ft, _environment);
return; return;
} else if ((boundary_limit + boundary_transition) >= altitude_agl_ft) { } 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(_boundary_table, altitude_agl_ft, &env1);
do_interpolate(_aloft_table, altitude_ft, &env2); do_interpolate(_aloft_table, altitude_ft, &env2);
@ -250,6 +266,7 @@ FGMetarCtrl::FGMetarCtrl( SGSubsystem * environmentCtrl )
station_elevation_ft(0.0), station_elevation_ft(0.0),
metar_valid(false), metar_valid(false),
setup_winds_aloft(true), setup_winds_aloft(true),
wind_interpolation_required(true),
// Interpolation constant definitions. // Interpolation constant definitions.
EnvironmentUpdatePeriodSec( 0.2 ), EnvironmentUpdatePeriodSec( 0.2 ),
MaxWindChangeKtsSec( 0.2 ), MaxWindChangeKtsSec( 0.2 ),
@ -260,6 +277,8 @@ FGMetarCtrl::FGMetarCtrl( SGSubsystem * environmentCtrl )
MaxCloudInterpolationHeightFt( 5000.0 ), MaxCloudInterpolationHeightFt( 5000.0 ),
MaxCloudInterpolationDeltaFt( 4000.0 ) MaxCloudInterpolationDeltaFt( 4000.0 )
{ {
windModulator = new FGBasicWindModulator();
metar_base_n = fgGetNode( "/environment/metar", true ); metar_base_n = fgGetNode( "/environment/metar", true );
station_id_n = metar_base_n->getNode("station-id", true ); station_id_n = metar_base_n->getNode("station-id", true );
station_elevation_n = metar_base_n->getNode("station-elevation-ft", true ); station_elevation_n = metar_base_n->getNode("station-elevation-ft", true );
@ -369,9 +388,11 @@ static void setupWindBranch( string branchName, double dir, double speed, double
} }
} }
static void setupWind( bool setup_aloft, double dir, double speed, double gust ) static void setupWind( bool setup_boundary, bool setup_aloft, double dir, double speed, double gust )
{ {
if( setup_boundary )
setupWindBranch( "boundary", dir, speed, gust ); setupWindBranch( "boundary", dir, speed, gust );
if( setup_aloft ) if( setup_aloft )
setupWindBranch( "aloft", dir, speed, gust ); setupWindBranch( "aloft", dir, speed, gust );
} }
@ -390,6 +411,7 @@ void
FGMetarCtrl::init () FGMetarCtrl::init ()
{ {
first_update = true; first_update = true;
wind_interpolation_required = true;
} }
void void
@ -398,12 +420,25 @@ FGMetarCtrl::reinit ()
init(); init();
} }
static inline double convert_to_360( double d )
{
if( d < 0.0 ) return d + 360.0;
if( d >= 360.0 ) return d - 360.0;
return d;
}
static inline double convert_to_180( double d )
{
return d > 180.0 ? d - 360.0 : d;
}
void void
FGMetarCtrl::update(double dt) FGMetarCtrl::update(double dt)
{ {
if( dt <= 0 || !metar_valid ||!enabled) if( dt <= 0 || !metar_valid ||!enabled)
return; return;
windModulator->update(dt);
// Interpolate the current configuration closer to the actual METAR // Interpolate the current configuration closer to the actual METAR
bool reinit_required = false; bool reinit_required = false;
@ -413,7 +448,7 @@ FGMetarCtrl::update(double dt)
double dir = base_wind_dir_n->getDoubleValue(); double dir = base_wind_dir_n->getDoubleValue();
double speed = base_wind_speed_n->getDoubleValue(); double speed = base_wind_speed_n->getDoubleValue();
double gust = gust_wind_speed_n->getDoubleValue(); double gust = gust_wind_speed_n->getDoubleValue();
setupWind(setup_winds_aloft, dir, speed, gust); setupWind(true, setup_winds_aloft, dir, speed, gust);
double metarvis = min_visibility_n->getDoubleValue(); double metarvis = min_visibility_n->getDoubleValue();
fgDefaultWeatherValue("visibility-m", metarvis); fgDefaultWeatherValue("visibility-m", metarvis);
@ -444,6 +479,7 @@ FGMetarCtrl::update(double dt)
layer_rebuild_required = true; layer_rebuild_required = true;
} else { } else {
if( wind_interpolation_required ) {
// Generate interpolated values between the METAR and the current // Generate interpolated values between the METAR and the current
// configuration. // configuration.
@ -470,7 +506,7 @@ FGMetarCtrl::update(double dt)
double y = fabs(current[1] - metar[1]); double y = fabs(current[1] - metar[1]);
// only interpolate if we have a difference // only interpolate if we have a difference
if (x + y > 0) { if (x + y > 0.01 ) {
double dx = x / (x + y); double dx = x / (x + y);
double dy = 1 - dx; double dy = 1 - dx;
@ -498,7 +534,37 @@ FGMetarCtrl::update(double dt)
SG_LOG( SG_GENERAL, SG_DEBUG, "Wind : " << dir_from << "@" << speed); SG_LOG( SG_GENERAL, SG_DEBUG, "Wind : " << dir_from << "@" << speed);
} }
double gust = gust_wind_speed_n->getDoubleValue(); double gust = gust_wind_speed_n->getDoubleValue();
setupWind(setup_winds_aloft, dir_from, speed, gust); setupWind(true, 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
// start with the main wind direction
double wind_dir = base_wind_dir_n->getDoubleValue();
double min = convert_to_180(base_wind_range_from_n->getDoubleValue());
double max = convert_to_180(base_wind_range_to_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 ));
}
// 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 );
}
setupWind(true, false, wind_dir, wind_speed, max);
reinit_required = true; reinit_required = true;
} }
@ -595,6 +661,7 @@ FGMetarCtrl::update(double dt)
set_temp_at_altitude(temperature_n->getDoubleValue(), station_elevation_ft); set_temp_at_altitude(temperature_n->getDoubleValue(), station_elevation_ft);
set_dewpoint_at_altitude(dewpoint_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 // Force an update of the 3D clouds
if( layer_rebuild_required ) if( layer_rebuild_required )
@ -629,6 +696,8 @@ void FGMetarCtrl::set_metar( const char * metar_string )
return; return;
} }
wind_interpolation_required = true;
min_visibility_n->setDoubleValue( m->getMinVisibility().getVisibility_m() ); min_visibility_n->setDoubleValue( m->getMinVisibility().getVisibility_m() );
max_visibility_n->setDoubleValue( m->getMaxVisibility().getVisibility_m() ); max_visibility_n->setDoubleValue( m->getMaxVisibility().getVisibility_m() );
@ -751,8 +820,7 @@ void MetarThread::run()
} }
#endif #endif
FGMetarFetcher::FGMetarFetcher() FGMetarFetcher::FGMetarFetcher() :
:
#if defined(ENABLE_THREADS) #if defined(ENABLE_THREADS)
metar_thread(NULL), metar_thread(NULL),
#endif #endif

View file

@ -36,6 +36,7 @@
#include <Navaids/positioned.hxx> #include <Navaids/positioned.hxx>
#include <Environment/environment.hxx> #include <Environment/environment.hxx>
#include "fgwind.hxx"
// forward decls // forward decls
class SGPropertyNode; class SGPropertyNode;
@ -142,10 +143,13 @@ public:
private: private:
void bind(); void bind();
void unbind(); void unbind();
SGSharedPtr<FGWindModulator> windModulator;
bool metar_valid; bool metar_valid;
bool enabled; bool enabled;
bool setup_winds_aloft; bool setup_winds_aloft;
bool first_update; bool first_update;
bool wind_interpolation_required;
double station_elevation_ft; double station_elevation_ft;
string metar; string metar;
double interpolate_prop(const char * currentname, const char * requiredname, double dvalue); double interpolate_prop(const char * currentname, const char * requiredname, double dvalue);

View file

@ -52,12 +52,12 @@ FGEnvironmentMgr::FGEnvironmentMgr ()
_controller = new FGInterpolateEnvironmentCtrl; _controller = new FGInterpolateEnvironmentCtrl;
_controller->setEnvironment(_environment); _controller->setEnvironment(_environment);
set_subsystem("controller", _controller, 0.5); set_subsystem("controller", _controller, 0.1 );
fgClouds = new FGClouds(); fgClouds = new FGClouds();
_metarcontroller = new FGMetarCtrl(_controller ); _metarcontroller = new FGMetarCtrl(_controller );
set_subsystem("metarcontroller", _metarcontroller, 0.25 ); set_subsystem("metarcontroller", _metarcontroller, 0.1 );
_metarfetcher = new FGMetarFetcher(); _metarfetcher = new FGMetarFetcher();
set_subsystem("metarfetcher", _metarfetcher, 1.0 ); set_subsystem("metarfetcher", _metarfetcher, 1.0 );

View file

@ -0,0 +1,36 @@
#include "fgwind.hxx"
#include <math.h>
#include <stdio.h>
FGWindModulator::FGWindModulator() :
direction_offset_norm(0.0),
magnitude_factor_norm(1.0)
{
}
FGWindModulator::~FGWindModulator()
{
}
FGBasicWindModulator::FGBasicWindModulator() :
elapsed(0.0),
direction_period(17),
speed_period(1)
{
}
FGBasicWindModulator::~FGBasicWindModulator()
{
}
void FGBasicWindModulator::update( double dt)
{
elapsed += dt;
double t = elapsed/direction_period;
direction_offset_norm = (sin(t)*sin(2*t)+sin(t/3)) / 1.75;
t = elapsed/speed_period;
magnitude_factor_norm = sin(t)* sin(5*direction_offset_norm*direction_offset_norm);;
magnitude_factor_norm = magnitude_factor_norm < 0 ? 0 : magnitude_factor_norm;
}

View file

@ -0,0 +1,40 @@
#ifndef _FGWIND_HXX
#include <simgear/structure/SGReferenced.hxx>
////////////////////////////////////////////////////////////////////////
// A Wind Modulator interface, generates gusts and wind direction changes
////////////////////////////////////////////////////////////////////////
class FGWindModulator : public SGReferenced {
public:
FGWindModulator();
virtual ~FGWindModulator();
virtual void update( double dt ) = 0;
double get_direction_offset_norm() const { return direction_offset_norm; }
double get_magnitude_factor_norm() const { return magnitude_factor_norm; }
protected:
double direction_offset_norm;
double magnitude_factor_norm;
};
////////////////////////////////////////////////////////////////////////
// A Basic Wind Modulator, implementation of FGWindModulator
// direction and magnitude variations are based on simple sin functions
////////////////////////////////////////////////////////////////////////
class FGBasicWindModulator : public FGWindModulator {
public:
FGBasicWindModulator();
virtual ~FGBasicWindModulator();
virtual void update( double dt );
void set_direction_period( double _direction_period ) { direction_period = _direction_period; }
double get_direction_period() const { return direction_period; }
void set_speed_period( double _speed_period ) { speed_period = _speed_period; }
double get_speed_period() const{ return speed_period; }
private:
double elapsed;
double direction_period;
double speed_period;
};
#endif