]> git.mxchange.org Git - flightgear.git/blob - src/FDM/YASim/Wing.cpp
Latest YASim changes.
[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::compile()
212 {
213     // Have we already been compiled?
214     if(_surfs.size() != 0) return;
215
216     // Assemble the start/end coordinates into an array, sort them,
217     // and remove duplicates.  This gives us the boundaries of our
218     // segments.
219     float bounds[8];
220     bounds[0] = _flap0Start;   bounds[1] = _flap0End;
221     bounds[2] = _flap1Start;   bounds[3] = _flap1End;
222     bounds[4] = _spoilerStart; bounds[5] = _spoilerEnd;
223     bounds[6] = _slatStart;    bounds[7] = _slatEnd;
224
225     // Sort in increasing order
226     int i;
227     for(i=0; i<8; i++) {
228         int minIdx = i;
229         float minVal = bounds[i];
230         int j;
231         for(j=i+1; j<8; j++) {
232             if(bounds[j] < minVal) {
233                 minIdx = j;
234                 minVal = bounds[j];
235             }
236         }
237         float tmp = bounds[i];
238         bounds[i] = minVal; bounds[minIdx] = tmp;
239     }
240                                   
241     // Uniqify
242     float last = bounds[0];
243     int nbounds = 1;
244     for(i=1; i<8; i++) {
245         if(bounds[i] != last)
246             bounds[nbounds++] = bounds[i];
247         last = bounds[i];
248     }
249
250     // Calculate a "nominal" segment length equal to an average chord,
251     // normalized to lie within 0-1 over the length of the wing.
252     float segLen = _chord * (0.5*(_taper+1)) / _length;
253
254     // Generating a unit vector pointing out the left wing.
255     float left[3];
256     left[0] = -Math::tan(_sweep);
257     left[1] = Math::cos(_dihedral);
258     left[2] = Math::sin(_dihedral);
259     Math::unit3(left, left);
260
261     // Calculate coordinates for the root and tip of the wing
262     float root[3], tip[3];
263     Math::set3(_base, root);
264     Math::set3(left, tip);
265     Math::mul3(_length, tip, tip);
266     Math::add3(root, tip, tip);
267
268     // The wing's Y axis will be the "left" vector.  The Z axis will
269     // be perpendicular to this and the local (!) X axis, because we
270     // want motion along the local X axis to be zero AoA (i.e. in the
271     // wing's XY plane) by definition.  Then the local X coordinate is
272     // just Y cross Z.
273     float orient[9], rightOrient[9];
274     float *x = orient, *y = orient+3, *z = orient+6;
275     x[0] = 1; x[1] = 0; x[2] = 0;
276     Math::set3(left, y);
277     Math::cross3(x, y, z);
278     Math::unit3(z, z);
279     Math::cross3(y, z, x);
280
281     if(_mirror) {
282         // Derive the right side orientation matrix from this one.
283         int i;
284         for(i=0; i<9; i++)  rightOrient[i] = orient[i];
285
286         // Negate all Y coordinates, this gets us a valid basis, but
287         // it's left handed!  So...
288         for(i=1; i<9; i+=3) rightOrient[i] = -rightOrient[i];
289
290         // Change the direction of the Y axis to get back to a
291         // right-handed system.
292         for(i=3; i<6; i++)  rightOrient[i] = -rightOrient[i];
293     }
294
295     // Now go through each boundary and make segments
296     for(i=0; i<(nbounds-1); i++) {
297         float start = bounds[i];
298         float end = bounds[i+1];
299         float mid = (start+end)/2;
300
301         bool flap0=0, flap1=0, slat=0, spoiler=0;
302         if(_flap0Start   < mid && mid < _flap0End)   flap0 = 1;
303         if(_flap1Start   < mid && mid < _flap1End)   flap1 = 1;
304         if(_slatStart    < mid && mid < _slatEnd)    slat = 1;
305         if(_spoilerStart < mid && mid < _spoilerEnd) spoiler = 1;
306
307         // FIXME: Should probably detect an error here if both flap0
308         // and flap1 are set.  Right now flap1 overrides.
309
310         int nSegs = (int)Math::ceil((end-start)/segLen);
311         float segWid = _length * (end - start)/nSegs;
312
313         int j;
314         for(j=0; j<nSegs; j++) {
315             float frac = start + (j+0.5) * (end-start)/nSegs;
316             float pos[3];
317             interp(root, tip, frac, pos);
318
319             float chord = _chord * (1 - (1-_taper)*frac);
320
321             Surface *s = newSurface(pos, orient, chord,
322                                     flap0, flap1, slat, spoiler);
323
324             SurfRec *sr = new SurfRec();
325             sr->surface = s;
326             sr->weight = chord * segWid;
327             s->setTotalDrag(sr->weight);
328             _surfs.add(sr);
329
330             if(_mirror) {
331                 pos[1] = -pos[1];
332                 s = newSurface(pos, rightOrient, chord,
333                                flap0, flap1, slat, spoiler);
334                 sr = new SurfRec();
335                 sr->surface = s;
336                 sr->weight = chord * segWid;
337                 s->setTotalDrag(sr->weight);
338                 _surfs.add(sr);
339             }
340         }
341     }
342 }
343
344 float Wing::getDragScale()
345 {
346     return _dragScale;
347 }
348
349 void Wing::setDragScale(float scale)
350 {
351     _dragScale = scale;
352     int i;
353     for(i=0; i<_surfs.size(); i++) {
354         SurfRec* s = (SurfRec*)_surfs.get(i);
355         s->surface->setTotalDrag(scale * s->weight);
356     }
357 }
358
359 void Wing::setLiftRatio(float ratio)
360 {
361     _liftRatio = ratio;
362     int i;
363     for(i=0; i<_surfs.size(); i++)
364         ((SurfRec*)_surfs.get(i))->surface->setZDrag(ratio);
365 }
366
367 float Wing::getLiftRatio()
368 {
369     return _liftRatio;
370 }
371
372 Surface* Wing::newSurface(float* pos, float* orient, float chord,
373                           bool flap0, bool flap1, bool slat, bool spoiler)
374 {
375     Surface* s = new Surface();
376
377     s->setPosition(pos);
378     s->setOrientation(orient);
379     s->setChord(chord);
380
381     // Camber is expressed as a fraction of stall peak, so convert.
382     s->setBaseZDrag(_camber*_stallPeak);
383
384     // The "main" (i.e. normal) stall angle
385     float stallAoA = _stall - _stallWidth/4;
386     s->setStall(0, stallAoA);
387     s->setStallWidth(0, _stallWidth);
388     s->setStallPeak(0, _stallPeak);
389
390     // The negative AoA stall is the same if we're using an uncambered
391     // airfoil, otherwise a "little badder".
392     if(_camber > 0) {
393         s->setStall(1, stallAoA * 0.8);
394         s->setStallWidth(1, _stallWidth * 0.5);
395     } else {
396         s->setStall(1, stallAoA);
397         s->setStall(1, _stallWidth);
398     }
399
400     // The "reverse" stalls are unmeasurable junk.  Just use 13deg and
401     // "sharp".
402     s->setStallPeak(1, 1);
403     int i;
404     for(i=2; i<4; i++) {
405         s->setStall(i, 0.2267);
406         s->setStallWidth(i, 1);
407     }
408     
409     if(flap0)   s->setFlapParams(_flap0Lift, _flap0Drag);
410     if(flap1)   s->setFlapParams(_flap1Lift, _flap1Drag);
411     if(slat)    s->setSlatParams(_slatAoA, _slatDrag);
412     if(spoiler) s->setSpoilerParams(_spoilerLift, _spoilerDrag);    
413
414     if(flap0)   _flap0Surfs.add(s);
415     if(flap1)   _flap1Surfs.add(s);
416     if(slat)    _slatSurfs.add(s);
417     if(spoiler) _spoilerSurfs.add(s);
418
419     return s;
420 }
421
422 void Wing::interp(float* v1, float* v2, float frac, float* out)
423 {
424     out[0] = v1[0] + frac*(v2[0]-v1[0]);
425     out[1] = v1[1] + frac*(v2[1]-v1[1]);
426     out[2] = v1[2] + frac*(v2[2]-v1[2]);
427 }
428
429 }; // namespace yasim