2009-04-20 14:20:05 +00:00
|
|
|
// simulates ridge lift
|
|
|
|
//
|
|
|
|
// Written by Patrice Poly
|
|
|
|
// Copyright (C) 2009 Patrice Poly - p.polypa@gmail.com
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// Entirely based on the paper :
|
|
|
|
// http://carrier.csi.cam.ac.uk/forsterlewis/soaring/sim/fsx/dev/sim_probe/sim_probe_paper.html
|
|
|
|
// by Ian Forster-Lewis, University of Cambridge, 26th December 2007
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// This program is free software; you can redistribute it and/or
|
|
|
|
// modify it under the terms of the GNU General Public License as
|
|
|
|
// published by the Free Software Foundation; either version 2 of the
|
|
|
|
// License, or (at your option) any later version.
|
|
|
|
//
|
|
|
|
// This program is distributed in the hope that it will be useful, but
|
|
|
|
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
// General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
|
|
// along with this program; if not, write to the Free Software
|
|
|
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
//
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
# include <config.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <Main/fg_props.hxx>
|
|
|
|
#include <Main/globals.hxx>
|
2009-04-24 13:52:30 +00:00
|
|
|
#include <Main/util.hxx>
|
2009-04-20 14:20:05 +00:00
|
|
|
#include <Scenery/scenery.hxx>
|
|
|
|
#include <string>
|
|
|
|
#include <math.h>
|
|
|
|
|
2009-04-24 13:52:30 +00:00
|
|
|
|
2009-04-20 14:20:05 +00:00
|
|
|
using std::string;
|
|
|
|
|
|
|
|
#include "ridge_lift.hxx"
|
|
|
|
|
2009-04-24 13:52:30 +00:00
|
|
|
static string CreateIndexedPropertyName(string Property, int index)
|
|
|
|
{
|
|
|
|
std::stringstream str;
|
|
|
|
str << index;
|
|
|
|
string tmp;
|
|
|
|
str >> tmp;
|
|
|
|
return Property + "[" + tmp + "]";
|
|
|
|
}
|
|
|
|
|
2009-04-24 20:38:05 +00:00
|
|
|
static inline double sign(double x) {
|
|
|
|
return x == 0 ? 0 : x > 0 ? 1.0 : -1.0;
|
|
|
|
}
|
|
|
|
|
2009-04-24 13:52:30 +00:00
|
|
|
static const double BOUNDARY1_m = 40.0;
|
|
|
|
|
2009-04-20 14:20:05 +00:00
|
|
|
//constructor
|
|
|
|
FGRidgeLift::FGRidgeLift ()
|
|
|
|
{
|
|
|
|
dist_probe_m[0] = 0.0; // in meters
|
|
|
|
dist_probe_m[1] = 250.0;
|
|
|
|
dist_probe_m[2] = 750.0;
|
|
|
|
dist_probe_m[3] = 2000.0;
|
|
|
|
dist_probe_m[4] = -100.0;
|
|
|
|
|
|
|
|
strength = 0.0;
|
|
|
|
timer = 0.0;
|
2009-04-26 09:18:13 +00:00
|
|
|
for( int i = 0; i < 5; i++ )
|
|
|
|
probe_elev_m[i] = probe_lat_deg[i] = probe_lon_deg[i] = 0.0;
|
|
|
|
|
|
|
|
for( int i = 0; i < 4; i++ )
|
|
|
|
slope[i] = 0.0;
|
2009-04-20 14:20:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//destructor
|
|
|
|
FGRidgeLift::~FGRidgeLift()
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void FGRidgeLift::init(void)
|
|
|
|
{
|
2009-04-26 09:18:13 +00:00
|
|
|
_enabled_node = fgGetNode( "/environment/ridge-lift/enabled", false );
|
|
|
|
|
2009-04-24 13:52:30 +00:00
|
|
|
_ridge_lift_fps_node = fgGetNode("/environment/ridge-lift-fps", true);
|
2009-04-20 14:20:05 +00:00
|
|
|
_surface_wind_from_deg_node =
|
|
|
|
fgGetNode("/environment/config/boundary/entry[0]/wind-from-heading-deg"
|
|
|
|
, true);
|
|
|
|
_surface_wind_speed_node =
|
|
|
|
fgGetNode("/environment/config/boundary/entry[0]/wind-speed-kt"
|
|
|
|
, true);
|
2009-04-24 13:52:30 +00:00
|
|
|
_earth_radius_node = fgGetNode("/position/sea-level-radius-ft", true);
|
|
|
|
_user_longitude_node = fgGetNode("/position/longitude-deg", true);
|
|
|
|
_user_latitude_node = fgGetNode("/position/latitude-deg", true);
|
|
|
|
_user_altitude_ft_node = fgGetNode("/position/altitude-ft", true);
|
|
|
|
_user_altitude_agl_ft_node = fgGetNode("/position/altitude-agl-ft", true);
|
2009-04-20 14:20:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void FGRidgeLift::bind() {
|
2009-04-24 13:52:30 +00:00
|
|
|
string prop;
|
2009-04-20 14:20:05 +00:00
|
|
|
|
2009-04-24 13:52:30 +00:00
|
|
|
for( int i = 0; i < 5; i++ ) {
|
|
|
|
prop = CreateIndexedPropertyName("/environment/ridge-lift/probe-elev-m", i );
|
|
|
|
fgTie( prop.c_str(), this, i, &FGRidgeLift::get_probe_elev_m); // read-only
|
|
|
|
|
|
|
|
prop = CreateIndexedPropertyName("/environment/ridge-lift/probe-lat-deg", i );
|
|
|
|
fgTie( prop.c_str(), this, i, &FGRidgeLift::get_probe_lat_deg); // read-only
|
|
|
|
|
|
|
|
prop = CreateIndexedPropertyName("/environment/ridge-lift/probe-lon-deg", i );
|
|
|
|
fgTie( prop.c_str(), this, i, &FGRidgeLift::get_probe_lon_deg); // read-only
|
|
|
|
}
|
|
|
|
|
|
|
|
for( int i = 0; i < 4; i++ ) {
|
|
|
|
prop = CreateIndexedPropertyName("/environment/ridge-lift/slope", i );
|
|
|
|
fgTie( prop.c_str(), this, i, &FGRidgeLift::get_slope); // read-only
|
|
|
|
}
|
2009-04-20 14:20:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void FGRidgeLift::unbind() {
|
2009-04-24 13:52:30 +00:00
|
|
|
string prop;
|
2009-04-20 14:20:05 +00:00
|
|
|
|
2009-04-24 13:52:30 +00:00
|
|
|
for( int i = 0; i < 5; i++ ) {
|
2009-04-20 14:20:05 +00:00
|
|
|
|
2009-04-24 13:52:30 +00:00
|
|
|
prop = CreateIndexedPropertyName("/environment/ridge-lift/probe-elev-m", i );
|
|
|
|
fgUntie( prop.c_str() );
|
2009-04-20 14:20:05 +00:00
|
|
|
|
2009-04-24 13:52:30 +00:00
|
|
|
prop = CreateIndexedPropertyName("/environment/ridge-lift/probe-lat-deg", i );
|
|
|
|
fgUntie( prop.c_str() );
|
2009-04-20 14:20:05 +00:00
|
|
|
|
2009-04-24 13:52:30 +00:00
|
|
|
prop = CreateIndexedPropertyName("/environment/ridge-lift/probe-lon-deg", i );
|
|
|
|
fgUntie( prop.c_str() );
|
|
|
|
}
|
2009-04-20 14:20:05 +00:00
|
|
|
|
2009-04-24 13:52:30 +00:00
|
|
|
for( int i = 0; i < 4; i++ ) {
|
|
|
|
prop = CreateIndexedPropertyName("/environment/ridge-lift/slope", i );
|
|
|
|
fgUntie( prop.c_str() );
|
|
|
|
}
|
|
|
|
}
|
2009-04-20 14:20:05 +00:00
|
|
|
|
2009-04-24 13:52:30 +00:00
|
|
|
void FGRidgeLift::update(double dt) {
|
2009-04-20 14:20:05 +00:00
|
|
|
|
2009-05-20 09:24:56 +00:00
|
|
|
if( dt <= 0 ) // paused, do nothing but keep current lift
|
|
|
|
return;
|
|
|
|
|
2009-04-26 09:18:13 +00:00
|
|
|
if( _enabled_node && false == _enabled_node->getBoolValue() ) {
|
|
|
|
if( strength != 0.0 ) {
|
|
|
|
strength = 0.0;
|
|
|
|
_ridge_lift_fps_node->setDoubleValue( 0 );
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2009-04-20 14:20:05 +00:00
|
|
|
|
2009-04-26 09:18:13 +00:00
|
|
|
//get the windspeed at ground level
|
2009-04-24 13:52:30 +00:00
|
|
|
double ground_wind_from_rad = _surface_wind_from_deg_node->getDoubleValue() * SG_DEGREES_TO_RADIANS;
|
|
|
|
double ground_wind_speed_mps = _surface_wind_speed_node->getDoubleValue() * SG_NM_TO_METER / 3600;
|
2009-04-20 14:20:05 +00:00
|
|
|
|
2009-04-24 13:52:30 +00:00
|
|
|
timer -= dt;
|
|
|
|
if (timer <= 0.0 ) {
|
|
|
|
// copy values
|
2009-04-20 14:20:05 +00:00
|
|
|
|
2009-04-24 13:52:30 +00:00
|
|
|
double user_latitude_rad = _user_latitude_node->getDoubleValue() * SG_DEGREES_TO_RADIANS;
|
|
|
|
double user_longitude_rad = _user_longitude_node->getDoubleValue() * SG_DEGREES_TO_RADIANS;
|
2009-04-20 14:20:05 +00:00
|
|
|
|
2009-04-24 13:52:30 +00:00
|
|
|
double earth_rad_m = _earth_radius_node->getDoubleValue() * SG_FEET_TO_METER;
|
|
|
|
if( earth_rad_m < SG_EPSILON )
|
|
|
|
earth_rad_m = SG_EARTH_RAD * 1000;
|
|
|
|
|
|
|
|
// Placing the probes
|
2009-04-20 14:20:05 +00:00
|
|
|
|
2009-04-24 13:52:30 +00:00
|
|
|
for (int i = 0; i < sizeof(probe_lat_rad)/sizeof(probe_lat_rad[0]); i++) {
|
|
|
|
double probe_radius_ratio = dist_probe_m[i]/earth_rad_m;
|
2009-04-26 09:18:13 +00:00
|
|
|
double sin_probe_radius_ratio = sin(probe_radius_ratio);
|
2009-04-24 13:52:30 +00:00
|
|
|
|
|
|
|
probe_lat_rad[i] = asin(sin(user_latitude_rad)*cos(probe_radius_ratio)
|
2009-04-26 09:18:13 +00:00
|
|
|
+cos(user_latitude_rad)*sin_probe_radius_ratio*cos(ground_wind_from_rad));
|
2009-05-23 12:48:18 +00:00
|
|
|
if (fabs(fabs(probe_lat_rad[i])-SG_PI/2.0) < SG_EPSILON ) {
|
2009-04-24 13:52:30 +00:00
|
|
|
probe_lon_rad[i] = user_latitude_rad; // probe on a pole
|
|
|
|
} else {
|
|
|
|
probe_lon_rad[i] = fmod((user_longitude_rad+asin(sin(ground_wind_from_rad)
|
2009-04-26 09:18:13 +00:00
|
|
|
*sin_probe_radius_ratio/cos(probe_lat_rad[i]))+SG_PI)
|
2009-04-24 13:52:30 +00:00
|
|
|
,SGD_2PI)-SG_PI;
|
|
|
|
}
|
|
|
|
probe_lat_deg[i]= probe_lat_rad[i] * SG_RADIANS_TO_DEGREES;
|
|
|
|
probe_lon_deg[i]= probe_lon_rad[i] * SG_RADIANS_TO_DEGREES;
|
|
|
|
}
|
2009-04-20 14:20:05 +00:00
|
|
|
|
2009-04-24 13:52:30 +00:00
|
|
|
for (int i = 0; i < sizeof(probe_elev_m)/sizeof(probe_elev_m[0]); i++) {
|
2009-05-23 12:48:18 +00:00
|
|
|
if (!globals->get_scenery()->get_elevation_m(SGGeod::fromGeodM(
|
|
|
|
SGGeod::fromRad(probe_lon_rad[i],probe_lat_rad[i]), 20000), probe_elev_m[i], 0)) {
|
2009-04-24 13:52:30 +00:00
|
|
|
probe_elev_m[i] = 0.1;
|
2009-04-20 14:20:05 +00:00
|
|
|
}
|
|
|
|
}
|
2009-04-24 08:20:48 +00:00
|
|
|
|
2009-04-24 13:52:30 +00:00
|
|
|
// slopes
|
|
|
|
double adj_slope[4];
|
|
|
|
slope[0] = (probe_elev_m[0] - probe_elev_m[1]) / dist_probe_m[1];
|
|
|
|
slope[1] = (probe_elev_m[1] - probe_elev_m[2]) / dist_probe_m[2];
|
|
|
|
slope[2] = (probe_elev_m[2] - probe_elev_m[3]) / dist_probe_m[3];
|
|
|
|
slope[3] = (probe_elev_m[4] - probe_elev_m[0]) / -dist_probe_m[4];
|
|
|
|
|
|
|
|
for (int i = 0; i < sizeof(slope)/sizeof(slope[0]); i++)
|
|
|
|
adj_slope[i] = sin(atan(5.0 * pow ( (fabs(slope[i])),1.7) ) ) *sign(slope[i]);
|
|
|
|
|
|
|
|
//adjustment
|
2009-04-26 09:18:13 +00:00
|
|
|
adj_slope[0] *= 0.2;
|
|
|
|
adj_slope[1] *= 0.2;
|
2009-04-24 13:52:30 +00:00
|
|
|
if ( adj_slope [2] < 0.0 ) {
|
2009-04-26 09:18:13 +00:00
|
|
|
adj_slope[2] *= 0.5;
|
2009-04-24 13:52:30 +00:00
|
|
|
} else {
|
|
|
|
adj_slope[2] = 0.0 ;
|
|
|
|
}
|
2009-04-20 14:20:05 +00:00
|
|
|
|
2009-04-24 13:52:30 +00:00
|
|
|
if ( ( adj_slope [0] >= 0.0 ) && ( adj_slope [3] < 0.0 ) ) {
|
|
|
|
adj_slope[3] = 0.0;
|
|
|
|
} else {
|
2009-04-26 09:18:13 +00:00
|
|
|
adj_slope[3] *= 0.2;
|
2009-04-24 13:52:30 +00:00
|
|
|
}
|
|
|
|
lift_factor = adj_slope[0]+adj_slope[1]+adj_slope[2]+adj_slope[3];
|
2009-04-20 14:20:05 +00:00
|
|
|
|
2009-04-24 13:52:30 +00:00
|
|
|
// restart the timer
|
|
|
|
timer = 1.0;
|
2009-04-20 14:20:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//user altitude above ground
|
2009-04-24 13:52:30 +00:00
|
|
|
double user_altitude_agl_m = _user_altitude_agl_ft_node->getDoubleValue() * SG_FEET_TO_METER;
|
2009-04-20 14:20:05 +00:00
|
|
|
|
|
|
|
//boundaries
|
2009-04-24 20:38:05 +00:00
|
|
|
double boundary2_m = 130.0; // in the lift
|
2009-04-24 13:52:30 +00:00
|
|
|
if (lift_factor < 0.0) { // in the sink
|
2009-04-24 08:20:48 +00:00
|
|
|
double highest_probe_temp= max ( probe_elev_m[1], probe_elev_m[2] );
|
|
|
|
double highest_probe_downwind_m= max ( highest_probe_temp, probe_elev_m[3] );
|
2009-04-24 13:52:30 +00:00
|
|
|
boundary2_m = highest_probe_downwind_m - probe_elev_m[0];
|
2009-04-24 08:20:48 +00:00
|
|
|
}
|
|
|
|
|
2009-04-24 13:52:30 +00:00
|
|
|
double agl_factor;
|
|
|
|
if ( user_altitude_agl_m < BOUNDARY1_m ) {
|
2009-04-20 14:20:05 +00:00
|
|
|
agl_factor = 0.5+0.5*user_altitude_agl_m /BOUNDARY1_m ;
|
2009-04-24 13:52:30 +00:00
|
|
|
} else if ( user_altitude_agl_m < boundary2_m ) {
|
2009-04-20 14:20:05 +00:00
|
|
|
agl_factor = 1.0;
|
2009-04-24 13:52:30 +00:00
|
|
|
} else {
|
|
|
|
agl_factor = exp(-(2 + probe_elev_m[0] / 2000) *
|
|
|
|
(user_altitude_agl_m - boundary2_m) / max(probe_elev_m[0],200.0));
|
2009-04-20 14:20:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
double lift_mps = lift_factor* ground_wind_speed_mps * agl_factor;
|
|
|
|
|
|
|
|
//the updraft, finally, in ft per second
|
2009-04-24 13:52:30 +00:00
|
|
|
strength = fgGetLowPass( strength, lift_mps * SG_METER_TO_FEET, dt );
|
2009-04-26 09:18:13 +00:00
|
|
|
_ridge_lift_fps_node->setDoubleValue( strength );
|
2009-04-20 14:20:05 +00:00
|
|
|
}
|