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