1#include "yasim-common.hpp"
8Wing::Wing(Version *ver,
bool mirror) :
17 for (
int s=0; s < _sections.size(); s++){
18 ws = (WingSection*)_sections.get(s);
19 for(
int i=0;
i<ws->_surfs.size();
i++) {
20 SurfRec* s = (SurfRec*)ws->_surfs.get(
i);
27int Wing::addWingSection(
float* base,
float chord,
float wingLength,
float taper,
28 float sweep,
float dihedral,
float twist,
float camber,
29 float idrag,
float incidence)
31 WingSection* ws =
new WingSection;
32 if (_sections.size() == 0) {
34 Math::set3(base,
_base);
35 ws->_rootChord = _float2chord(base, chord);
36 ws->_sectionIncidence = incidence;
38 WingSection* prev = (WingSection*)_sections.get(_sections.size()-1);
40 ws->_rootChord = prev->_tipChord;
41 ws->_sectionIncidence = prev->_sectionIncidence + prev->_twist;
43 ws->_length = wingLength;
45 ws->_sweepAngleCenterLine = sweep;
46 ws->_dihedral = dihedral;
49 ws->_inducedDrag = idrag;
50 ws->_id = _sections.add(ws);
51 ws->calculateGeometry();
55 _netSpan = ws->_sectionSpan;
56 _area = ws->getArea();
57 _meanChord = ws->_meanChord;
58 _sweepLEMin = _sweepLEMax = ws->calculateSweepAngleLeadingEdge();
62 _mac = _weightedMeanChord(_mac, _area, ws->getMAC(), ws->getArea());
63 _netSpan += ws->_sectionSpan;
64 _area += ws->getArea();
65 _meanChord = _meanChord * ws->_meanChord * 0.5f;
66 float s = ws->calculateSweepAngleLeadingEdge();
67 if (_sweepLEMax < s) _sweepLEMax = s;
68 if (_sweepLEMin > s) _sweepLEMin = s;
70 _chord2float(ws->_tipChord, _tip);
71 _wingSpan = 2 * _tip[1];
73 _aspectRatio = _wingSpan*_wingSpan/_area;
75 _taper = ws->_tipChord.length / ((WingSection*)_sections.get(0))->_rootChord.length;
79Chord Wing::_float2chord(
float* pos,
float lenght)
89void Wing::_chord2float(Chord c,
float* pos)
96Chord Wing::_weightedMeanChord(Chord a,
float wa, Chord b,
float wb)
99 c.length = Math::weightedMean(a.length, wa, b.length, wb);
100 c.x = Math::weightedMean(a.x, wa, b.x, wb);
101 c.y = Math::weightedMean(a.y, wa, b.y, wb);
102 c.z = Math::weightedMean(a.z, wa, b.z, wb);
106void Wing::WingSection::calculateGeometry()
108 _meanChord = _rootChord.length*(_taper+1)*0.5f;
109 calculateWingCoordinateSystem();
115void Wing::WingSection::calculateWingCoordinateSystem() {
120 left[0] = -Math::tan(_sweepAngleCenterLine);
121 left[1] = Math::cos(_dihedral);
122 left[2] = Math::sin(_dihedral);
123 Math::unit3(left, left);
129 float *x = _orient, *y = _orient+3, *z = _orient+6;
130 x[0] = 1; x[1] = 0; x[2] = 0;
132 Math::cross3(x, y, z);
134 Math::cross3(y, z, x);
137 for(
i=0;
i<9;
i++) _rightOrient[
i] = _orient[
i];
140 for(
i=1;
i<9;
i+=3) _rightOrient[
i] = -_rightOrient[
i];
143 for(
i=3;
i<6;
i++) _rightOrient[
i] = -_rightOrient[
i];
146void Wing::WingSection::calculateTipChord() {
147 float *y = _orient+3;
148 _tipChord.x = _rootChord.x + _length * y[0];
149 _tipChord.y = _rootChord.y + _length * y[1];
150 _tipChord.z = _rootChord.z + _length * y[2];
151 _tipChord.length = _rootChord.length * _taper;
154void Wing::WingSection::calculateSpan()
157 _sectionSpan = Math::abs(_rootChord.y - _tipChord.y);
160void Wing::WingSection::calculateMAC()
165 const float commonFactor = (0.5+_taper)/(3*(1+_taper));
166 _mac.length = _rootChord.length-(2*_rootChord.length*(1-_taper)*commonFactor);
168 _mac.y = Math::abs(2*(_tipChord.y-_rootChord.y))*commonFactor;
170 _mac.x = -Math::tan(_sweepAngleCenterLine)*_mac.y + _mac.length/2 + _rootChord.x;
171 _mac.z = Math::tan(_dihedral)*_mac.y + _rootChord.z;
173 _mac.y += _rootChord.y;
176float Wing::WingSection::calculateSweepAngleLeadingEdge()
182 (sin(_sweepAngleCenterLine)+(1-_taper)*_rootChord.length/(2*_length)) /
183 cos(_sweepAngleCenterLine)
187void Wing::WingSection::setIncidence(
float incidence)
190 for(
int i=0;
i<_surfs.size();
i++)
191 ((SurfRec*)_surfs.get(
i))->surface->setIncidence(incidence + _sectionIncidence);
194void Wing::setFlapParams(
int section, WingFlaps type, FlapParams fp)
196 ((WingSection*)_sections.get(section))->_flapParams[type] = fp;
199void Wing::setSectionDrag(
int section,
float pdrag)
201 ((WingSection*)_sections.get(section))->_dragScale = pdrag;
204void Wing::setSectionStallParams(
int section, StallParams sp)
206 ((WingSection*)_sections.get(section))->_stallParams = sp;
209void Wing::setFlapPos(WingFlaps type,
float lval,
float rval)
212 if (type == WING_SPOILER || type == WING_SLAT) {
215 lval = Math::clamp(lval,
min, 1.0f);
216 rval = Math::clamp(rval,
min, 1.0f);
218 for (
int section=0; section < _sections.size(); section++)
220 ws = (WingSection*)_sections.get(section);
221 for(
int i=0;
i < ws->_flapSurfs[type].size();
i++) {
225 ((Surface*)ws->_flapSurfs[type].get(
i))->setFlapPos(lval);
226 if(_mirror) ((Surface*)ws->_flapSurfs[type].get(++
i))->setFlapPos(rval);
229 ((Surface*)ws->_flapSurfs[type].get(
i))->setSlatPos(lval);
232 ((Surface*)ws->_flapSurfs[type].get(
i))->setSpoilerPos(lval);
233 if(_mirror) ((Surface*)ws->_flapSurfs[type].get(++
i))->setSpoilerPos(rval);
240void Wing::setFlapEffectiveness(WingFlaps f,
float lval)
242 lval = Math::clamp(lval, 1, 10);
244 for (
int section=0; section < _sections.size(); section++)
246 ws = (WingSection*)_sections.get(section);
247 for(
int i=0;
i<ws->_flapSurfs[f].size();
i++) {
248 ((Surface*)ws->_flapSurfs[f].get(
i))->setFlapEffectiveness(lval);
257 for (
int section=0; section < _sections.size(); section++)
259 ws = (WingSection*)_sections.get(section);
261 if(! ws->_surfs.empty())
return;
267 const int NUM_BOUNDS {10};
268 float bounds[NUM_BOUNDS];
269 bounds[0] = ws->_flapParams[WING_FLAP0].start;
270 bounds[1] = ws->_flapParams[WING_FLAP0].end;
271 bounds[2] = ws->_flapParams[WING_FLAP1].start;
272 bounds[3] = ws->_flapParams[WING_FLAP1].end;
273 bounds[4] = ws->_flapParams[WING_SPOILER].start;
274 bounds[5] = ws->_flapParams[WING_SPOILER].end;
275 bounds[6] = ws->_flapParams[WING_SLAT].start;
276 bounds[7] = ws->_flapParams[WING_SLAT].end;
278 bounds[8] = 0; bounds[9] = 1;
281 for(
int i=0;
i<NUM_BOUNDS;
i++) {
283 float minVal = bounds[
i];
284 for(
int j=
i+1; j<NUM_BOUNDS; j++) {
285 if(bounds[j] < minVal) {
290 float tmp = bounds[
i];
291 bounds[
i] = minVal; bounds[minIdx] = tmp;
295 float last = bounds[0];
297 for(
int i=1;
i<NUM_BOUNDS;
i++) {
298 if(bounds[
i] != last)
299 bounds[nbounds++] = bounds[
i];
305 float segLen = ws->_meanChord / ws->_length;
308 for(
int i=0;
i<(nbounds-1);
i++) {
309 float start = bounds[
i];
310 float end = bounds[
i+1];
311 float mid = (start+end)/2;
313 bool hasFlap0=0, hasFlap1=0, hasSlat=0, hasSpoiler=0;
314 if(ws->_flapParams[WING_FLAP0].start < mid && mid < ws->_flapParams[WING_FLAP0].end)
316 if(ws->_flapParams[WING_FLAP1].start < mid && mid < ws->_flapParams[WING_FLAP1].end)
318 if(ws->_flapParams[WING_SLAT].start < mid && mid < ws->_flapParams[WING_SLAT].end)
320 if(ws->_flapParams[WING_SPOILER].start < mid && mid < ws->_flapParams[WING_SPOILER].end)
326 int nSegs = (int)Math::ceil((end-start)/segLen);
327 if (ws->_twist != 0 && nSegs < 8)
329 float segWid = ws->_length * (end - start)/nSegs;
332 _chord2float(ws->_rootChord, base);
334 _chord2float(ws->_tipChord, tip);
336 for(
int j=0; j<nSegs; j++) {
337 float frac = start + (j+0.5f) * (end-start)/nSegs;
339 _interp(base, tip, frac, pos);
341 float chord = ws->_rootChord.length * (1 - (1-ws->_taper)*frac);
342 float weight = chord * segWid;
343 float twist = ws->_twist * frac;
345 ws->newSurface(_version, pos, ws->_orient, chord,
346 hasFlap0, hasFlap1, hasSlat, hasSpoiler, weight, twist, _flow, _Mcrit);
350 ws->newSurface(_version, pos, ws->_rightOrient, chord,
351 hasFlap0, hasFlap1, hasSlat, hasSpoiler, weight, twist, _flow, _Mcrit);
357 setIncidence(_incidence);
359 _writeInfoToProptree();
362float Wing::getArea()
const
364 if (_mirror)
return 2 * _area;
368void Wing::multiplyLiftRatio(
float factor)
371 for (
int section=0; section < _sections.size(); section++)
373 ws = (WingSection*)_sections.get(section);
374 ws->multiplyLiftRatio(factor);
378void Wing::multiplyDragCoefficient(
float factor)
381 for (
int section=0; section < _sections.size(); section++)
383 ws = (WingSection*)_sections.get(section);
384 ws->multiplyDragCoefficient(factor);
389bool Wing::setIncidence(
float incidence)
391 if (incidence < _incidenceMin || incidence > _incidenceMax)
395 _incidence = incidence;
397 for (
int section=0; section < _sections.size(); section++)
399 ws = (WingSection*)_sections.get(section);
400 ws->setIncidence(incidence);
403 _wingN->getNode(
"incidence-deg",
true)->setFloatValue(_incidence * RAD2DEG);
408void Wing::WingSection::setDragCoefficient(
float scale)
411 for(
int i=0;
i<_surfs.size();
i++) {
412 SurfRec* s = (SurfRec*)_surfs.get(
i);
413 s->surface->setTotalForceCoefficient(
scale * s->weight);
417void Wing::WingSection::multiplyDragCoefficient(
float factor)
419 setDragCoefficient(_dragScale * factor);
422void Wing::WingSection::setLiftRatio(
float ratio)
425 for(
int i=0;
i<_surfs.size();
i++)
426 ((SurfRec*)_surfs.get(
i))->surface->setLiftCoefficient(ratio);
429void Wing::WingSection::multiplyLiftRatio(
float factor)
431 setLiftRatio(_liftRatio * factor);
434void Wing::WingSection::newSurface(Version* _version,
float* pos,
float* orient,
float chord,
bool hasFlap0,
bool hasFlap1,
bool hasSlat,
bool hasSpoiler,
float weight,
float twist, FlowRegime flow,
float mcrit)
436 Surface* s =
new Surface(_version, pos, weight);
438 s->setOrientation(orient);
440 s->setFlowRegime(flow);
443 s->setZeroAlphaLift(_camber*_stallParams.peak);
446 float stallAoA = _stallParams.aoa - _stallParams.width/4;
447 s->setStall(0, stallAoA);
448 s->setStallWidth(0, _stallParams.width);
449 s->setStallPeak(0, _stallParams.peak);
454 s->setStall(1, stallAoA * 0.8f);
455 s->setStallWidth(1, _stallParams.width * 0.5f);
457 s->setStall(1, stallAoA);
458 if( _version->isVersionOrNewer( YASIM_VERSION::V_2017_2 )) {
460 s->setStallWidth(1, _stallParams.width);
463 s->setStall(1, _stallParams.width);
469 s->setStallPeak(1, 1);
472 s->setStall(
i, 0.2267f);
473 s->setStallWidth(
i, 0.01);
476 if(hasFlap0) s->setFlapParams(_flapParams[WING_FLAP0].lift, _flapParams[WING_FLAP0].drag);
477 if(hasFlap1) s->setFlapParams(_flapParams[WING_FLAP1].lift, _flapParams[WING_FLAP1].drag);
478 if(hasSlat) s->setSlatParams(_flapParams[WING_SLAT].aoa, _flapParams[WING_SLAT].drag);
479 if(hasSpoiler) s->setSpoilerParams(_flapParams[WING_SPOILER].lift, _flapParams[WING_SPOILER].drag);
481 if(hasFlap0) _flapSurfs[WING_FLAP0].add(s);
482 if(hasFlap1) _flapSurfs[WING_FLAP1].add(s);
483 if(hasSlat) _flapSurfs[WING_SLAT].add(s);
484 if(hasSpoiler) _flapSurfs[WING_SPOILER].add(s);
486 s->setInducedDrag(_inducedDrag);
487 if(flow == FLOW_TRANSONIC) {
488 s->setCriticalMachNumber(mcrit);
491 SurfRec *sr =
new SurfRec();
498void Wing::_interp(
const float* v1,
const float* v2,
const float frac,
float* out)
500 out[0] = v1[0] + frac*(v2[0]-v1[0]);
501 out[1] = v1[1] + frac*(v2[1]-v1[1]);
502 out[2] = v1[2] + frac*(v2[2]-v1[2]);
505void Wing::_writeInfoToProptree()
507 if (_wingN ==
nullptr)
509 WingSection* ws = (WingSection*)_sections.get(0);
510 _wingN->getNode(
"root-chord",
true)->setFloatValue(ws->_rootChord.length);
511 _wingN->getNode(
"tip-x",
true)->setFloatValue(_tip[0]);
512 _wingN->getNode(
"tip-y",
true)->setFloatValue(_tip[1]);
513 _wingN->getNode(
"tip-z",
true)->setFloatValue(_tip[2]);
514 _wingN->getNode(
"base-x",
true)->setFloatValue(
_base[0]);
515 _wingN->getNode(
"base-y",
true)->setFloatValue(
_base[1]);
516 _wingN->getNode(
"base-z",
true)->setFloatValue(
_base[2]);
517 _wingN->getNode(
"taper",
true)->setFloatValue(getTaper());
518 _wingN->getNode(
"wing-span",
true)->setFloatValue(getSpan());
519 _wingN->getNode(
"wing-area",
true)->setFloatValue(getArea());
520 _wingN->getNode(
"aspect-ratio",
true)->setFloatValue(getAspectRatio());
521 _wingN->getNode(
"standard-mean-chord",
true)->setFloatValue(getSMC());
522 _wingN->getNode(
"mac",
true)->setFloatValue(getMACLength());
523 _wingN->getNode(
"mac-x",
true)->setFloatValue(getMACx());
524 _wingN->getNode(
"mac-y",
true)->setFloatValue(getMACy());
525 _wingN->getNode(
"mac-z",
true)->setFloatValue(getMACz());
530 for (
int section=0; section < _sections.size(); section++) {
531 ws = (WingSection*)_sections.get(section);
533 for (
int surf=0; surf < ws->numSurfaces(); surf++) {
534 Surface* s = ws->getSurface(surf);
535 float drag = s->getTotalForceCoefficient();
538 mass = ws->getSurfaceWeight(surf);
539 mass = mass * Math::sqrt(mass);
542 SGPropertyNode_ptr sectN = _wingN->getChild(
"section",section,1);
543 sectN->getNode(
"surface-count",
true)->setFloatValue(ws->numSurfaces());
544 sectN->getNode(
"weight",
true)->setFloatValue(mass);
545 sectN->getNode(
"base-x",
true)->setFloatValue(ws->_rootChord.x);
546 sectN->getNode(
"base-y",
true)->setFloatValue(ws->_rootChord.y);
547 sectN->getNode(
"base-z",
true)->setFloatValue(ws->_rootChord.z);
548 sectN->getNode(
"chord",
true)->setFloatValue(ws->_rootChord.length);
549 sectN->getNode(
"incidence-deg",
true)->setFloatValue(ws->_sectionIncidence * RAD2DEG);
550 sectN->getNode(
"mac",
true)->setFloatValue(ws->_mac.length);
551 sectN->getNode(
"mac-x",
true)->setFloatValue(ws->getMACx());
552 sectN->getNode(
"mac-y",
true)->setFloatValue(ws->getMACy());
553 sectN->getNode(
"mac-z",
true)->setFloatValue(ws->getMACz());
556 _wingN->getNode(
"weight",
true)->setFloatValue(wgt);
557 _wingN->getNode(
"drag",
true)->setFloatValue(dragSum);
560void Wing::weight2mass(
float scale)
562 if (_wingN ==
nullptr)
564 float wgt = _wingN->getNode(
"weight",
true)->getFloatValue();
565 _wingN->getNode(
"mass",
true)->setFloatValue(wgt *
scale);
570float Wing::updateModel(Model* model)
574 for (
int section=0; section < _sections.size(); section++) {
575 ws = (WingSection*)_sections.get(section);
576 for(
int surf=0; surf < ws->numSurfaces(); surf++) {
577 Surface* s = ws->getSurface(surf);
578 model->addSurface(s);
580 float weight = ws->getSurfaceWeight(surf);
581 weight = weight * Math::sqrt(weight);
586 int mid = model->getBody()->addMass(weight, pos,
true);
587 if (_wingN !=
nullptr) {
588 SGPropertyNode_ptr n = _wingN->getNode(
"surfaces",
true)->getChild(
"surface", s->getID(),
true);
589 n->getNode(
"c0",
true)->setFloatValue(s->getTotalForceCoefficient());
590 n->getNode(
"cdrag",
true)->setFloatValue(s->getDragCoefficient());
591 n->getNode(
"clift",
true)->setFloatValue(s->getLiftCoefficient());
592 n->getNode(
"mass-id",
true)->setIntValue(mid);
596 if (_wingN !=
nullptr) {
597 _wingN->getNode(
"weight",
true)->setFloatValue(_weight);
602void Wing::printSectionInfo()
605 printf(
"#wing sections: %d\n", _sections.size());
606 for (
int section=0; section < _sections.size(); section++) {
607 ws = (WingSection*)_sections.get(section);
608 printf(
"Section %d base point: x=\"%.3f\" y=\"%.3f\" z=\"%.3f\" chord=\"%.3f\" incidence=\"%.1f\" (at section root in deg)\n", section,
609 ws->_rootChord.x, ws->_rootChord.y, ws->_rootChord.z, ws->_rootChord.length, ws->_sectionIncidence * RAD2DEG);