]> git.mxchange.org Git - flightgear.git/blob - src/Environment/ridge_lift.cxx
just in case that someone does not like ridge lift at all, set the property
[flightgear.git] / src / Environment / ridge_lift.cxx
1 // simulates ridge lift
2 //
3 // Written by Patrice Poly
4 // Copyright (C) 2009 Patrice Poly - p.polypa@gmail.com
5 //
6 //
7 // Entirely based  on the paper : 
8 // http://carrier.csi.cam.ac.uk/forsterlewis/soaring/sim/fsx/dev/sim_probe/sim_probe_paper.html
9 // by Ian Forster-Lewis, University of Cambridge, 26th December 2007
10 //
11 //
12 // This program is free software; you can redistribute it and/or
13 // modify it under the terms of the GNU General Public License as
14 // published by the Free Software Foundation; either version 2 of the
15 // License, or (at your option) any later version.
16 //
17 // This program is distributed in the hope that it will be useful, but
18 // WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20 // General Public License for more details.
21 //
22 // You should have received a copy of the GNU General Public License
23 // along with this program; if not, write to the Free Software
24 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
25 //
26 //
27
28
29 #ifdef HAVE_CONFIG_H
30 #  include <config.h>
31 #endif
32
33 #include <Main/fg_props.hxx>
34 #include <Main/globals.hxx>
35 #include <Main/util.hxx>
36 #include <Scenery/scenery.hxx>
37 #include <string>
38 #include <math.h>
39
40
41 using std::string;
42
43 #include "ridge_lift.hxx"
44
45 static string CreateIndexedPropertyName(string Property, int index)
46 {
47         std::stringstream str;
48         str << index;
49         string tmp;
50         str >> tmp;
51         return Property + "[" + tmp + "]";
52 }
53
54 static inline double sign(double x) {
55         return x == 0 ? 0 : x > 0 ? 1.0 : -1.0;
56 }
57
58 static const    double BOUNDARY1_m = 40.0;
59
60 //constructor
61 FGRidgeLift::FGRidgeLift ()
62 {       
63         dist_probe_m[0] = 0.0; // in meters
64         dist_probe_m[1] = 250.0;
65         dist_probe_m[2] = 750.0;
66         dist_probe_m[3] = 2000.0;
67         dist_probe_m[4] = -100.0;
68
69         strength = 0.0;
70         timer = 0.0;
71         for( int i = 0; i < 5; i++ )
72                 probe_elev_m[i] = probe_lat_deg[i] = probe_lon_deg[i] = 0.0;
73
74         for( int i = 0; i < 4; i++ )
75                 slope[i] = 0.0;
76 }
77
78 //destructor
79 FGRidgeLift::~FGRidgeLift()
80 {
81
82 }
83
84 void FGRidgeLift::init(void)
85 {
86         _enabled_node = fgGetNode( "/environment/ridge-lift/enabled", false );
87
88         _ridge_lift_fps_node = fgGetNode("/environment/ridge-lift-fps", true);
89         _surface_wind_from_deg_node =
90                         fgGetNode("/environment/config/boundary/entry[0]/wind-from-heading-deg"
91                         , true);
92         _surface_wind_speed_node =
93                         fgGetNode("/environment/config/boundary/entry[0]/wind-speed-kt"
94                         , true);
95         _earth_radius_node = fgGetNode("/position/sea-level-radius-ft", true);
96         _user_longitude_node = fgGetNode("/position/longitude-deg", true);
97         _user_latitude_node = fgGetNode("/position/latitude-deg", true);
98         _user_altitude_ft_node = fgGetNode("/position/altitude-ft", true);
99         _user_altitude_agl_ft_node = fgGetNode("/position/altitude-agl-ft", true);
100 }
101
102 void FGRidgeLift::bind() {
103         string prop;
104
105         for( int i = 0; i < 5; i++ ) {
106                 prop = CreateIndexedPropertyName("/environment/ridge-lift/probe-elev-m", i );
107                 fgTie( prop.c_str(), this, i, &FGRidgeLift::get_probe_elev_m); // read-only
108
109                 prop = CreateIndexedPropertyName("/environment/ridge-lift/probe-lat-deg", i );
110                 fgTie( prop.c_str(), this, i, &FGRidgeLift::get_probe_lat_deg); // read-only
111
112                 prop = CreateIndexedPropertyName("/environment/ridge-lift/probe-lon-deg", i );
113                 fgTie( prop.c_str(), this, i, &FGRidgeLift::get_probe_lon_deg); // read-only
114         }
115
116         for( int i = 0; i < 4; i++ ) {
117                 prop = CreateIndexedPropertyName("/environment/ridge-lift/slope", i );
118                 fgTie( prop.c_str(), this, i, &FGRidgeLift::get_slope); // read-only
119         }
120 }
121
122 void FGRidgeLift::unbind() {
123         string prop;
124
125         for( int i = 0; i < 5; i++ ) {
126
127                 prop = CreateIndexedPropertyName("/environment/ridge-lift/probe-elev-m", i );
128                 fgUntie( prop.c_str() );
129
130                 prop = CreateIndexedPropertyName("/environment/ridge-lift/probe-lat-deg", i );
131                 fgUntie( prop.c_str() );
132
133                 prop = CreateIndexedPropertyName("/environment/ridge-lift/probe-lon-deg", i );
134                 fgUntie( prop.c_str() );
135         }
136
137         for( int i = 0; i < 4; i++ ) {
138                 prop = CreateIndexedPropertyName("/environment/ridge-lift/slope", i );
139                 fgUntie( prop.c_str() );
140         }
141 }
142
143 void FGRidgeLift::update(double dt) {
144
145         if( _enabled_node && false == _enabled_node->getBoolValue() ) {
146                 if( strength != 0.0 ) {
147                         strength = 0.0;
148                         _ridge_lift_fps_node->setDoubleValue( 0 );
149                 }
150                 return;
151         }
152
153         //get the windspeed at ground level
154         double ground_wind_from_rad = _surface_wind_from_deg_node->getDoubleValue() * SG_DEGREES_TO_RADIANS;
155         double ground_wind_speed_mps = _surface_wind_speed_node->getDoubleValue() * SG_NM_TO_METER / 3600;
156
157         timer -= dt;
158         if (timer <= 0.0 ) {
159                 // copy values 
160
161                 double user_latitude_rad = _user_latitude_node->getDoubleValue() * SG_DEGREES_TO_RADIANS;
162                 double user_longitude_rad = _user_longitude_node->getDoubleValue() * SG_DEGREES_TO_RADIANS;
163         
164                 double earth_rad_m = _earth_radius_node->getDoubleValue() * SG_FEET_TO_METER;
165                 if( earth_rad_m < SG_EPSILON )
166                                 earth_rad_m = SG_EARTH_RAD * 1000;
167
168                 // Placing the probes
169         
170                 for (int i = 0; i < sizeof(probe_lat_rad)/sizeof(probe_lat_rad[0]); i++) {
171                         double probe_radius_ratio = dist_probe_m[i]/earth_rad_m;
172                         double sin_probe_radius_ratio = sin(probe_radius_ratio);
173
174                         probe_lat_rad[i] = asin(sin(user_latitude_rad)*cos(probe_radius_ratio)
175                                         +cos(user_latitude_rad)*sin_probe_radius_ratio*cos(ground_wind_from_rad));
176                         if (probe_lat_rad[i] < SG_EPSILON ) {
177                                 probe_lon_rad[i] = user_latitude_rad; // probe on a pole        
178                         } else {
179                                 probe_lon_rad[i] = fmod((user_longitude_rad+asin(sin(ground_wind_from_rad)
180                                                         *sin_probe_radius_ratio/cos(probe_lat_rad[i]))+SG_PI)
181                                                 ,SGD_2PI)-SG_PI;
182                         }
183                         probe_lat_deg[i]= probe_lat_rad[i] * SG_RADIANS_TO_DEGREES;
184                         probe_lon_deg[i]= probe_lon_rad[i] * SG_RADIANS_TO_DEGREES;
185                 }
186         
187                 for (int i = 0; i < sizeof(probe_elev_m)/sizeof(probe_elev_m[0]); i++) {
188                         double elevation = 0;
189                         if (globals->get_scenery()->get_elevation_m(SGGeod::fromGeodM(
190                                 SGGeod::fromRad(probe_lon_rad[i],probe_lat_rad[i]), 20000), elevation, 0)) {
191                                 if ( elevation > 0.1 ) { 
192                                         probe_elev_m[i] = elevation; 
193                                 } else { 
194                                         probe_elev_m[i] = 0.1 ;
195                                 }
196                         } else { 
197                                 probe_elev_m[i] = 0.1;
198                         }
199                 }
200
201                 // slopes
202                 double adj_slope[4];
203                 slope[0] = (probe_elev_m[0] - probe_elev_m[1]) / dist_probe_m[1];
204                 slope[1] = (probe_elev_m[1] - probe_elev_m[2]) / dist_probe_m[2];
205                 slope[2] = (probe_elev_m[2] - probe_elev_m[3]) / dist_probe_m[3];
206                 slope[3] = (probe_elev_m[4] - probe_elev_m[0]) / -dist_probe_m[4];
207         
208                 for (int i = 0; i < sizeof(slope)/sizeof(slope[0]); i++)
209                         adj_slope[i] = sin(atan(5.0 * pow ( (fabs(slope[i])),1.7) ) ) *sign(slope[i]);
210         
211                 //adjustment
212                 adj_slope[0] *= 0.2;
213                 adj_slope[1] *= 0.2;
214                 if ( adj_slope [2] < 0.0 ) {
215                         adj_slope[2] *= 0.5;
216                 } else {
217                         adj_slope[2] = 0.0 ;
218                 }
219         
220                 if ( ( adj_slope [0] >= 0.0 ) && ( adj_slope [3] < 0.0 ) ) {
221                         adj_slope[3] = 0.0;
222                 } else {
223                         adj_slope[3] *= 0.2;
224                 }
225                 lift_factor = adj_slope[0]+adj_slope[1]+adj_slope[2]+adj_slope[3];
226         
227                 // restart the timer
228                 timer = 1.0;
229         }
230         
231         //user altitude above ground
232         double user_altitude_agl_m = _user_altitude_agl_ft_node->getDoubleValue() * SG_FEET_TO_METER;
233         
234         //boundaries
235         double boundary2_m = 130.0; // in the lift
236         if (lift_factor < 0.0) { // in the sink
237                 double highest_probe_temp= max ( probe_elev_m[1], probe_elev_m[2] );
238                 double highest_probe_downwind_m= max ( highest_probe_temp, probe_elev_m[3] );
239                 boundary2_m = highest_probe_downwind_m - probe_elev_m[0];
240         }
241
242         double agl_factor;
243         if ( user_altitude_agl_m < BOUNDARY1_m ) {
244                 agl_factor = 0.5+0.5*user_altitude_agl_m /BOUNDARY1_m ;
245         } else if ( user_altitude_agl_m < boundary2_m ) {
246                 agl_factor = 1.0;
247         } else {
248                 agl_factor = exp(-(2 + probe_elev_m[0] / 2000) * 
249                                 (user_altitude_agl_m - boundary2_m) / max(probe_elev_m[0],200.0));
250         }
251         
252         double lift_mps = lift_factor* ground_wind_speed_mps * agl_factor;
253         
254         //the updraft, finally, in ft per second
255         strength = fgGetLowPass( strength, lift_mps * SG_METER_TO_FEET, dt );
256         _ridge_lift_fps_node->setDoubleValue( strength );
257 }