1 /*
2 * CRRCsim - the Charles River Radio Control Club Flight Simulator Project
3 *
4 * Copyright (C) 2010 Joel Lienard (original author)
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2
8 * as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330,
18 * Boston, MA 02111-1307, USA.
19 *
20 */
21
22
23 /*************
24 * crrc_wind_from_terrain.cpp
25 *
26 * very simple wind calculation from terrain profil
27 *
28 *********/
29
30 /*
31 We make a modelling locally the ground by a simple shape.
32 That modelling is made from some points (2 - 10) in the vertical plane aligned with the wind.
33 The distance from these measurement points of the terrain is all the bigger as the height is big.
34 Mode 1: modelling by a plane
35 Mode 2: modelling by in-plane potential flow with 2D panel method
36 */
37
38 #include <crrc_config.h>
39
40 #include <plib/ssg.h>
41
42 #include "../global.h"
43 #include "../crrc_main.h"
44 #include "model_based_scenery.h"
45 #include "wind_from_terrain.h"
46
47
48 #define H_MIN 1. // minimum height above terrain for wind estimation (1 foot)
49
50 /*
51 data used by simple 2D panel method
52 */
53 #define N_UP_PTS 6 // number of up/down-stream points used
54 #define NPTS (2*N_UP_PTS) // total number of points defining terrain profile (panels)
55 #define NPAN (NPTS-1) // total number of panels
56 #define REF_L 300. // reference length to smooth terrain slope beyond land's end
57 #define REF_Z 100. // reference height from terrain
58 #define POW_Z 0.5 // power law to scale ratio of actual to reference height
59 #define BASELEN 0.5 // first panel length realtive to height from terrain
60 #define RATE 1.5 // panel length growth rate
61 #define EPS_END .1 // accuracy in defining streamwise position of land's end
62 #define IT_MAX 20 // max iter in defining streamwise position of land's end
63 #define DELTAX 10. // to compute terain slope at land's end
64
65 static float x[NPTS], z[NPTS]; // panel vertex coords
66 static float cx[NPAN], cz[NPAN]; // panel control point coords
67 static float cc[NPAN], ss[NPAN]; // panel cosine & sine
68 static float A[NPAN][NPAN]; // system matrix
69 static float src[NPAN]; // unknown sources
70
71 /**
72 * Compute normal velocity induced on control point of panel i
73 * by a uniform source strength distribution on panel j
74 *
75 */
vn_src0(int i,int j)76 static float vn_src0(int i, int j)
77 {
78 float xr1, zr1, xr2, zr2, rq1, rq2, b0, c0, d0, e0;
79
80 if (i == j)
81 {
82 return M_PI;
83 }
84 else
85 {
86 xr1 = cx[i] - x[j];
87 zr1 = cz[i] - z[j];
88 xr2 = cx[i] - x[j+1];
89 zr2 = cz[i] - z[j+1];
90 rq1 = xr1*xr1 + zr1*zr1;
91 rq2 = xr2*xr2 + zr2*zr2;
92 b0 = atan2(zr2*xr1 - xr2*zr1, xr2*xr1 + zr2*zr1);
93 c0 = ss[i]*cc[j] - cc[i]*ss[j];
94 d0 = cc[i]*cc[j] + ss[i]*ss[j];
95 e0 = .5*log(rq2/rq1);
96 return c0*e0 + d0*b0;
97 }
98 }
99
100 /**
101 * Compute velocity components vx,vz induced on point px,pz
102 * by a uniform source strength distribution on panel j.
103 *
104 */
v_src0(float px,float pz,int j,float * vx,float * vz)105 static void v_src0(float px, float pz, int j, float *vx, float *vz)
106 {
107 float xr1, zr1, xr2, zr2, rq1, rq2, b0, c0, d0, e0;
108
109 xr1 = px - x[j];
110 zr1 = pz - z[j];
111 xr2 = px - x[j+1];
112 zr2 = pz - z[j+1];
113 rq1 = xr1*xr1 + zr1*zr1;
114 rq2 = xr2*xr2 + zr2*zr2;
115 b0 = atan2(zr2*xr1 - xr2*zr1, xr2*xr1 + zr2*zr1);
116 c0 = -ss[j];
117 d0 = cc[j];
118 e0 = .5*log(rq2/rq1);
119 *vz = c0 * e0 + d0 * b0;
120 *vx = c0 * b0 - d0 * e0;
121 }
122
123 /**
124 * Solve linear system by Gauss elimination
125 *
126 */
solve_gs(float A[][NPAN],float x[],int n)127 static void solve_gs(float A[][NPAN], float x[], int n)
128 {
129 for(int k = 0; k < n-1; k++)
130 for(int i = n-1; i > k; i--)
131 {
132 A[i][k] /= A[k][k];
133 for(int j = n-1; j > k; j--)
134 A[i][j] -= A[k][j]*A[i][k];
135 }
136
137 for(int i = 1; i < n; i++)
138 for(int j = 0; j < i; j++)
139 x[i] -= A[i][j]*x[j];
140 x[n-1] /= A[n-1][n-1];
141 for(int i = n-2; i >= 0; i--)
142 {
143 for(int j = n-2; j > i; j--)
144 x[i] -= A[i][j]*x[j];
145 x[i] /= A[i][i];
146 }
147 }
148
wind_from_terrain(double X,double Y,double Z,float * x_wind_velocity,float * y_wind_velocity,float * z_wind_velocity)149 int wind_from_terrain(double X, double Y, double Z,
150 float *x_wind_velocity, float *y_wind_velocity, float *z_wind_velocity)
151 {
152 // freestream wind velocity
153 float flWindVel = cfg->wind->getVelocity();
154 float flWindDir = cfg->wind->getDirection()*M_PI/180.;
155 float flWindDirX = cos(flWindDir); //upstream versor
156 float flWindDirY = sin(flWindDir); //upstream versor
157
158 float z_c = Global::scenery->getHeight(X, Y); //terrain height below the point
159 float H = -Z; //positive down -> positive up
160 if ((H - z_c) < H_MIN)
161 H = z_c + H_MIN;
162 float dz = H - z_c;
163
164 // if is above a defined terrain, estimate wind speed from terrain
165 if (z_c > DEEPEST_HELL)
166 {
167 sgVec3 wind;
168 sgSetVec3(wind, 0, 0, 0);
169
170 if (Global::wind_mode == 1)
171 {
172 //
173 //We tilt the vector of wind along the slope, with the same speed in module
174 //
175 const float COEF = 0.5; //define angle of measure
176 float dx = COEF*dz*flWindDirX; //upstream vector
177 float dy = COEF*dz*flWindDirY; //upstream vector
178
179 float z_f = Global::scenery->getHeight(X+dx, Y+dy);//terrain height upstream
180 if (z_f==DEEPEST_HELL) { z_f = z_c;}
181 float z_b = Global::scenery->getHeight(X-dx, Y-dy);//terrain height downstream
182 if (z_b==DEEPEST_HELL) { z_b = z_c;}
183 sgVec3 p_c, p_f, p_b;
184 sgSetVec3(p_c, X, Y, -z_c);
185 sgSetVec3(p_f, X+dx, Y+dy, -z_f);
186 sgSetVec3(p_b, X-dx, Y-dy, -z_b);
187 //float z_l = Global::scenery->getHeight(X+dx, Y-dsin);//left
188 //float z_r = Global::scenery->getHeight(X-dx, Y+dy);//right
189 //sgVec3 p_0, p_l, p_r;
190 //sgSetVec3(p_0,X,Y,H);
191
192 sgVec3 dir;
193 sgSubVec3(dir, p_f, p_b);
194 sgNormaliseVec3(dir); //-> Unit vector in the direction of the wind
195 sgScaleVec3(wind, dir, -flWindVel);
196 }
197 else if (Global::wind_mode == 2)
198 {
199 //
200 //2D potential flow in a wind-aligned vertical plane
201 //
202
203 // define panels vertex points in a reference system with x axis aligned
204 // with wind direction (negative upstream) and origin on the point X,Y
205 if (dz < 0.1*REF_Z) dz = 0.1*REF_Z; // do not reduce ref length too much
206 float dd = 0.5*BASELEN*pow(dz/REF_Z,POW_Z)*REF_Z;
207 float ds = dd;
208 float d1 = 0.0;
209 float d2 = 0.0;
210 float dzdd1 = 0.0;
211 float dzdd2 = 0.0;
212 float z1 = DEEPEST_HELL;
213 float z2 = DEEPEST_HELL;
214 for(int i=1; i <= N_UP_PTS; i++)
215 {
216 float dx = ds*flWindDirX;
217 float dy = ds*flWindDirY;
218
219 x[N_UP_PTS-i] = -ds;
220 z[N_UP_PTS-i] = Global::scenery->getHeight(X+dx, Y+dy);
221 x[N_UP_PTS-1+i] = +ds;
222 z[N_UP_PTS-1+i] = Global::scenery->getHeight(X-dx, Y-dy);
223
224 // check if upwind point is outside defined terrain profile
225 // in case find terrain elevation and slope at upstream land's end
226 if (z[N_UP_PTS-i] == DEEPEST_HELL)
227 {
228 if (z1 == DEEPEST_HELL)
229 {
230 float xa = x[N_UP_PTS-i+1];
231 float za = z[N_UP_PTS-i+1];
232 float xb = x[N_UP_PTS-i];
233 float xc, zc;
234 int it = 0;
235 while ((fabs(xa - xb) > EPS_END) && it < IT_MAX)
236 {
237 it++;
238 xc = 0.5*(xa + xb);
239 zc = Global::scenery->getHeight(X-xc*flWindDirX, Y-xc*flWindDirY);
240 if (zc == DEEPEST_HELL)
241 xb = xc;
242 else
243 {
244 xa = xc;
245 za = zc;
246 }
247 }
248 d1 = xa;
249 z1 = za;
250 // estimate terrain slope at land's end
251 xc = xa + DELTAX;
252 zc = Global::scenery->getHeight(X-xc*flWindDirX, Y-xc*flWindDirY);
253 dzdd1 = (z1 - zc)/DELTAX;
254 }
255 z[N_UP_PTS-i] = z1 + dzdd1*REF_L*(1. - exp(-fabs(x[N_UP_PTS-i] - d1)/REF_L));
256 }
257 // check if downwind point is outside defined terrain profile
258 // in case find terrain elevation and slope at downstream land's end
259 if (z[N_UP_PTS-1+i] == DEEPEST_HELL)
260 {
261 if (z2 == DEEPEST_HELL)
262 {
263 float xa = x[N_UP_PTS-1+i-1];
264 float za = z[N_UP_PTS-1+i-1];
265 float xb = x[N_UP_PTS-1+i];
266 float xc, zc;
267 int it = 0;
268 while ((fabs(xa - xb) > EPS_END) && it < IT_MAX)
269 {
270 it++;
271 xc = 0.5*(xa + xb);
272 zc = Global::scenery->getHeight(X-xc*flWindDirX, Y-xc*flWindDirY);
273 if (zc == DEEPEST_HELL)
274 xb = xc;
275 else
276 {
277 xa = xc;
278 za = zc;
279 }
280 }
281 d2 = xa;
282 z2 = za;
283 // estimate terrain slope at land's end
284 xc = xa - DELTAX;
285 zc = Global::scenery->getHeight(X-xc*flWindDirX, Y-xc*flWindDirY);
286 dzdd2 = (z2 - zc)/DELTAX;
287 }
288 z[N_UP_PTS-1+i] = z2 + dzdd2*REF_L*(1. - exp(-fabs(x[N_UP_PTS-1+i] - d2)/REF_L));
289 }
290
291 dd *= (i == 1 ? 2. : 1.)*RATE;
292 ds += dd;
293 }
294
295 // compute panels geometry
296 for(int i = 0; i < NPAN; i++)
297 {
298 cx[i] = .5*(x[i+1] + x[i]);
299 cz[i] = .5*(z[i+1] + z[i]);
300 float lx = x[i+1] - x[i];
301 float lz = z[i+1] - z[i];
302 float ll = hypot(lx, lz);
303 ss[i] = lz/ll;
304 cc[i] = lx/ll;
305 }
306
307 // construct system matrix and source term
308 for(int i = 0; i < NPAN; i++)
309 {
310 src[i] = ss[i]; // freestream flow = horizontal wind
311 for( int j = 0; j < NPAN; j++ )
312 A[i][j] = vn_src0(i, j);
313 }
314
315 // solve system matrix for the unknown source/vortex strength
316 solve_gs(A, src, NPAN);
317
318 // compute velocity induced on target point
319 float vx = 1.; // freestream flow = horizontal wind
320 float vz = 0.; // freestream flow = horizontal wind
321 for(int i = 0; i < NPAN; i++)
322 {
323 float dvx, dvz;
324 v_src0(0., H, i, &dvx, &dvz); // induced (in plane) velocity on target point
325 vx += src[i]*dvx;
326 vz += src[i]*dvz;
327 }
328
329 // define resulting wind vector
330 sgSetVec3(wind, flWindDirX*vx, flWindDirY*vx, vz);
331 sgScaleVec3(wind, -flWindVel);
332 }
333
334 // return results
335 *x_wind_velocity = wind[0];
336 *y_wind_velocity = wind[1];
337 *z_wind_velocity = wind[2];
338
339 return 0;
340 }
341 else
342 {
343 // point is invalid
344 return 1;
345 }
346 }
347