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