]> git.mxchange.org Git - flightgear.git/blob - src/Environment/ridge_lift.cxx
- fill in probe[0] before using
[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 const double FGRidgeLift::dist_probe_m[] = { // in meters
61      0.0, 
62    250.0,
63    750.0,
64   2000.0,
65   -100.0
66 };
67
68 //constructor
69 FGRidgeLift::FGRidgeLift ()
70 {       
71         strength = 0.0;
72         timer = 0.0;
73         for( int i = 0; i < 5; i++ )
74                 probe_elev_m[i] = probe_lat_deg[i] = probe_lon_deg[i] = 0.0;
75
76         for( int i = 0; i < 4; i++ )
77                 slope[i] = 0.0;
78 }
79
80 //destructor
81 FGRidgeLift::~FGRidgeLift()
82 {
83
84 }
85
86 void FGRidgeLift::init(void)
87 {
88         _enabled_node = fgGetNode( "/environment/ridge-lift/enabled", false );
89
90         _ridge_lift_fps_node = fgGetNode("/environment/ridge-lift-fps", true);
91         _surface_wind_from_deg_node =
92                         fgGetNode("/environment/config/boundary/entry[0]/wind-from-heading-deg"
93                         , true);
94         _surface_wind_speed_node =
95                         fgGetNode("/environment/config/boundary/entry[0]/wind-speed-kt"
96                         , true);
97         _user_longitude_node = fgGetNode("/position/longitude-deg", true);
98         _user_latitude_node = fgGetNode("/position/latitude-deg", true);
99         _user_altitude_agl_ft_node = fgGetNode("/position/altitude-agl-ft", true);
100         _ground_elev_node = fgGetNode("/position/ground-elev-ft", true );
101 }
102
103 void FGRidgeLift::bind() {
104         string prop;
105
106         for( int i = 0; i < 5; i++ ) {
107                 prop = CreateIndexedPropertyName("/environment/ridge-lift/probe-elev-m", i );
108                 fgTie( prop.c_str(), this, i, &FGRidgeLift::get_probe_elev_m); // read-only
109
110                 prop = CreateIndexedPropertyName("/environment/ridge-lift/probe-lat-deg", i );
111                 fgTie( prop.c_str(), this, i, &FGRidgeLift::get_probe_lat_deg); // read-only
112
113                 prop = CreateIndexedPropertyName("/environment/ridge-lift/probe-lon-deg", i );
114                 fgTie( prop.c_str(), this, i, &FGRidgeLift::get_probe_lon_deg); // read-only
115         }
116
117         for( int i = 0; i < 4; i++ ) {
118                 prop = CreateIndexedPropertyName("/environment/ridge-lift/slope", i );
119                 fgTie( prop.c_str(), this, i, &FGRidgeLift::get_slope); // read-only
120         }
121 }
122
123 void FGRidgeLift::unbind() {
124         string prop;
125
126         for( int i = 0; i < 5; i++ ) {
127
128                 prop = CreateIndexedPropertyName("/environment/ridge-lift/probe-elev-m", i );
129                 fgUntie( prop.c_str() );
130
131                 prop = CreateIndexedPropertyName("/environment/ridge-lift/probe-lat-deg", i );
132                 fgUntie( prop.c_str() );
133
134                 prop = CreateIndexedPropertyName("/environment/ridge-lift/probe-lon-deg", i );
135                 fgUntie( prop.c_str() );
136         }
137
138         for( int i = 0; i < 4; i++ ) {
139                 prop = CreateIndexedPropertyName("/environment/ridge-lift/slope", i );
140                 fgUntie( prop.c_str() );
141         }
142 }
143
144 void FGRidgeLift::update(double dt) {
145
146         if( dt <= SGLimitsd::min() ) // paused, do nothing but keep current lift
147                 return;
148
149         if( _enabled_node && false == _enabled_node->getBoolValue() ) {
150                 // do nothing if lift has been zeroed
151                 if( strength != 0.0 ) {
152                         if( strength > 0.1 ) {
153                                 // slowly fade out strong lifts
154                                 strength = fgGetLowPass( strength, 0, dt );
155                         } else {
156                                 strength = 0.0;
157                         }
158                         _ridge_lift_fps_node->setDoubleValue( strength );
159                 }
160                 return;
161         }
162
163         timer -= dt;
164         if (timer <= 0.0 ) {
165
166                 // NOTE: this mixes geocentric and geodetic calculations which give mathematically incorrect results
167                 //       probably this is acceptable for calculating the ridge lift, converting from 
168                 //       geodetic to geocentric coordinates is somewhat expensive
169
170                 // probe0 is current position
171                 probe_lat_deg[0] = _user_latitude_node->getDoubleValue();
172                 probe_lon_deg[0] = _user_longitude_node->getDoubleValue();
173                 probe_elev_m[0]  = _ground_elev_node->getDoubleValue() * SG_FEET_TO_METER;
174
175                 SGGeoc myPosition = SGGeoc::fromDegM( probe_lon_deg[0], probe_lat_deg[0], 20000.0 );
176                 double ground_wind_from_rad = _surface_wind_from_deg_node->getDoubleValue() * SG_DEGREES_TO_RADIANS + SG_PI;
177
178                 // compute the remaining probes
179                 for (int i = 1; i < sizeof(probe_lat_deg)/sizeof(probe_lat_deg[0]); i++) {
180                         SGGeoc probe = myPosition.advanceRadM( ground_wind_from_rad, dist_probe_m[i] );
181                         probe_lat_deg[i] = probe.getLatitudeDeg();
182                         probe_lon_deg[i] = probe.getLongitudeDeg();
183                         if (!globals->get_scenery()->get_elevation_m(
184                                 SGGeod::fromDegM(probe_lon_deg[i],probe_lat_deg[i], 20000), probe_elev_m[i], NULL )) {
185                                 // no ground found? use elevation of previous probe :-(
186                                 probe_elev_m[i] = probe_elev_m[i-1];
187                         }
188                 }
189
190                 // slopes
191                 double adj_slope[sizeof(slope)];
192                 slope[0] = (probe_elev_m[0] - probe_elev_m[1]) / dist_probe_m[1];
193                 slope[1] = (probe_elev_m[1] - probe_elev_m[2]) / dist_probe_m[2];
194                 slope[2] = (probe_elev_m[2] - probe_elev_m[3]) / dist_probe_m[3];
195                 slope[3] = (probe_elev_m[4] - probe_elev_m[0]) / -dist_probe_m[4];
196         
197                 for (int i = 0; i < sizeof(slope)/sizeof(slope[0]); i++)
198                         adj_slope[i] = sin(atan(5.0 * pow ( (fabs(slope[i])),1.7) ) ) *sign(slope[i]);
199         
200                 //adjustment
201                 adj_slope[0] *= 0.2;
202                 adj_slope[1] *= 0.2;
203                 if ( adj_slope [2] < 0.0 ) {
204                         adj_slope[2] *= 0.5;
205                 } else {
206                         adj_slope[2] = 0.0 ;
207                 }
208         
209                 if ( ( adj_slope [0] >= 0.0 ) && ( adj_slope [3] < 0.0 ) ) {
210                         adj_slope[3] = 0.0;
211                 } else {
212                         adj_slope[3] *= 0.2;
213                 }
214                 lift_factor = adj_slope[0]+adj_slope[1]+adj_slope[2]+adj_slope[3];
215         
216                 // restart the timer
217                 timer = 1.0;
218         }
219         
220         //user altitude above ground
221         double user_altitude_agl_m = _user_altitude_agl_ft_node->getDoubleValue() * SG_FEET_TO_METER;
222         
223         //boundaries
224         double boundary2_m = 130.0; // in the lift
225         if (lift_factor < 0.0) { // in the sink
226                 double highest_probe_temp= max ( probe_elev_m[1], probe_elev_m[2] );
227                 double highest_probe_downwind_m= max ( highest_probe_temp, probe_elev_m[3] );
228                 boundary2_m = highest_probe_downwind_m - probe_elev_m[0];
229         }
230
231         double agl_factor;
232         if ( user_altitude_agl_m < BOUNDARY1_m ) {
233                 agl_factor = 0.5+0.5*user_altitude_agl_m /BOUNDARY1_m ;
234         } else if ( user_altitude_agl_m < boundary2_m ) {
235                 agl_factor = 1.0;
236         } else {
237                 agl_factor = exp(-(2 + probe_elev_m[0] / 2000) * 
238                                 (user_altitude_agl_m - boundary2_m) / max(probe_elev_m[0],200.0));
239         }
240         
241         double ground_wind_speed_mps = _surface_wind_speed_node->getDoubleValue() * SG_NM_TO_METER / 3600;
242         double lift_mps = lift_factor* ground_wind_speed_mps * agl_factor;
243         
244         //the updraft, finally, in ft per second
245         strength = fgGetLowPass( strength, lift_mps * SG_METER_TO_FEET, dt );
246         _ridge_lift_fps_node->setDoubleValue( strength );
247 }