21inline float abs(
float f) {
return f<0 ? -f : f; }
23Airplane::Airplane() : _model(this)
30 for(
i=0;
i<_fuselages.size();
i++)
31 delete (Fuselage*)_fuselages.get(
i);
32 for(
i=0;
i<_tanks.size();
i++)
33 delete (Tank*)_tanks.get(
i);
34 for(
i=0;
i<_thrusters.size();
i++)
35 delete (ThrustRec*)_thrusters.get(
i);
36 for(
i=0;
i<_gears.size();
i++) {
37 GearRec*
g = (GearRec*)_gears.get(
i);
41 for(
i=0;
i<_surfs.size();
i++)
42 delete (Surface*)_surfs.get(
i);
43 for(
i=0;
i<_contacts.size();
i++) {
44 ContactRec* c = (ContactRec*)_contacts.get(
i);
48 for(
i=0;
i<_solveWeights.size();
i++)
49 delete (SolveWeight*)_solveWeights.get(
i);
50 for(
i=0;
i<_config[
CRUISE].controls.size();
i++) {
51 ControlSetting* c = (ControlSetting*)_config[CRUISE].controls.get(
i);
54 for(
i=0;
i<_config[
APPROACH].controls.size();
i++) {
55 ControlSetting* c = (ControlSetting*)_config[APPROACH].controls.get(
i);
60 for(
i=0;
i<_vstabs.size();
i++)
61 delete (Wing*)_vstabs.get(
i);
62 for(
i=0;
i<_weights.size();
i++)
63 delete (WeightRec*)_weights.get(
i);
66void Airplane::iterate(
float dt)
73void Airplane::calcFuelWeights()
75 for(
int i=0;
i<_tanks.size();
i++) {
76 Tank* t = (Tank*)_tanks.get(
i);
77 _model.getBody()->setMass(t->handle, t->fill);
81void Airplane::getPilotAccel(
float* out)
83 State* s = _model.getState();
86 Glue::geodUp(s->pos, out);
87 Math::mul3(-9.8f, out, out);
88 Math::vmul33(s->orient, out, out);
91 float acceleration[3];
93 Math::vmul33(s->orient, s->acc, acceleration);
94 acceleration[1] = -acceleration[1];
95 acceleration[2] = -acceleration[2];
97 Math::add3(acceleration, out, out);
102Wing* Airplane::getWing()
104 if (_wing ==
nullptr) {
105 _wing =
new Wing((Version*)
this,
true);
110Wing* Airplane::getTail()
112 if (_tail ==
nullptr) {
113 _tail =
new Wing((Version*)
this,
true);
118void Airplane::updateGearState()
120 for(
int i=0;
i<_gears.size();
i++) {
121 GearRec* gr = (GearRec*)_gears.get(
i);
122 float ext = gr->gear->getExtension();
124 gr->surf->setDragCoefficient(ext);
125 gr->surf->setYDrag(ext);
126 gr->surf->setLiftCoefficient(ext);
131void Airplane::setConfig(Configuration cfg,
float speed,
float altitude,
float fuel,
float gla,
float aoa)
133 _config[cfg].id = cfg;
134 _config[cfg].speed = speed;
138 _config[cfg].state.setupOrientationFromAoa(aoa);
139 _config[cfg].aoa = aoa;
140 _config[cfg].fuel = fuel;
141 _config[cfg].glideAngle = gla;
145void Airplane::setElevatorControl(
const char* propName)
147 _approachElevator = _addControlSetting(APPROACH, propName, 0);
151void Airplane::setHstabTrimControl(
const char* propName)
155 _tailIncidence = _addControlSetting(APPROACH, propName, 0);
156 _tailIncidenceCopy = _addControlSetting(CRUISE, propName, 0);
159void Airplane::addControlSetting(Configuration cfg,
const char* prop,
float val)
161 _addControlSetting(cfg, prop,val);
164Airplane::ControlSetting* Airplane::_addControlSetting(Configuration cfg,
const char* prop,
float val)
166 ControlSetting* c =
new ControlSetting();
167 c->propHandle = _controlMap.getInputPropertyHandle(prop);
169 _config[cfg].controls.add(c);
177void Airplane::addControlInput(
const char* propName, ControlMap::ControlType type,
void* obj,
int subobj,
int opt,
float src0,
float src1,
float dst0,
float dst1)
179 ControlMap::ObjectID oid = ControlMap::getObjectID(obj, subobj);
180 _controlMap.addMapping(propName, type, oid, opt, src0, src1, dst0, dst1);
182 if (type == ControlMap::INCIDENCE && obj == _tail) {
183 setHstabTrimControl(propName);
187void Airplane::addSolutionWeight(Configuration cfg,
int idx,
float wgt)
189 SolveWeight* w =
new SolveWeight();
193 _solveWeights.add(w);
196void Airplane::addFuselage(
const float* front,
const float* back,
float width,
197 float taper,
float mid,
198 float cx,
float cy,
float cz,
float idrag)
200 Fuselage* f =
new Fuselage();
201 Math::set3(front, f->front);
202 Math::set3(back, f->back);
213int Airplane::addTank(
const float* pos,
float cap,
float density)
215 Tank* t =
new Tank();
216 Math::set3(pos, t->pos);
219 t->density = density;
220 t->handle = 0xffffffff;
221 return _tanks.add(t);
224void Airplane::addGear(Gear* gear)
226 GearRec*
g =
new GearRec();
232void Airplane::addThruster(Thruster* thruster,
float mass,
const float* cg)
234 ThrustRec* t =
new ThrustRec();
235 t->thruster = thruster;
237 Math::set3(cg, t->cg);
242void Airplane::addBallast(
const float* pos,
float mass)
244 _model.getBody()->addMass(mass, pos,
true);
249int Airplane::addWeight(
const float* pos,
float size)
251 WeightRec* wr =
new WeightRec();
252 wr->handle = _model.getBody()->addMass(0, pos);
254 wr->surf =
new Surface(
this, pos, size*size);
255 _model.addSurface(wr->surf);
256 _surfs.add(wr->surf);
258 return _weights.add(wr);
262void Airplane::setWeight(
int handle,
float mass)
264 WeightRec* wr = (WeightRec*)_weights.get(handle);
266 _model.getBody()->setMass(wr->handle, mass);
271 wr->surf->setDragCoefficient(0);
272 wr->surf->setYDrag(0);
273 wr->surf->setLiftCoefficient(0);
275 wr->surf->setDragCoefficient(1);
276 wr->surf->setYDrag(1);
277 wr->surf->setLiftCoefficient(1);
281void Airplane::setFuelFraction(
float frac)
283 for(
int i=0;
i<_tanks.size();
i++) {
284 Tank* t = (Tank*)_tanks.get(
i);
285 t->fill = frac * t->cap;
286 _model.getBody()->setMass(t->handle, t->cap * frac);
297void Airplane::addContactPoint(
const float* pos)
299 ContactRec* c =
new ContactRec;
301 Math::set3(pos, c->p);
305float Airplane::compileWing(Wing* w)
316 addContactPoint(tip);
317 if(w->isMirrored()) {
319 addContactPoint(tip);
324 wgt = w->updateModel(&_model);
328void Airplane::compileRotorgear()
330 getRotorgear()->compile();
333float Airplane::compileFuselage(Fuselage* f)
336 addContactPoint(f->front);
337 addContactPoint(f->back);
341 Math::sub3(f->front, f->back, fwd);
342 float len = Math::mag3(fwd);
344 _failureMsg =
"Zero length fuselage";
347 float wid = f->width;
348 int segs = (int)Math::ceil(len/wid);
349 float segWgt = len*wid/segs;
351 for(j=0; j<segs; j++) {
352 float frac = (j+0.5f) / segs;
355 scale = f->taper+(1-f->taper) * (frac / f->mid);
357 if( isVersionOrNewer( YASIM_VERSION::V_32 ) ) {
359 scale = 1 - (1-f->taper) * (frac - f->mid) / (1 - f->mid);
362 scale = f->taper+(1-f->taper) * (frac - f->mid) / (1 - f->mid);
368 Math::mul3(frac, fwd, pos);
369 Math::add3(f->back, pos, pos);
372 float mass =
scale*segWgt * Math::sqrt(
scale*segWgt);
373 _model.getBody()->addMass(mass, pos,
true);
381 float sideDrag = len/wid;
383 if ( isVersionOrNewer( YASIM_VERSION::V_32 ) ) {
400 float dragCoefficient =
scale*segWgt*f->_cx;
401 if( isVersionOrNewer( YASIM_VERSION::V_32 ) ) {
402 dragCoefficient =
scale*segWgt;
406 Surface* s =
new Surface(
this, pos, dragCoefficient);
407 if( isVersionOrNewer( YASIM_VERSION::V_32 ) ) {
408 s->setDragCoefficient(f->_cx);
410 s->setYDrag(sideDrag*f->_cy);
411 s->setLiftCoefficient(sideDrag*f->_cz);
412 s->setInducedDrag(f->_idrag);
416 float *x=o, *y=o+3, *z=o+6;
418 y[0] = 0; y[1] = 1; y[2] = 0;
419 Math::cross3(x, y, z);
421 Math::cross3(z, x, y);
422 s->setOrientation(o);
424 _model.addSurface(s);
432void Airplane::compileGear(GearRec* gr)
441 float pos[3], cmp[3];
442 g->getCompression(cmp);
443 float length = 3 * Math::mag3(cmp);
445 pos[2] -= (
g->getWheelRadius() +
g->getTyreRadius());
446 Math::mul3(0.5, cmp, cmp);
447 Math::add3(pos, cmp, pos);
450 Surface* s =
new Surface(
this, pos, length*length);
454 _model.addSurface(s);
465void Airplane::compileContactPoints()
470 comp[0] = 0; comp[1] = 0; comp[2] = DIST;
474 float mass = _model.getBody()->getTotalMass();
475 float spring = (1/DIST) * 9.8f * 10.0f * mass;
476 float damp = 2 * Math::sqrt(spring * mass);
478 for(
int i=0;
i<_contacts.size();
i++) {
479 ContactRec* c = (ContactRec*)_contacts.get(
i);
481 Gear*
g =
new Gear();
483 g->setPosition(c->p);
485 g->setCompression(comp);
486 g->setSpring(spring);
491 g->setStaticFriction(0.6f);
492 g->setDynamicFriction(0.5f);
493 g->setContactPoint(1);
499void Airplane::compile(
bool verbose)
501 RigidBody* body = _model.getBody();
502 int firstMass = body->numMasses();
503 SGPropertyNode_ptr baseN =
fgGetNode(
"/fdm/yasim/model/wings",
true);
513 if (baseN !=
nullptr) {
514 _wingsN = baseN->getChild(
"wing", 0,
true);
515 _wing->setPropertyNode(_wingsN);
517 aeroWgt += compileWing(_wing);
520 _cgDesiredFront = _wing->getMACx() - _wing->getMACLength()*_cgDesiredMin;
521 _cgDesiredAft = _wing->getMACx() - _wing->getMACLength()*_cgDesiredMax;
523 SGPropertyNode_ptr n =
fgGetNode(
"/fdm/yasim/model",
true);
524 n->getNode(
"cg-x-range-front",
true)->setFloatValue(_cgDesiredFront);
525 n->getNode(
"cg-x-range-aft",
true)->setFloatValue(_cgDesiredAft);
530 if (baseN !=
nullptr) {
531 _wingsN = baseN->getChild(
"tail", 0,
true);
532 _tail->setPropertyNode(_wingsN);
534 aeroWgt += compileWing(_tail);
537 for(
int i=0;
i<_vstabs.size();
i++)
539 Wing* vs = (Wing*)_vstabs.get(
i);
541 _wingsN = baseN->getChild(
"stab",
i,
true);
542 vs->setPropertyNode(_wingsN);
544 aeroWgt += compileWing(vs);
548 for(
int i=0;
i<_fuselages.size();
i++)
549 aeroWgt += compileFuselage((Fuselage*)_fuselages.get(
i));
552 float nonAeroWgt = _ballast;
553 for(
int i=0;
i<_thrusters.size();
i++)
554 nonAeroWgt += ((ThrustRec*)_thrusters.get(
i))->mass;
557 float wscale = (_emptyWeight-nonAeroWgt)/aeroWgt;
558 for(
int i=firstMass;
i<body->numMasses();
i++) {
559 body->setMass(
i, body->getMass(
i)*wscale);
562 if (baseN !=
nullptr) {
563 if (_wing) _wing->weight2mass(wscale);
564 if (_tail) _tail->weight2mass(wscale);
565 for(
int i=0;
i<_vstabs.size();
i++)
567 ((Wing*)_vstabs.get(
i))->weight2mass(wscale);
571 for(
int i=0;
i<_thrusters.size();
i++) {
572 ThrustRec* t = (ThrustRec*)_thrusters.get(
i);
573 body->addMass(t->mass, t->cg,
true);
578 for(
int i=0;
i<_tanks.size();
i++) {
579 Tank* t = (Tank*)_tanks.get(
i);
580 t->handle = body->addMass(0, t->pos);
583 _config[
CRUISE].weight = _emptyWeight + totalFuel*_config[
CRUISE].fuel;
590 for(
int i=0;
i<_gears.size();
i++)
591 compileGear((GearRec*)_gears.get(
i));
594 for(
int i=0;
i<_thrusters.size();
i++) {
595 ThrustRec* tr = (ThrustRec*)_thrusters.get(
i);
596 tr->handle = _model.addThruster(tr->thruster);
605 gespan = _wing->getSpan();
607 if(!isVersionOrNewer( YASIM_VERSION::V_2017_2 )) {
612 gespan += 2*Math::abs(pos[2]);
615 baseN->getChild(
"wing", 0)->getNode(
"gnd-eff-span",
true)->setFloatValue(gespan);
617 _model.setGroundEffect(pos, gespan, 0.15f);
622 if (_failureMsg)
return;
625 calculateCGHardLimits();
627 if(_wing && _tail) solveAirplane(verbose);
632 solveHelicopter(verbose);
637 compileContactPoints();
640void Airplane::solveGear()
643 _model.getBody()->getCG(cg);
652 for(
i=0;
i<_gears.size();
i++) {
653 GearRec* gr = (GearRec*)_gears.get(
i);
659 pos[2] -= (
g->getWheelRadius() +
g->getTyreRadius());
660 Math::sub3(cg, pos, pos);
661 gr->wgt = 1.0f/(0.5f+Math::sqrt(pos[0]*pos[0] + pos[1]*pos[1]));
662 if (!
g->getIgnoreWhileSolving())
667 for(
i=0;
i<_gears.size();
i++)
668 ((GearRec*)_gears.get(
i))->wgt /= total;
673 float descentRate = 2.0f*_config[
APPROACH].speed/19.1f;
678 float energy = 0.5f*_config[
APPROACH].weight*descentRate*descentRate;
680 for(
i=0;
i<_gears.size();
i++) {
681 GearRec* gr = (GearRec*)_gears.get(
i);
682 float e = energy * gr->wgt;
684 gr->gear->getCompression(comp);
685 float len = Math::mag3(comp)*(1+2*gr->gear->getInitialLoad());
688 float k = 2 * e / (len*len);
690 gr->gear->setSpring(k * gr->gear->getSpring());
693 gr->gear->setDamping(2*Math::sqrt(k*_config[APPROACH].weight*gr->wgt)
694 * gr->gear->getDamping());
698void Airplane::calculateCGHardLimits()
702 for (
int i = 0;
i < _gears.size();
i++) {
703 GearRec* gr = (GearRec*)_gears.get(
i);
705 gr->gear->getPosition(pos);
706 if (pos[0] > _cgMax) _cgMax = pos[0];
707 if (pos[0] < _cgMin) _cgMin = pos[0];
711void Airplane::initEngines()
713 for(
int i=0;
i<_thrusters.size();
i++) {
714 ThrustRec* tr = (ThrustRec*)_thrusters.get(
i);
715 tr->thruster->init();
719void Airplane::stabilizeThrust()
721 for(
int i=0;
i<_thrusters.size();
i++)
722 _model.getThruster(
i)->stabilize();
726void Airplane::setupWeights(Configuration cfg)
729 for(
i=0;
i<_weights.size();
i++)
731 for(
i=0;
i<_solveWeights.size();
i++) {
732 SolveWeight* w = (SolveWeight*)_solveWeights.get(
i);
734 setWeight(w->id, w->wgt);
739void Airplane::setControlValues(
const Vector& controls)
742 for(
int i=0;
i < controls.size();
i++) {
743 ControlSetting* c = (ControlSetting*)controls.get(
i);
744 if (c->propHandle >= 0)
745 _controlMap.setInput(c->propHandle, c->val);
747 _controlMap.applyControls();
750void Airplane::runConfig(Config &cfg)
754 if (!(cfg.id == APPROACH)) {
755 cfg.state.setupOrientationFromAoa(cfg.aoa);
757 cfg.state.setupSpeedAndPosition(cfg.speed, cfg.glideAngle);
758 _model.setState(&cfg.state);
759 _model.setStandardAtmosphere(cfg.altitude);
760 setControlValues(cfg.controls);
764 Math::mul3(-1, cfg.state.v, wind);
765 cfg.state.globalToLocal(wind, wind);
767 setFuelFraction(cfg.fuel);
768 setupWeights(cfg.id);
772 for(
int i=0;
i<_thrusters.size();
i++) {
773 Thruster* t = ((ThrustRec*)_thrusters.get(
i))->thruster;
775 t->setStandardAtmosphere(cfg.altitude);
782 _model.getBody()->recalc();
783 _model.getBody()->reset();
784 _model.initIteration();
785 _model.calcForces(&cfg.state);
789void Airplane::applyDragFactor(
float factor)
791 float applied = Math::pow(factor, _solverDelta);
792 _dragFactor *= applied;
794 _wing->multiplyDragCoefficient(applied);
796 _tail->multiplyDragCoefficient(applied);
798 for(
i=0;
i<_vstabs.size();
i++) {
799 Wing* w = (Wing*)_vstabs.get(
i);
800 w->multiplyDragCoefficient(applied);
802 for(
i=0;
i<_fuselages.size();
i++) {
803 Fuselage* f = (Fuselage*)_fuselages.get(
i);
804 for(
int j=0; j<f->surfs.size(); j++) {
805 Surface* s = (Surface*)f->surfs.get(j);
806 if( isVersionOrNewer( YASIM_VERSION::V_32 ) ) {
815 s->mulDragCoefficient(applied);
819 s->mulTotalForceCoefficient(applied);
823 for(
i=0;
i<_weights.size();
i++) {
824 WeightRec* wr = (WeightRec*)_weights.get(
i);
825 wr->surf->mulTotalForceCoefficient(applied);
827 for(
i=0;
i<_gears.size();
i++) {
828 GearRec* gr = (GearRec*)_gears.get(
i);
829 gr->surf->mulTotalForceCoefficient(applied);
835void Airplane::applyLiftRatio(
float factor)
837 float applied = Math::pow(factor, _solverDelta);
838 _liftRatio *= applied;
840 _wing->multiplyLiftRatio(applied);
842 _tail->multiplyLiftRatio(applied);
844 for(
i=0;
i<_vstabs.size();
i++) {
845 Wing* w = (Wing*)_vstabs.get(
i);
846 w->multiplyLiftRatio(applied);
851float Airplane::normFactor(
float f)
859float Airplane::_getPitch(Config &cfg)
862 _model.getBody()->getAngularAccel(tmp);
863 cfg.state.localToGlobal(tmp, tmp);
868float Airplane::_getLiftForce(Config &cfg)
871 _model.getBody()->getAccel(tmp);
872 cfg.state.localToGlobal(tmp, tmp);
873 return cfg.weight * tmp[2];
877float Airplane::_getDragForce(Config &cfg)
880 _model.getBody()->getAccel(tmp);
881 cfg.state.localToGlobal(tmp, tmp);
882 return cfg.weight * tmp[0];
887float Airplane::_checkConvergence(
float prev,
float current)
889 static int damping {0};
891 if ((prev*current) < 0 && (
abs(current + prev) < 0.01f)) {
892 if (!damping) fprintf(stderr,
"YASim warning: possible convergence problem.\n");
894 if (current < 1) current *=
abs(current);
895 else current = sqrt(current);
900void Airplane::solveAirplane(
bool verbose)
902 static const float ARCMIN = 0.0002909f;
905 _solutionIterations = 0;
908 if (_approachElevator ==
nullptr) {
909 setElevatorControl(
"/controls/flight/elevator-trim");
912 if (_tailIncidence ==
nullptr) {
915 _tailIncidence =
new ControlSetting;
916 _tailIncidenceCopy =
new ControlSetting;
920 fprintf(stdout,
"i\tdAoa\tdTail\tcl0\tcp1\n");
923 float prevTailDelta {0};
925 if(_solutionIterations++ > _solverMaxIterations) {
926 _failureMsg =
"Solution failed to converge!";
930 runConfig(_config[CRUISE]);
932 _model.getThrust(tmp);
933 float thrust = tmp[0] + _config[
CRUISE].weight * Math::sin(_config[CRUISE].glideAngle) * 9.81;
935 float cDragForce = _getDragForce(_config[CRUISE]);
936 float clift0 = _getLiftForce(_config[CRUISE]);
937 float cpitch0 = _getPitch(_config[CRUISE]);
940 runConfig(_config[APPROACH]);
941 double apitch0 = _getPitch(_config[APPROACH]);
942 float alift = _getLiftForce(_config[APPROACH]);
945 float savedAoa = _config[
CRUISE].aoa;
946 _config[
CRUISE].aoa += ARCMIN;
947 runConfig(_config[CRUISE]);
948 _config[
CRUISE].aoa = savedAoa;
950 float clift1 = _getLiftForce(_config[CRUISE]);
953 float savedIncidence = _tailIncidence->val;
955 _tailIncidenceCopy->val = _tailIncidence->val += ARCMIN;
956 if (!_tail->setIncidence(_tailIncidence->val)) {
957 _failureMsg =
"Tail incidence out of bounds.";
960 runConfig(_config[CRUISE]);
961 _tailIncidenceCopy->val = _tailIncidence->val = savedIncidence;
962 _tail->setIncidence(_tailIncidence->val);
964 float cpitch1 = _getPitch(_config[CRUISE]);
967 float awgt = 9.8f * _config[
APPROACH].weight;
969 float dragFactor = thrust / (thrust-cDragForce);
970 float liftFactor = awgt / (awgt+alift);
973 if(dragFactor <= 0) {
974 _failureMsg =
"dragFactor < 0 (drag > thrust)";
977 if(liftFactor <= 0) {
978 _failureMsg =
"liftFactor < 0";
986 const float ELEVDIDDLE = 0.001f;
987 _approachElevator->val += ELEVDIDDLE;
988 runConfig(_config[APPROACH]);
989 _approachElevator->val -= ELEVDIDDLE;
991 double apitch1 = _getPitch(_config[APPROACH]);
997 applyDragFactor(dragFactor);
998 applyLiftRatio(liftFactor);
1001 if(normFactor(dragFactor) > _solverThreshold*1.0001
1002 || normFactor(liftFactor) > _solverThreshold*1.0001)
1008 float aoaDelta = -clift0 * (ARCMIN/(clift1-clift0));
1009 float tailDelta = -cpitch0 * (ARCMIN/(cpitch1-cpitch0));
1012 if (_solverMode > 0) {
1013 tailDelta = _checkConvergence(prevTailDelta, tailDelta);
1014 prevTailDelta = tailDelta;
1017 fprintf(stdout,
"%4d\t%f\t%f\t%f\t%f\n", _solutionIterations, aoaDelta, tailDelta, clift0, cpitch1);
1019 _config[
CRUISE].aoa += _solverDelta*aoaDelta;
1020 _tailIncidence->val += _solverDelta*tailDelta;
1022 _config[
CRUISE].aoa = Math::clamp(_config[CRUISE].aoa, -0.175f, 0.175f);
1023 _tailIncidence->val = Math::clamp(_tailIncidence->val, -0.175f, 0.175f);
1025 if(
abs(cDragForce/_config[CRUISE].weight) < _solverThreshold*0.0001 &&
1026 abs(alift/_config[APPROACH].weight) < _solverThreshold*0.0001 &&
1027 abs(aoaDelta) < _solverThreshold*.000017 &&
1028 abs(tailDelta) < _solverThreshold*.000017)
1030 float elevDelta = -apitch0 * (ELEVDIDDLE/(apitch1-apitch0));
1032 fprintf(stdout,
"%4d dElev %f, ap0 %f,ap1 %f \n", _solutionIterations, elevDelta, apitch0, apitch1);
1035 if(
abs(elevDelta) < _solverThreshold*0.0001)
1039 _approachElevator->val += _solverDelta * elevDelta;
1040 if(
abs(_approachElevator->val) > 1) {
1041 _failureMsg =
"Insufficient elevator to trim for approach.";
1047 if(_dragFactor < 1e-06 || _dragFactor > 1e6) {
1048 _failureMsg =
"Drag factor beyond reasonable bounds.";
1050 }
else if(_liftRatio < 1e-04 || _liftRatio > 1e4) {
1051 _failureMsg =
"Lift ratio beyond reasonable bounds.";
1053 }
else if(Math::abs(_config[CRUISE].aoa) >= .17453293) {
1054 _failureMsg =
"Cruise AoA > 10 degrees";
1056 }
else if(Math::abs(_tailIncidence->val) >= .17453293) {
1057 _failureMsg =
"Tail incidence > 10 degrees";
1061 if (_wingsN !=
nullptr) {
1062 if (_tailIncidence->propHandle >= 0) {
1063 fgSetFloat(_controlMap.getProperty(_tailIncidence->propHandle)->name, _tailIncidence->val);
1068void Airplane::solveHelicopter(
bool verbose)
1070 _solutionIterations = 0;
1072 if (getRotorgear()!=0)
1074 Rotorgear* rg = getRotorgear();
1075 applyDragFactor(Math::pow(rg->getYasimDragFactor()/1000,
1077 applyLiftRatio(Math::pow(rg->getYasimLiftFactor(),
1084 applyDragFactor(Math::pow(15.7/1000, 1/_solverDelta));
1085 applyLiftRatio(Math::pow(104, 1/_solverDelta));
1087 _config[
CRUISE].state.setupState(0,0,0);
1088 _model.setState(&_config[CRUISE].state);
1089 setupWeights(APPROACH);
1090 _controlMap.reset();
1091 _model.getBody()->reset();
1092 _model.setStandardAtmosphere(_config[CRUISE].
altitude);
1095float Airplane::getCGMAC()
1099 _model.getBody()->getCG(cg);
1100 return (_wing->getMACx() - cg[0]) / _wing->getMACLength();
1105float Airplane::getWingSpan()
const
1107 if (_wing ==
nullptr)
return -1;
1108 return _wing->getSpan();
1111float Airplane::getWingArea()
const
1113 if (_wing ==
nullptr)
return -1;
1114 return _wing->getArea();
1117float Airplane::_getWingLoad(
float mass)
const
1119 if (_wing ==
nullptr)
return -1;
1120 float area = _wing->getArea();
1121 if (area == 0)
return -1;
1122 else return mass / area;
1126float Airplane::_getWingLever(
const Wing* w)
const
1128 if (w ==
nullptr)
return -1;
1132 float ac = w->getMACx() - 0.25f * w->getMACLength();
1137float Airplane::getMaxThrust()
1139 float wind[3] {0,0,0};
1140 float thrust[3] {0,0,0};
1141 float sum[3] {0,0,0};
1142 for(
int i=0;
i<_thrusters.size();
i++) {
1143 Thruster* t = ((ThrustRec*)_thrusters.get(
i))->thruster;
1145 t->setStandardAtmosphere(0);
1148 t->getThrust(thrust);
1149 Math::add3(thrust, sum, sum);
1154float Airplane::getTailIncidence()
const
1156 if (_tailIncidence !=
nullptr) {
1157 return _tailIncidence->val;
1162float Airplane::getApproachElevator()
const
1164 if (_approachElevator !=
nullptr) {
1165 return _approachElevator->val;