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