]> git.mxchange.org Git - flightgear.git/blob - src/FDM/YASim/Wing.cpp
Use the density values from the environment subsystem, to properly handle
[flightgear.git] / src / FDM / YASim / Wing.cpp
1 #include "Math.hpp"
2 #include "Surface.hpp"
3 #include "Wing.hpp"
4 namespace yasim {
5
6 Wing::Wing()
7 {
8     _mirror = false;
9     _base[0] = _base[1] = _base[2] = 0;
10     _length = 0;
11     _chord = 0;
12     _taper = 0;
13     _sweep = 0;
14     _dihedral = 0;
15     _stall = 0;
16     _stallWidth = 0;
17     _stallPeak = 0;
18     _camber = 0;
19     _incidence = 0;
20     _dragScale = 1;
21     _liftRatio = 1;
22     _flap0Start = 0;
23     _flap0End = 0;
24     _flap0Lift = 0;
25     _flap0Drag = 0;
26     _flap1Start = 0;
27     _flap1End = 0;
28     _flap1Lift = 0;
29     _flap1Drag = 0;
30     _spoilerStart = 0;
31     _spoilerEnd = 0;
32     _spoilerLift = 0;
33     _spoilerDrag = 0;
34     _slatStart = 0;
35     _slatEnd = 0;
36     _slatAoA = 0;
37     _slatDrag = 0;
38 }
39
40 Wing::~Wing()
41 {
42     int i;
43     for(i=0; i<_surfs.size(); i++) {
44         SurfRec* s = (SurfRec*)_surfs.get(i);
45         delete s->surface;
46         delete s;
47     }
48 }
49
50 int Wing::numSurfaces()
51 {
52     return _surfs.size();
53 }
54
55 Surface* Wing::getSurface(int n)
56 {
57     return ((SurfRec*)_surfs.get(n))->surface;
58 }
59
60 float Wing::getSurfaceWeight(int n)
61 {
62     return ((SurfRec*)_surfs.get(n))->weight;
63 }
64
65 void Wing::setMirror(bool mirror)
66 {
67     _mirror = mirror;
68 }
69
70 void Wing::setBase(float* base)
71 {
72     int i;
73     for(i=0; i<3; i++) _base[i] = base[i];
74 }
75
76 void Wing::setLength(float length)
77 {
78     _length = length;
79 }
80
81 void Wing::setChord(float chord)
82 {
83     _chord = chord;
84 }
85
86 void Wing::setTaper(float taper)
87 {
88     _taper = taper;
89 }
90
91 void Wing::setSweep(float sweep)
92 {
93     _sweep = sweep;
94 }
95
96 void Wing::setDihedral(float dihedral)
97 {
98     _dihedral = dihedral;
99 }
100
101 void Wing::setStall(float aoa)
102 {
103     _stall = aoa;
104 }
105
106 void Wing::setStallWidth(float angle)
107 {
108     _stallWidth = angle;
109 }
110
111 void Wing::setStallPeak(float fraction)
112 {
113     _stallPeak = fraction;
114 }
115
116 void Wing::setCamber(float camber)
117 {
118     _camber = camber;
119 }
120
121 void Wing::setIncidence(float incidence)
122 {
123     _incidence = incidence;
124     int i;
125     for(i=0; i<_surfs.size(); i++)
126         ((SurfRec*)_surfs.get(i))->surface->setIncidence(incidence);
127 }
128
129 void Wing::setFlap0(float start, float end, float lift, float drag)
130 {
131     _flap0Start = start;
132     _flap0End = end;
133     _flap0Lift = lift;
134     _flap0Drag = drag;
135 }
136
137 void Wing::setFlap1(float start, float end, float lift, float drag)
138 {
139     _flap1Start = start;
140     _flap1End = end;
141     _flap1Lift = lift;
142     _flap1Drag = drag;
143 }
144
145 void Wing::setSlat(float start, float end, float aoa, float drag)
146 {
147     _slatStart = start;
148     _slatEnd = end;
149     _slatAoA = aoa;
150     _slatDrag = drag;
151 }
152
153 void Wing::setSpoiler(float start, float end, float lift, float drag)
154 {
155     _spoilerStart = start;
156     _spoilerEnd = end;
157     _spoilerLift = lift;
158     _spoilerDrag = drag;
159 }
160
161 void Wing::setFlap0(float lval, float rval)
162 {
163     lval = Math::clamp(lval, -1, 1);
164     rval = Math::clamp(rval, -1, 1);
165     int i;
166     for(i=0; i<_flap0Surfs.size(); i++) {
167         ((Surface*)_flap0Surfs.get(i))->setFlap(lval);
168         if(_mirror) ((Surface*)_flap0Surfs.get(++i))->setFlap(rval);
169     }
170 }
171
172 void Wing::setFlap1(float lval, float rval)
173 {
174     lval = Math::clamp(lval, -1, 1);
175     rval = Math::clamp(rval, -1, 1);
176     int i;
177     for(i=0; i<_flap1Surfs.size(); i++) {
178         ((Surface*)_flap1Surfs.get(i))->setFlap(lval);
179         if(_mirror) ((Surface*)_flap1Surfs.get(++i))->setFlap(rval);
180     }
181 }
182
183 void Wing::setSpoiler(float lval, float rval)
184 {
185     lval = Math::clamp(lval, 0, 1);
186     rval = Math::clamp(rval, 0, 1);
187     int i;
188     for(i=0; i<_spoilerSurfs.size(); i++) {
189         ((Surface*)_spoilerSurfs.get(i))->setSpoiler(lval);
190         if(_mirror) ((Surface*)_spoilerSurfs.get(++i))->setSpoiler(rval);
191     }
192 }
193
194 void Wing::setSlat(float val)
195 {
196     val = Math::clamp(val, 0, 1);
197     int i;
198     for(i=0; i<_slatSurfs.size(); i++)
199         ((Surface*)_slatSurfs.get(i))->setSlat(val);
200 }
201
202 float Wing::getGroundEffect(float* posOut)
203 {
204     int i;
205     for(i=0; i<3; i++) posOut[i] = _base[i];
206     float span = _length * Math::cos(_sweep) * Math::cos(_dihedral);
207     span = 2*(span + Math::abs(_base[2]));
208     return span;
209 }
210
211 void Wing::getTip(float* tip)
212 {
213     tip[0] = -Math::tan(_sweep);
214     tip[1] = Math::cos(_dihedral);
215     tip[2] = Math::sin(_dihedral);
216     Math::unit3(tip, tip);
217     Math::mul3(_length, tip, tip);
218     Math::add3(_base, tip, tip);
219 }
220
221 bool Wing::isMirrored()
222 {
223     return _mirror;
224 }
225
226 void Wing::compile()
227 {
228     // Have we already been compiled?
229     if(_surfs.size() != 0) return;
230
231     // Assemble the start/end coordinates into an array, sort them,
232     // and remove duplicates.  This gives us the boundaries of our
233     // segments.
234     float bounds[8];
235     bounds[0] = _flap0Start;   bounds[1] = _flap0End;
236     bounds[2] = _flap1Start;   bounds[3] = _flap1End;
237     bounds[4] = _spoilerStart; bounds[5] = _spoilerEnd;
238     bounds[6] = _slatStart;    bounds[7] = _slatEnd;
239
240     // Sort in increasing order
241     int i;
242     for(i=0; i<8; i++) {
243         int minIdx = i;
244         float minVal = bounds[i];
245         int j;
246         for(j=i+1; j<8; j++) {
247             if(bounds[j] < minVal) {
248                 minIdx = j;
249                 minVal = bounds[j];
250             }
251         }
252         float tmp = bounds[i];
253         bounds[i] = minVal; bounds[minIdx] = tmp;
254     }
255                                   
256     // Uniqify
257     float last = bounds[0];
258     int nbounds = 1;
259     for(i=1; i<8; i++) {
260         if(bounds[i] != last)
261             bounds[nbounds++] = bounds[i];
262         last = bounds[i];
263     }
264
265     // Calculate a "nominal" segment length equal to an average chord,
266     // normalized to lie within 0-1 over the length of the wing.
267     float segLen = _chord * (0.5f*(_taper+1)) / _length;
268
269     // Generating a unit vector pointing out the left wing.
270     float left[3];
271     left[0] = -Math::tan(_sweep);
272     left[1] = Math::cos(_dihedral);
273     left[2] = Math::sin(_dihedral);
274     Math::unit3(left, left);
275
276     // Calculate coordinates for the root and tip of the wing
277     float root[3], tip[3];
278     Math::set3(_base, root);
279     Math::set3(left, tip);
280     Math::mul3(_length, tip, tip);
281     Math::add3(root, tip, tip);
282
283     // The wing's Y axis will be the "left" vector.  The Z axis will
284     // be perpendicular to this and the local (!) X axis, because we
285     // want motion along the local X axis to be zero AoA (i.e. in the
286     // wing's XY plane) by definition.  Then the local X coordinate is
287     // just Y cross Z.
288     float orient[9], rightOrient[9];
289     float *x = orient, *y = orient+3, *z = orient+6;
290     x[0] = 1; x[1] = 0; x[2] = 0;
291     Math::set3(left, y);
292     Math::cross3(x, y, z);
293     Math::unit3(z, z);
294     Math::cross3(y, z, x);
295
296     if(_mirror) {
297         // Derive the right side orientation matrix from this one.
298         int i;
299         for(i=0; i<9; i++)  rightOrient[i] = orient[i];
300
301         // Negate all Y coordinates, this gets us a valid basis, but
302         // it's left handed!  So...
303         for(i=1; i<9; i+=3) rightOrient[i] = -rightOrient[i];
304
305         // Change the direction of the Y axis to get back to a
306         // right-handed system.
307         for(i=3; i<6; i++)  rightOrient[i] = -rightOrient[i];
308     }
309
310     // Now go through each boundary and make segments
311     for(i=0; i<(nbounds-1); i++) {
312         float start = bounds[i];
313         float end = bounds[i+1];
314         float mid = (start+end)/2;
315
316         bool flap0=0, flap1=0, slat=0, spoiler=0;
317         if(_flap0Start   < mid && mid < _flap0End)   flap0 = 1;
318         if(_flap1Start   < mid && mid < _flap1End)   flap1 = 1;
319         if(_slatStart    < mid && mid < _slatEnd)    slat = 1;
320         if(_spoilerStart < mid && mid < _spoilerEnd) spoiler = 1;
321
322         // FIXME: Should probably detect an error here if both flap0
323         // and flap1 are set.  Right now flap1 overrides.
324
325         int nSegs = (int)Math::ceil((end-start)/segLen);
326         float segWid = _length * (end - start)/nSegs;
327
328         int j;
329         for(j=0; j<nSegs; j++) {
330             float frac = start + (j+0.5f) * (end-start)/nSegs;
331             float pos[3];
332             interp(root, tip, frac, pos);
333
334             float chord = _chord * (1 - (1-_taper)*frac);
335
336             Surface *s = newSurface(pos, orient, chord,
337                                     flap0, flap1, slat, spoiler);
338
339             SurfRec *sr = new SurfRec();
340             sr->surface = s;
341             sr->weight = chord * segWid;
342             s->setTotalDrag(sr->weight);
343             _surfs.add(sr);
344
345             if(_mirror) {
346                 pos[1] = -pos[1];
347                 s = newSurface(pos, rightOrient, chord,
348                                flap0, flap1, slat, spoiler);
349                 sr = new SurfRec();
350                 sr->surface = s;
351                 sr->weight = chord * segWid;
352                 s->setTotalDrag(sr->weight);
353                 _surfs.add(sr);
354             }
355         }
356     }
357
358     // Last of all, re-set the incidence in case setIncidence() was
359     // called before we were compiled.
360     setIncidence(_incidence);
361 }
362
363 float Wing::getDragScale()
364 {
365     return _dragScale;
366 }
367
368 void Wing::setDragScale(float scale)
369 {
370     _dragScale = scale;
371     int i;
372     for(i=0; i<_surfs.size(); i++) {
373         SurfRec* s = (SurfRec*)_surfs.get(i);
374         s->surface->setTotalDrag(scale * s->weight);
375     }
376 }
377
378 void Wing::setLiftRatio(float ratio)
379 {
380     _liftRatio = ratio;
381     int i;
382     for(i=0; i<_surfs.size(); i++)
383         ((SurfRec*)_surfs.get(i))->surface->setZDrag(ratio);
384 }
385
386 float Wing::getLiftRatio()
387 {
388     return _liftRatio;
389 }
390
391 Surface* Wing::newSurface(float* pos, float* orient, float chord,
392                           bool flap0, bool flap1, bool slat, bool spoiler)
393 {
394     Surface* s = new Surface();
395
396     s->setPosition(pos);
397     s->setOrientation(orient);
398     s->setChord(chord);
399
400     // Camber is expressed as a fraction of stall peak, so convert.
401     s->setBaseZDrag(_camber*_stallPeak);
402
403     // The "main" (i.e. normal) stall angle
404     float stallAoA = _stall - _stallWidth/4;
405     s->setStall(0, stallAoA);
406     s->setStallWidth(0, _stallWidth);
407     s->setStallPeak(0, _stallPeak);
408
409     // The negative AoA stall is the same if we're using an uncambered
410     // airfoil, otherwise a "little badder".
411     if(_camber > 0) {
412         s->setStall(1, stallAoA * 0.8f);
413         s->setStallWidth(1, _stallWidth * 0.5f);
414     } else {
415         s->setStall(1, stallAoA);
416         s->setStall(1, _stallWidth);
417     }
418
419     // The "reverse" stalls are unmeasurable junk.  Just use 13deg and
420     // "sharp".
421     s->setStallPeak(1, 1);
422     int i;
423     for(i=2; i<4; i++) {
424         s->setStall(i, 0.2267f);
425         s->setStallWidth(i, 1);
426     }
427     
428     if(flap0)   s->setFlapParams(_flap0Lift, _flap0Drag);
429     if(flap1)   s->setFlapParams(_flap1Lift, _flap1Drag);
430     if(slat)    s->setSlatParams(_slatAoA, _slatDrag);
431     if(spoiler) s->setSpoilerParams(_spoilerLift, _spoilerDrag);    
432
433     if(flap0)   _flap0Surfs.add(s);
434     if(flap1)   _flap1Surfs.add(s);
435     if(slat)    _slatSurfs.add(s);
436     if(spoiler) _spoilerSurfs.add(s);
437
438     return s;
439 }
440
441 void Wing::interp(float* v1, float* v2, float frac, float* out)
442 {
443     out[0] = v1[0] + frac*(v2[0]-v1[0]);
444     out[1] = v1[1] + frac*(v2[1]-v1[1]);
445     out[2] = v1[2] + frac*(v2[2]-v1[2]);
446 }
447
448 }; // namespace yasim