diff --git a/src/FDM/YASim/Airplane.cpp b/src/FDM/YASim/Airplane.cpp index a419090f1..0a4b128b6 100644 --- a/src/FDM/YASim/Airplane.cpp +++ b/src/FDM/YASim/Airplane.cpp @@ -60,9 +60,9 @@ Airplane::~Airplane() for(i=0; i<_solveWeights.size(); i++) delete (SolveWeight*)_solveWeights.get(i); for(i=0; i<_cruiseConfig.controls.size(); i++) - delete (Control*)_cruiseConfig.controls.get(i); + delete (ControlSetting*)_cruiseConfig.controls.get(i); for(i=0; i<_approachConfig.controls.size(); i++) { - Control* c = (Control*)_approachConfig.controls.get(i); + ControlSetting* c = (ControlSetting*)_approachConfig.controls.get(i); if(c != &_approachElevator) delete c; } @@ -111,15 +111,31 @@ void Airplane::getPilotAccel(float* out) // FIXME: rotational & centripetal acceleration needed } +Wing* Airplane::getWing() +{ + if (_wing == nullptr) { + _wing = new Wing((Version*)this, true); + } + return _wing; +} + +Wing* Airplane::getTail() +{ + if (_tail == nullptr) { + _tail = new Wing((Version*)this, true); + } + return _tail; +} + void Airplane::updateGearState() { for(int i=0; i<_gears.size(); i++) { GearRec* gr = (GearRec*)_gears.get(i); float ext = gr->gear->getExtension(); - gr->surf->setXDrag(ext); + gr->surf->setDragCoefficient(ext); gr->surf->setYDrag(ext); - gr->surf->setZDrag(ext); + gr->surf->setLiftCoefficient(ext); } } @@ -143,33 +159,32 @@ void Airplane::setCruise(float speed, float altitude, float fuel, float gla) _cruiseConfig.glideAngle = gla; } -void Airplane::setElevatorControl(int control) +void Airplane::setElevatorControl(const char* prop) { - _approachElevator.control = control; + _approachElevator.propHandle = getControlMap()->getInputPropertyHandle(prop); _approachElevator.val = 0; _approachConfig.controls.add(&_approachElevator); } -void Airplane::addApproachControl(int control, float val) +void Airplane::addControlSetting(Configuration cfg, const char* prop, float val) { - Control* c = new Control(); - c->control = control; + ControlSetting* c = new ControlSetting(); + c->propHandle = getControlMap()->getInputPropertyHandle(prop); c->val = val; - _approachConfig.controls.add(c); + switch (cfg) { + case APPROACH: + _approachConfig.controls.add(c); + break; + case CRUISE: + _cruiseConfig.controls.add(c); + break; + } } -void Airplane::addCruiseControl(int control, float val) -{ - Control* c = new Control(); - c->control = control; - c->val = val; - _cruiseConfig.controls.add(c); -} - -void Airplane::addSolutionWeight(bool approach, int idx, float wgt) +void Airplane::addSolutionWeight(Configuration cfg, int idx, float wgt) { SolveWeight* w = new SolveWeight(); - w->approach = approach; + w->approach = (cfg == APPROACH); w->idx = idx; w->wgt = wgt; _solveWeights.add(w); @@ -238,9 +253,7 @@ int Airplane::addWeight(float* pos, float size) WeightRec* wr = new WeightRec(); wr->handle = _model.getBody()->addMass(0, pos); - wr->surf = new Surface(this); - wr->surf->setPosition(pos); - wr->surf->setTotalDrag(size*size); + wr->surf = new Surface(this, pos, size*size); _model.addSurface(wr->surf); _surfs.add(wr->surf); @@ -257,13 +270,13 @@ void Airplane::setWeight(int handle, float mass) // Kill the aerodynamic drag if the mass is exactly zero. This is // how we simulate droppable stores. if(mass == 0) { - wr->surf->setXDrag(0); - wr->surf->setYDrag(0); - wr->surf->setZDrag(0); + wr->surf->setDragCoefficient(0); + wr->surf->setYDrag(0); + wr->surf->setLiftCoefficient(0); } else { - wr->surf->setXDrag(1); - wr->surf->setYDrag(1); - wr->surf->setZDrag(1); + wr->surf->setDragCoefficient(1); + wr->surf->setYDrag(1); + wr->surf->setLiftCoefficient(1); } } @@ -308,49 +321,9 @@ float Airplane::compileWing(Wing* w) addContactPoint(tip); tip[1] *= -1; //undo mirror } - if (_wingsN != 0) { - _wingsN->getNode("tip-x", true)->setFloatValue(tip[0]); - _wingsN->getNode("tip-y", true)->setFloatValue(tip[1]); - _wingsN->getNode("tip-z", true)->setFloatValue(tip[2]); - w->getBase(tip); - _wingsN->getNode("base-x", true)->setFloatValue(tip[0]); - _wingsN->getNode("base-y", true)->setFloatValue(tip[1]); - _wingsN->getNode("base-z", true)->setFloatValue(tip[2]); - _wingsN->getNode("wing-span", true)->setFloatValue(w->getSpan()); - _wingsN->getNode("wing-area", true)->setFloatValue(w->getArea()); - _wingsN->getNode("aspect-ratio", true)->setFloatValue(w->getAspectRatio()); - _wingsN->getNode("standard-mean-chord", true)->setFloatValue(w->getSMC()); - _wingsN->getNode("mac", true)->setFloatValue(w->getMAC()); - _wingsN->getNode("mac-x", true)->setFloatValue(w->getMACx()); - _wingsN->getNode("mac-y", true)->setFloatValue(w->getMACy()); - } float wgt = 0; - float dragSum = 0; - for(int i=0; inumSurfaces(); i++) { - Surface* s = (Surface*)w->getSurface(i); - float td = s->getTotalDrag(); - int sid = s->getID(); - - _model.addSurface(s); - - float mass = w->getSurfaceWeight(i); - mass = mass * Math::sqrt(mass); - float pos[3]; - s->getPosition(pos); - int mid = _model.getBody()->addMass(mass, pos, true); - if (_wingsN != 0) { - SGPropertyNode_ptr n = _wingsN->getNode("surfaces", true)->getChild("surface", sid, true); - n->getNode("drag", true)->setFloatValue(td); - n->getNode("mass-id", true)->setIntValue(mid); - } - wgt += mass; - dragSum += td; - } - if (_wingsN != 0) { - _wingsN->getNode("weight", true)->setFloatValue(wgt); - _wingsN->getNode("drag", true)->setFloatValue(dragSum); - } + wgt = w->updateModel(&_model); return wgt; } @@ -371,7 +344,7 @@ float Airplane::compileFuselage(Fuselage* f) float len = Math::mag3(fwd); if (len == 0) { _failureMsg = "Zero length fuselage"; - return 0; + return 0; } float wid = f->width; int segs = (int)Math::ceil(len/wid); @@ -379,19 +352,18 @@ float Airplane::compileFuselage(Fuselage* f) int j; for(j=0; jmid) scale = f->taper+(1-f->taper) * (frac / f->mid); else { - if( isVersionOrNewer( YASIM_VERSION_32 ) ) { - // Correct calculation of width for fuselage taper. - scale = 1 - (1-f->taper) * (frac - f->mid) / (1 - f->mid); - } else { - // Original, incorrect calculation of width for fuselage taper. - scale = f->taper+(1-f->taper) * (frac - f->mid) / (1 - f->mid); - } - } + if( isVersionOrNewer( YASIM_VERSION_32 ) ) { + // Correct calculation of width for fuselage taper. + scale = 1 - (1-f->taper) * (frac - f->mid) / (1 - f->mid); + } else { + // Original, incorrect calculation of width for fuselage taper. + scale = f->taper+(1-f->taper) * (frac - f->mid) / (1 - f->mid); + } + } // Where are we? float pos[3]; @@ -403,44 +375,42 @@ float Airplane::compileFuselage(Fuselage* f) _model.getBody()->addMass(mass, pos, true); wgt += mass; + + // The following is the original YASim value for sideDrag. + // Originally YASim calculated the fuselage's lateral drag + // coefficient as (solver drag factor) * (len/wid). + // However, this greatly underestimates a fuselage's lateral drag. + float sideDrag = len/wid; + + if ( isVersionOrNewer( YASIM_VERSION_32 ) ) { + // New YASim assumes a fixed lateral drag coefficient of 0.5. + // This will not be multiplied by the solver drag factor, because + // that factor is tuned to match the drag in the direction of + // flight, which is completely independent of lateral drag. + // The value of 0.5 is only a ballpark estimate, roughly matching + // the side-on drag for a long cylinder at the higher Reynolds + // numbers typical for an aircraft's lateral drag. + // This fits if the fuselage is long and has a round cross section. + // For flat-sided fuselages, the value should be increased, up to + // a limit of around 2 for a long rectangular prism. + // For very short fuselages, in which the end effects are strong, + // the value should be reduced. + // Such adjustments can be made using the fuselage's "cy" and "cz" + // XML parameters: "cy" for the sides, "cz" for top and bottom. + sideDrag = 0.5; + } + float dragCoefficient = scale*segWgt*f->_cx; + if( isVersionOrNewer( YASIM_VERSION_32 ) ) { + dragCoefficient = scale*segWgt; + } + // Make a Surface too - Surface* s = new Surface(this); - s->setPosition(pos); - - // The following is the original YASim value for sideDrag. - // Originally YASim calculated the fuselage's lateral drag - // coefficient as (solver drag factor) * (len/wid). - // However, this greatly underestimates a fuselage's lateral drag. - float sideDrag = len/wid; - - if ( isVersionOrNewer( YASIM_VERSION_32 ) ) { - // New YASim assumes a fixed lateral drag coefficient of 0.5. - // This will not be multiplied by the solver drag factor, because - // that factor is tuned to match the drag in the direction of - // flight, which is completely independent of lateral drag. - // The value of 0.5 is only a ballpark estimate, roughly matching - // the side-on drag for a long cylinder at the higher Reynolds - // numbers typical for an aircraft's lateral drag. - // This fits if the fuselage is long and has a round cross section. - // For flat-sided fuselages, the value should be increased, up to - // a limit of around 2 for a long rectangular prism. - // For very short fuselages, in which the end effects are strong, - // the value should be reduced. - // Such adjustments can be made using the fuselage's "cy" and "cz" - // XML parameters: "cy" for the sides, "cz" for top and bottom. - sideDrag = 0.5; - } - - if( isVersionOrNewer( YASIM_VERSION_32 ) ) { - s->setXDrag(f->_cx); - } + Surface* s = new Surface(this, pos, dragCoefficient); + if( isVersionOrNewer( YASIM_VERSION_32 ) ) { + s->setDragCoefficient(f->_cx); + } s->setYDrag(sideDrag*f->_cy); - s->setZDrag(sideDrag*f->_cz); - if( isVersionOrNewer( YASIM_VERSION_32 ) ) { - s->setTotalDrag(scale*segWgt); - } else { - s->setTotalDrag(scale*segWgt*f->_cx); - } + s->setLiftCoefficient(sideDrag*f->_cz); s->setInducedDrag(f->_idrag); // FIXME: fails for fuselages aligned along the Y axis @@ -449,8 +419,8 @@ float Airplane::compileFuselage(Fuselage* f) Math::unit3(fwd, x); y[0] = 0; y[1] = 1; y[2] = 0; Math::cross3(x, y, z); - Math::unit3(z, z); - Math::cross3(z, x, y); + Math::unit3(z, z); + Math::cross3(z, x, y); s->setOrientation(o); _model.addSurface(s); @@ -465,9 +435,6 @@ void Airplane::compileGear(GearRec* gr) { Gear* g = gr->gear; - // Make a Surface object for the aerodynamic behavior - Surface* s = new Surface(this); - gr->surf = s; // Put the surface at the half-way point on the gear strut, give // it a drag coefficient equal to a square of the same dimension @@ -480,8 +447,9 @@ void Airplane::compileGear(GearRec* gr) Math::mul3(0.5, cmp, cmp); Math::add3(pos, cmp, pos); - s->setPosition(pos); - s->setTotalDrag(length*length); + // Make a Surface object for the aerodynamic behavior + Surface* s = new Surface(this, pos, length*length); + gr->surf = s; _model.addGear(g); _model.addSurface(s); @@ -543,28 +511,38 @@ void Airplane::compile() // The Wing objects if (_wing) { - if (baseN != 0) _wingsN = baseN->getChild("wing", 0, true); - aeroWgt += compileWing(_wing); - - // convert % to absolute x coordinates - _cgDesiredFront = _wing->getMACx() - _wing->getMAC()*_cgDesiredMin; - _cgDesiredAft = _wing->getMACx() - _wing->getMAC()*_cgDesiredMax; - if (baseN != 0) { - SGPropertyNode_ptr n = fgGetNode("/fdm/yasim/model", true); - n->getNode("cg-x-range-front", true)->setFloatValue(_cgDesiredFront); - n->getNode("cg-x-range-aft", true)->setFloatValue(_cgDesiredAft); - } + if (baseN != 0) { + _wingsN = baseN->getChild("wing", 0, true); + _wing->setPropertyNode(_wingsN); + } + aeroWgt += compileWing(_wing); + + // convert % to absolute x coordinates + _cgDesiredFront = _wing->getMACx() - _wing->getMACLength()*_cgDesiredMin; + _cgDesiredAft = _wing->getMACx() - _wing->getMACLength()*_cgDesiredMax; + if (baseN != 0) { + SGPropertyNode_ptr n = fgGetNode("/fdm/yasim/model", true); + n->getNode("cg-x-range-front", true)->setFloatValue(_cgDesiredFront); + n->getNode("cg-x-range-aft", true)->setFloatValue(_cgDesiredAft); + } } if (_tail) { - if (baseN != 0) _wingsN = baseN->getChild("tail", 0, true); - aeroWgt += compileWing(_tail); + if (baseN != 0) { + _wingsN = baseN->getChild("tail", 0, true); + _tail->setPropertyNode(_wingsN); + } + aeroWgt += compileWing(_tail); } int i; for(i=0; i<_vstabs.size(); i++) { - if (baseN != 0) _wingsN = baseN->getChild("stab", i, true); - aeroWgt += compileWing((Wing*)_vstabs.get(i)); + Wing* vs = (Wing*)_vstabs.get(i); + if (baseN != 0) { + _wingsN = baseN->getChild("stab", i, true); + vs->setPropertyNode(_wingsN); + } + aeroWgt += compileWing(vs); } // The fuselage(s) @@ -578,9 +556,13 @@ void Airplane::compile() // Rescale to the specified empty weight float wscale = (_emptyWeight-nonAeroWgt)/aeroWgt; - for(i=firstMass; inumMasses(); i++) + for(i=firstMass; inumMasses(); i++) { body->setMass(i, body->getMass(i)*wscale); - + } + if (_wingsN != nullptr) { + float w = _wingsN->getNode("weight", true)->getFloatValue(); + _wingsN->getNode("mass", true)->setFloatValue(w * wscale); + } // Add the thruster masses for(i=0; i<_thrusters.size(); i++) { ThrustRec* t = (ThrustRec*)_thrusters.get(i); @@ -745,13 +727,13 @@ void Airplane::setupWeights(bool isApproach) } } -/// load values for controls as defined in cruise configuration +/// load values for controls as defined in cruise/approach configuration void Airplane::loadControls(const Vector& controls) { _controls.reset(); for(int i=0; i < controls.size(); i++) { - Control* c = (Control*)controls.get(i); - _controls.setInput(c->control, c->val); + ControlSetting* c = (ControlSetting*)controls.get(i); + _controls.setInput(c->propHandle, c->val); } _controls.applyControls(); } @@ -800,43 +782,43 @@ void Airplane::applyDragFactor(float factor) float applied = Math::pow(factor, SOLVE_TWEAK); _dragFactor *= applied; if(_wing) - _wing->setDragScale(_wing->getDragScale() * applied); + _wing->multiplyDragCoefficient(applied); if(_tail) - _tail->setDragScale(_tail->getDragScale() * applied); + _tail->multiplyDragCoefficient(applied); int i; for(i=0; i<_vstabs.size(); i++) { - Wing* w = (Wing*)_vstabs.get(i); - w->setDragScale(w->getDragScale() * applied); + Wing* w = (Wing*)_vstabs.get(i); + w->multiplyDragCoefficient(applied); } for(i=0; i<_fuselages.size(); i++) { - Fuselage* f = (Fuselage*)_fuselages.get(i); - int j; - for(j=0; jsurfs.size(); j++) { - Surface* s = (Surface*)f->surfs.get(j); - if( isVersionOrNewer( YASIM_VERSION_32 ) ) { - // For new YASim, the solver drag factor is only applied to - // the X axis for Fuselage Surfaces. - // The solver is tuning the coefficient for longitudinal drag, - // along the direction of flight. A fuselage's lateral drag - // is completely independent and is normally much higher; - // it won't be affected by the streamlining done to reduce - // longitudinal drag. So the solver should only adjust the - // fuselage's longitudinal (X axis) drag coefficient. - s->setXDrag(s->getXDrag() * applied); - } else { - // Originally YASim applied the drag factor to all axes - // for Fuselage Surfaces. - s->setTotalDrag(s->getTotalDrag() * applied); - } - } + Fuselage* f = (Fuselage*)_fuselages.get(i); + int j; + for(j=0; jsurfs.size(); j++) { + Surface* s = (Surface*)f->surfs.get(j); + if( isVersionOrNewer( YASIM_VERSION_32 ) ) { + // For new YASim, the solver drag factor is only applied to + // the X axis for Fuselage Surfaces. + // The solver is tuning the coefficient for longitudinal drag, + // along the direction of flight. A fuselage's lateral drag + // is completely independent and is normally much higher; + // it won't be affected by the streamlining done to reduce + // longitudinal drag. So the solver should only adjust the + // fuselage's longitudinal (X axis) drag coefficient. + s->setDragCoefficient(s->getDragCoefficient() * applied); + } else { + // Originally YASim applied the drag factor to all axes + // for Fuselage Surfaces. + s->mulTotalForceCoefficient(applied); + } + } } for(i=0; i<_weights.size(); i++) { - WeightRec* wr = (WeightRec*)_weights.get(i); - wr->surf->setTotalDrag(wr->surf->getTotalDrag() * applied); + WeightRec* wr = (WeightRec*)_weights.get(i); + wr->surf->mulTotalForceCoefficient(applied); } for(i=0; i<_gears.size(); i++) { - GearRec* gr = (GearRec*)_gears.get(i); - gr->surf->setTotalDrag(gr->surf->getTotalDrag() * applied); + GearRec* gr = (GearRec*)_gears.get(i); + gr->surf->mulTotalForceCoefficient(applied); } } @@ -846,13 +828,13 @@ void Airplane::applyLiftRatio(float factor) float applied = Math::pow(factor, SOLVE_TWEAK); _liftRatio *= applied; if(_wing) - _wing->setLiftRatio(_wing->getLiftRatio() * applied); + _wing->multiplyLiftRatio(applied); if(_tail) - _tail->setLiftRatio(_tail->getLiftRatio() * applied); + _tail->multiplyLiftRatio(applied); int i; for(i=0; i<_vstabs.size(); i++) { Wing* w = (Wing*)_vstabs.get(i); - w->setLiftRatio(w->getLiftRatio() * applied); + w->multiplyLiftRatio(applied); } } @@ -878,57 +860,57 @@ void Airplane::solve() return; } - // Run an iteration at cruise, and extract the needed numbers: - runConfig(_cruiseConfig); + // Run an iteration at cruise, and extract the needed numbers: + runConfig(_cruiseConfig); - _model.getThrust(tmp); + _model.getThrust(tmp); float thrust = tmp[0] + _cruiseConfig.weight * Math::sin(_cruiseConfig.glideAngle) * 9.81; - _model.getBody()->getAccel(tmp); + _model.getBody()->getAccel(tmp); _cruiseConfig.state.localToGlobal(tmp, tmp); - float xforce = _cruiseConfig.weight * tmp[0]; - float clift0 = _cruiseConfig.weight * tmp[2]; + float xforce = _cruiseConfig.weight * tmp[0]; + float clift0 = _cruiseConfig.weight * tmp[2]; - _model.getBody()->getAngularAccel(tmp); + _model.getBody()->getAngularAccel(tmp); _cruiseConfig.state.localToGlobal(tmp, tmp); - float pitch0 = tmp[1]; + float pitch0 = tmp[1]; - // Run an approach iteration, and do likewise + // Run an approach iteration, and do likewise runConfig(_approachConfig); - _model.getBody()->getAngularAccel(tmp); + _model.getBody()->getAngularAccel(tmp); _approachConfig.state.localToGlobal(tmp, tmp); - double apitch0 = tmp[1]; + double apitch0 = tmp[1]; - _model.getBody()->getAccel(tmp); + _model.getBody()->getAccel(tmp); _approachConfig.state.localToGlobal(tmp, tmp); - float alift = _approachConfig.weight * tmp[2]; + float alift = _approachConfig.weight * tmp[2]; - // Modify the cruise AoA a bit to get a derivative - _cruiseConfig.aoa += ARCMIN; + // Modify the cruise AoA a bit to get a derivative + _cruiseConfig.aoa += ARCMIN; runConfig(_cruiseConfig); _cruiseConfig.aoa -= ARCMIN; - - _model.getBody()->getAccel(tmp); + + _model.getBody()->getAccel(tmp); _cruiseConfig.state.localToGlobal(tmp, tmp); - float clift1 = _cruiseConfig.weight * tmp[2]; + float clift1 = _cruiseConfig.weight * tmp[2]; - // Do the same with the tail incidence - _tail->setIncidence(_tailIncidence + ARCMIN); + // Do the same with the tail incidence + _tail->setIncidence(_tailIncidence + ARCMIN); runConfig(_cruiseConfig); _tail->setIncidence(_tailIncidence); - _model.getBody()->getAngularAccel(tmp); + _model.getBody()->getAngularAccel(tmp); _cruiseConfig.state.localToGlobal(tmp, tmp); - float pitch1 = tmp[1]; + float pitch1 = tmp[1]; - // Now calculate: - float awgt = 9.8f * _approachConfig.weight; + // Now calculate: + float awgt = 9.8f * _approachConfig.weight; - float dragFactor = thrust / (thrust-xforce); - float liftFactor = awgt / (awgt+alift); - float aoaDelta = -clift0 * (ARCMIN/(clift1-clift0)); - float tailDelta = -pitch0 * (ARCMIN/(pitch1-pitch0)); + float dragFactor = thrust / (thrust-xforce); + float liftFactor = awgt / (awgt+alift); + float aoaDelta = -clift0 * (ARCMIN/(clift1-clift0)); + float tailDelta = -pitch0 * (ARCMIN/(pitch1-pitch0)); // Sanity: if(dragFactor <= 0 || liftFactor <= 0) @@ -943,36 +925,36 @@ void Airplane::solve() runConfig(_approachConfig); _approachElevator.val -= ELEVDIDDLE; - _model.getBody()->getAngularAccel(tmp); + _model.getBody()->getAngularAccel(tmp); _approachConfig.state.localToGlobal(tmp, tmp); - double apitch1 = tmp[1]; + double apitch1 = tmp[1]; float elevDelta = -apitch0 * (ELEVDIDDLE/(apitch1-apitch0)); // Now apply the values we just computed. Note that the // "minor" variables are deferred until we get the lift/drag // numbers in the right ballpark. - applyDragFactor(dragFactor); - applyLiftRatio(liftFactor); + applyDragFactor(dragFactor); + applyLiftRatio(liftFactor); - // DON'T do the following until the above are sane - if(normFactor(dragFactor) > STHRESH*1.0001 - || normFactor(liftFactor) > STHRESH*1.0001) - { - continue; - } + // DON'T do the following until the above are sane + if(normFactor(dragFactor) > STHRESH*1.0001 + || normFactor(liftFactor) > STHRESH*1.0001) + { + continue; + } - // OK, now we can adjust the minor variables: - _cruiseConfig.aoa += SOLVE_TWEAK*aoaDelta; - _tailIncidence += SOLVE_TWEAK*tailDelta; - - _cruiseConfig.aoa = Math::clamp(_cruiseConfig.aoa, -0.175f, 0.175f); - _tailIncidence = Math::clamp(_tailIncidence, -0.175f, 0.175f); + // OK, now we can adjust the minor variables: + _cruiseConfig.aoa += SOLVE_TWEAK*aoaDelta; + _tailIncidence += SOLVE_TWEAK*tailDelta; + + _cruiseConfig.aoa = Math::clamp(_cruiseConfig.aoa, -0.175f, 0.175f); + _tailIncidence = Math::clamp(_tailIncidence, -0.175f, 0.175f); if(abs(xforce/_cruiseConfig.weight) < STHRESH*0.0001 && - abs(alift/_approachConfig.weight) < STHRESH*0.0001 && - abs(aoaDelta) < STHRESH*.000017 && - abs(tailDelta) < STHRESH*.000017) + abs(alift/_approachConfig.weight) < STHRESH*0.0001 && + abs(aoaDelta) < STHRESH*.000017 && + abs(tailDelta) < STHRESH*.000017) { // If this finaly value is OK, then we're all done if(abs(elevDelta) < STHRESH*0.0001) @@ -988,17 +970,17 @@ void Airplane::solve() } if(_dragFactor < 1e-06 || _dragFactor > 1e6) { - _failureMsg = "Drag factor beyond reasonable bounds."; - return; + _failureMsg = "Drag factor beyond reasonable bounds."; + return; } else if(_liftRatio < 1e-04 || _liftRatio > 1e4) { - _failureMsg = "Lift ratio beyond reasonable bounds."; - return; + _failureMsg = "Lift ratio beyond reasonable bounds."; + return; } else if(Math::abs(_cruiseConfig.aoa) >= .17453293) { - _failureMsg = "Cruise AoA > 10 degrees"; - return; + _failureMsg = "Cruise AoA > 10 degrees"; + return; } else if(Math::abs(_tailIncidence) >= .17453293) { - _failureMsg = "Tail incidence > 10 degrees"; - return; + _failureMsg = "Tail incidence > 10 degrees"; + return; } } @@ -1034,7 +1016,7 @@ float Airplane::getCGMAC() if (_wing) { float cg[3]; _model.getBody()->getCG(cg); - return (_wing->getMACx() - cg[0]) / _wing->getMAC(); + return (_wing->getMACx() - cg[0]) / _wing->getMACLength(); } return 0; } diff --git a/src/FDM/YASim/Airplane.hpp b/src/FDM/YASim/Airplane.hpp index d985d6734..7304e986c 100644 --- a/src/FDM/YASim/Airplane.hpp +++ b/src/FDM/YASim/Airplane.hpp @@ -24,6 +24,11 @@ public: Airplane(); ~Airplane(); + enum Configuration { + APPROACH, + CRUISE, + }; + void iterate(float dt); void calcFuelWeights(); @@ -37,9 +42,9 @@ public: void setEmptyWeight(float weight) { _emptyWeight = weight; } - void setWing(Wing* wing) { _wing = wing; } - Wing* getWing() { return _wing; } - void setTail(Wing* tail) { _tail = tail; } + Wing* getWing(); + bool hasWing() const { return (_wing != nullptr); } + Wing* getTail(); void addVStab(Wing* vstab) { _vstabs.add(vstab); } void addFuselage(float* front, float* back, float width, @@ -59,11 +64,10 @@ public: void setApproach(float speed, float altitude, float aoa, float fuel, float gla); void setCruise(float speed, float altitude, float fuel, float gla); - void setElevatorControl(int control); - void addApproachControl(int control, float val); - void addCruiseControl(int control, float val); + void setElevatorControl(const char* prop); + void addControlSetting(Configuration cfg, const char* prop, float val); - void addSolutionWeight(bool approach, int idx, float wgt); + void addSolutionWeight(Configuration cfg, int idx, float wgt); int numGear() const { return _gears.size(); } Gear* getGear(int g) { return ((GearRec*)_gears.get(g))->gear; } @@ -134,8 +138,8 @@ private: float cg[3]; float mass; }; - struct Control { - int control; + struct ControlSetting { + int propHandle; float val; }; struct WeightRec { @@ -210,7 +214,7 @@ private: float _dragFactor {1}; float _liftRatio {1}; float _tailIncidence {0}; - Control _approachElevator; + ControlSetting _approachElevator; const char* _failureMsg {0}; float _cgMax {-1e6}; // hard limits for cg from gear position diff --git a/src/FDM/YASim/Atmosphere.cpp b/src/FDM/YASim/Atmosphere.cpp index 5e751dbce..dd1649993 100644 --- a/src/FDM/YASim/Atmosphere.cpp +++ b/src/FDM/YASim/Atmosphere.cpp @@ -211,7 +211,7 @@ bool Atmosphere::test() { fprintf(stderr, "Columns = %d\n", numColumns); fprintf(stderr, "Rows = %d\n", rows); - for (int alt = 0; alt < maxTableIndex(); alt++) { + for (int alt = 0; alt <= maxTableIndex(); alt++) { float density = calcStdDensity(data[alt][PRESSURE], data[alt][TEMPERATURE]); float delta = data[alt][DENSITY] - density; fprintf(stderr, "%d : %f \n", alt, delta); diff --git a/src/FDM/YASim/CMakeLists.txt b/src/FDM/YASim/CMakeLists.txt index 0d7efa881..3065af68a 100644 --- a/src/FDM/YASim/CMakeLists.txt +++ b/src/FDM/YASim/CMakeLists.txt @@ -26,6 +26,7 @@ set(COMMON Turbulence.cpp Wing.cpp Version.cpp + yasim-common.cpp ) set(SOURCES diff --git a/src/FDM/YASim/ControlMap.cpp b/src/FDM/YASim/ControlMap.cpp index bf56615f9..679d7aec5 100644 --- a/src/FDM/YASim/ControlMap.cpp +++ b/src/FDM/YASim/ControlMap.cpp @@ -2,6 +2,8 @@ # include "config.h" #endif +#include + #include "Jet.hpp" #include "Thruster.hpp" #include "PropEngine.hpp" @@ -41,18 +43,14 @@ ControlMap::~ControlMap() } /** - input : index to _inputs - type: identifier (see enum OutputType) +prop: name of input property +control: identifier (see enum OutputType) +object: object to which this input belongs to +options: bits OPT_INVERT, OPT_SPLIT, OPT_SQUARE */ -void ControlMap::addMapping(int input, int type, void* object, int options, - float src0, float src1, float dst0, float dst1) +void ControlMap::addMapping(const char* prop, Control control, ObjectID id, int options, float src0, float src1, float dst0, float dst1) { - addMapping(input, type, object, options); - - // The one we just added is last in the list (ugly, awful hack!) - Vector* maps = (Vector*)_inputs.get(input); - MapRec* m = (MapRec*)maps->get(maps->size() - 1); - + MapRec* m = (MapRec*)addMapping(prop, control, id, options); m->src0 = src0; m->src1 = src1; m->dst0 = dst0; @@ -60,29 +58,33 @@ void ControlMap::addMapping(int input, int type, void* object, int options, } /** - input : index to _inputs - type: identifier (see enum OutputType) +prop: name of input property +control: identifier (see enum OutputType) +object: object to which this input belongs to +options: bits OPT_INVERT, OPT_SPLIT, OPT_SQUARE */ -void ControlMap::addMapping(int input, int type, void* object, int options) +void* ControlMap::addMapping(const char* prop, Control control, ObjectID id, int options) { + int inputPropHandle = getInputPropertyHandle(prop); // See if the output object already exists - OutRec* out = 0; + OutRec* out {nullptr}; int i; - for(i=0; i<_outputs.size(); i++) { - OutRec* o = (OutRec*)_outputs.get(i); - if(o->object == object && o->type == type) { - out = o; - break; - } + for(i = 0; i < _outputs.size(); i++) { + OutRec* o = (OutRec*)_outputs.get(i); + if(o->oid.object == id.object && o->oid.subObj == id.subObj + && o->control == control) + { + out = o; + break; + } } // Create one if it doesn't - if(out == 0) { - out = new OutRec(); - out->type = type; - out->object = object; - out->oldL = out->oldR = out->time = 0; - _outputs.add(out); + if(out == nullptr) { + out = new OutRec(); + out->control = control; + out->oid = id; + _outputs.add(out); } // Make a new input record @@ -92,51 +94,50 @@ void ControlMap::addMapping(int input, int type, void* object, int options) map->idx = out->maps.add(map); // The default ranges differ depending on type! - map->src1 = map->dst1 = rangeMax(type); - map->src0 = map->dst0 = rangeMin(type); + map->src1 = map->dst1 = rangeMax(control); + map->src0 = map->dst0 = rangeMin(control); // And add it to the approproate vectors. - Vector* maps = (Vector*)_inputs.get(input); + Vector* maps = (Vector*)_inputs.get(inputPropHandle); maps->add(map); + return map; } void ControlMap::reset() { // Set all the values to zero - for(int i=0; i<_outputs.size(); i++) { - OutRec* o = (OutRec*)_outputs.get(i); - for(int j=0; jmaps.size(); j++) - ((MapRec*)(o->maps.get(j)))->val = 0; + for(int i = 0; i < _outputs.size(); i++) { + OutRec* o = (OutRec*)_outputs.get(i); + for(int j = 0; j < o->maps.size(); j++) { + ((MapRec*)(o->maps.get(j)))->val = 0; + } } } void ControlMap::setInput(int input, float val) { Vector* maps = (Vector*)_inputs.get(input); - for(int i=0; isize(); i++) { - MapRec* m = (MapRec*)maps->get(i); - - float val2 = val; - - // Do the scaling operation. Clamp to [src0:src1], rescale to - // [0:1] within that range, then map to [dst0:dst1]. - if(val2 < m->src0) val2 = m->src0; - if(val2 > m->src1) val2 = m->src1; - val2 = (val2 - m->src0) / (m->src1 - m->src0); - val2 = m->dst0 + val2 * (m->dst1 - m->dst0); - - m->val = val2; + for(int i = 0; i < maps->size(); i++) { + MapRec* m = (MapRec*)maps->get(i); + float val2 = val; + // Do the scaling operation. Clamp to [src0:src1], rescale to + // [0:1] within that range, then map to [dst0:dst1]. + val2 = Math::clamp(val2, m->src0, m->src1); + val2 = (val2 - m->src0) / (m->src1 - m->src0); + m->val = m->dst0 + val2 * (m->dst1 - m->dst0); } } -int ControlMap::getOutputHandle(void* obj, int type) +int ControlMap::getOutputHandle(ObjectID id, Control control) { - for(int i=0; i<_outputs.size(); i++) { - OutRec* o = (OutRec*)_outputs.get(i); - if(o->object == obj && o->type == type) - return i; + for(int i = 0; i < _outputs.size(); i++) { + OutRec* o = (OutRec*)_outputs.get(i); + if(o->oid.object == id.object && o->oid.subObj == id.subObj + && o->control == control) + return i; } - return 0; + fprintf(stderr, "ControlMap::getOutputHandle cannot find *%ld, control %d \nMissing in XML?!", (long)id.object, control); + return -1; } void ControlMap::setTransitionTime(int handle, float time) @@ -157,27 +158,28 @@ float ControlMap::getOutputR(int handle) void ControlMap::applyControls(float dt) { int outrec; - for(outrec=0; outrec<_outputs.size(); outrec++) { - OutRec* o = (OutRec*)_outputs.get(outrec); - - // Generate a summed value. Note the check for "split" - // control axes like ailerons. - float lval = 0, rval = 0; - int i; - for(i=0; imaps.size(); i++) { - MapRec* m = (MapRec*)o->maps.get(i); - float val = m->val; + for(outrec=0; outrec<_outputs.size(); outrec++) + { + OutRec* o = (OutRec*)_outputs.get(outrec); + + // Generate a summed value. Note the check for "split" + // control axes like ailerons. + float lval = 0, rval = 0; + int i; + for(i=0; imaps.size(); i++) { + MapRec* m = (MapRec*)o->maps.get(i); + float val = m->val; - if(m->opt & OPT_SQUARE) - val = val * Math::abs(val); - if(m->opt & OPT_INVERT) - val = -val; - lval += val; - if(m->opt & OPT_SPLIT) - rval -= val; - else - rval += val; - } + if(m->opt & OPT_SQUARE) + val = val * Math::abs(val); + if(m->opt & OPT_INVERT) + val = -val; + lval += val; + if(m->opt & OPT_SPLIT) + rval -= val; + else + rval += val; + } // If there is a finite transition time, clamp the values to // the maximum travel allowed in this dt. @@ -187,7 +189,7 @@ void ControlMap::applyControls(float dt) float adl = Math::abs(dl); float adr = Math::abs(dr); - float max = (dt/o->time) * (rangeMax(o->type) - rangeMin(o->type)); + float max = (dt/o->time) * (rangeMax(o->control) - rangeMin(o->control)); if(adl > max) dl = dl*max/adl; if(adr > max) dr = dr*max/adr; @@ -198,133 +200,250 @@ void ControlMap::applyControls(float dt) o->oldL = lval; o->oldR = rval; - void* obj = o->object; - switch(o->type) { - case THROTTLE: ((Thruster*)obj)->setThrottle(lval); break; - case MIXTURE: ((Thruster*)obj)->setMixture(lval); break; - case CONDLEVER: ((TurbineEngine*)((PropEngine*) - obj)->getEngine())->setCondLever(lval); break; - case STARTER: ((Thruster*)obj)->setStarter(lval != 0.0); break; - case MAGNETOS: ((PropEngine*)obj)->setMagnetos((int)lval); break; - case ADVANCE: ((PropEngine*)obj)->setAdvance(lval); break; - case PROPPITCH: ((PropEngine*)obj)->setPropPitch(lval); break; - case PROPFEATHER: ((PropEngine*)obj)->setPropFeather((int)lval); break; - case REHEAT: ((Jet*)obj)->setReheat(lval); break; - case VECTOR: ((Jet*)obj)->setRotation(lval); break; - case BRAKE: ((Gear*)obj)->setBrake(lval); break; - case STEER: ((Gear*)obj)->setRotation(lval); break; - case EXTEND: ((Gear*)obj)->setExtension(lval); break; - case HEXTEND: ((Hook*)obj)->setExtension(lval); break; - case LEXTEND: ((Launchbar*)obj)->setExtension(lval); break; - case LACCEL: ((Launchbar*)obj)->setAcceleration(lval); break; - case CASTERING:((Gear*)obj)->setCastering(lval != 0); break; - case SLAT: ((Wing*)obj)->setSlatPos(lval); break; - case FLAP0: ((Wing*)obj)->setFlap0Pos(lval, rval); break; - case FLAP0EFFECTIVENESS: ((Wing*)obj)->setFlap0Effectiveness(lval); break; - case FLAP1: ((Wing*)obj)->setFlap1Pos(lval, rval); break; - case FLAP1EFFECTIVENESS: ((Wing*)obj)->setFlap1Effectiveness(lval); break; - case SPOILER: ((Wing*)obj)->setSpoilerPos(lval, rval); break; - case COLLECTIVE: ((Rotor*)obj)->setCollective(lval); break; - case CYCLICAIL: ((Rotor*)obj)->setCyclicail(lval,rval); break; - case CYCLICELE: ((Rotor*)obj)->setCyclicele(lval,rval); break; - case TILTPITCH: ((Rotor*)obj)->setTiltPitch(lval); break; - case TILTYAW: ((Rotor*)obj)->setTiltYaw(lval); break; - case TILTROLL: ((Rotor*)obj)->setTiltRoll(lval); break; - case ROTORBALANCE: - ((Rotor*)obj)->setRotorBalance(lval); break; - case ROTORBRAKE: ((Rotorgear*)obj)->setRotorBrake(lval); break; - case ROTORENGINEON: - ((Rotorgear*)obj)->setEngineOn((int)lval); break; - case ROTORENGINEMAXRELTORQUE: - ((Rotorgear*)obj)->setRotorEngineMaxRelTorque(lval); break; - case ROTORRELTARGET: - ((Rotorgear*)obj)->setRotorRelTarget(lval); break; - case REVERSE_THRUST: ((Jet*)obj)->setReverse(lval != 0); break; - case BOOST: - ((PistonEngine*)((Thruster*)obj)->getEngine())->setBoost(lval); - break; - case WASTEGATE: - ((PistonEngine*)((Thruster*)obj)->getEngine())->setWastegate(lval); - break; - case WINCHRELSPEED: ((Hitch*)obj)->setWinchRelSpeed(lval); break; - case HITCHOPEN: ((Hitch*)obj)->setOpen(lval!=0); break; - case PLACEWINCH: ((Hitch*)obj)->setWinchPositionAuto(lval!=0); break; - case FINDAITOW: ((Hitch*)obj)->findBestAIObject(lval!=0); break; - } + void* obj = o->oid.object; + switch(o->control) { + case THROTTLE: + ((Thruster*)obj)->setThrottle(lval); + break; + case MIXTURE: + ((Thruster*)obj)->setMixture(lval); + break; + case CONDLEVER: + ((TurbineEngine*)((PropEngine*)obj)->getEngine())->setCondLever(lval); + break; + case STARTER: + ((Thruster*)obj)->setStarter(lval != 0.0); + break; + case MAGNETOS: + ((PropEngine*)obj)->setMagnetos((int)lval); + break; + case ADVANCE: + ((PropEngine*)obj)->setAdvance(lval); + break; + case PROPPITCH: + ((PropEngine*)obj)->setPropPitch(lval); + break; + case PROPFEATHER: + ((PropEngine*)obj)->setPropFeather((int)lval); + break; + case REHEAT: + ((Jet*)obj)->setReheat(lval); + break; + case VECTOR: + ((Jet*)obj)->setRotation(lval); + break; + case BRAKE: + ((Gear*)obj)->setBrake(lval); + break; + case STEER: + ((Gear*)obj)->setRotation(lval); + break; + case EXTEND: + ((Gear*)obj)->setExtension(lval); + break; + case HEXTEND: + ((Hook*)obj)->setExtension(lval); + break; + case LEXTEND: + ((Launchbar*)obj)->setExtension(lval); + break; + case LACCEL: + ((Launchbar*)obj)->setAcceleration(lval); + break; + case CASTERING: + ((Gear*)obj)->setCastering(lval != 0); + break; + case SLAT: + ((Wing*)obj)->setFlapPos(WING_SLAT,lval); + break; + case FLAP0: + ((Wing*)obj)->setFlapPos(WING_FLAP0, lval, rval); + break; + case FLAP0EFFECTIVENESS: + ((Wing*)obj)->setFlapEffectiveness(WING_FLAP0,lval); + break; + case FLAP1: + ((Wing*)obj)->setFlapPos(WING_FLAP1,lval, rval); + break; + case FLAP1EFFECTIVENESS: + ((Wing*)obj)->setFlapEffectiveness(WING_FLAP1,lval); + break; + case SPOILER: + ((Wing*)obj)->setFlapPos(WING_SPOILER, lval, rval); + break; + case COLLECTIVE: + ((Rotor*)obj)->setCollective(lval); + break; + case CYCLICAIL: + ((Rotor*)obj)->setCyclicail(lval,rval); + break; + case CYCLICELE: + ((Rotor*)obj)->setCyclicele(lval,rval); + break; + case TILTPITCH: + ((Rotor*)obj)->setTiltPitch(lval); + break; + case TILTYAW: + ((Rotor*)obj)->setTiltYaw(lval); + break; + case TILTROLL: + ((Rotor*)obj)->setTiltRoll(lval); + break; + case ROTORBALANCE: + ((Rotor*)obj)->setRotorBalance(lval); + break; + case ROTORBRAKE: + ((Rotorgear*)obj)->setRotorBrake(lval); + break; + case ROTORENGINEON: + ((Rotorgear*)obj)->setEngineOn((int)lval); + break; + case ROTORENGINEMAXRELTORQUE: + ((Rotorgear*)obj)->setRotorEngineMaxRelTorque(lval); + break; + case ROTORRELTARGET: + ((Rotorgear*)obj)->setRotorRelTarget(lval); + break; + case REVERSE_THRUST: + ((Jet*)obj)->setReverse(lval != 0); + break; + case BOOST: + ((PistonEngine*)((Thruster*)obj)->getEngine())->setBoost(lval); + break; + case WASTEGATE: + ((PistonEngine*)((Thruster*)obj)->getEngine())->setWastegate(lval); + break; + case WINCHRELSPEED: + ((Hitch*)obj)->setWinchRelSpeed(lval); + break; + case HITCHOPEN: + ((Hitch*)obj)->setOpen(lval!=0); + break; + case PLACEWINCH: + ((Hitch*)obj)->setWinchPositionAuto(lval!=0); + break; + case FINDAITOW: + ((Hitch*)obj)->findBestAIObject(lval!=0); + break; + case PROP: + break; + case INCIDENCE: + break; + } } } -float ControlMap::rangeMin(int type) +float ControlMap::rangeMin(Control control) { // The minimum of the range for each type of control - switch(type) { - case FLAP0: return -1; // [-1:1] - case FLAP1: return -1; - case STEER: return -1; - case CYCLICELE: return -1; - case CYCLICAIL: return -1; - case COLLECTIVE: return -1; - case WINCHRELSPEED: return -1; - case MAGNETOS: return 0; // [0:3] - case FLAP0EFFECTIVENESS: return 1; // [0:10] - case FLAP1EFFECTIVENESS: return 1; // [0:10] - default: return 0; // [0:1] + switch(control) { + case FLAP0: return -1; // [-1:1] + case FLAP1: return -1; + case STEER: return -1; + case CYCLICELE: return -1; + case CYCLICAIL: return -1; + case COLLECTIVE: return -1; + case WINCHRELSPEED: return -1; + case MAGNETOS: return 0; // [0:3] + case FLAP0EFFECTIVENESS: return 1; // [0:10] + case FLAP1EFFECTIVENESS: return 1; // [0:10] + default: return 0; // [0:1] } } -float ControlMap::rangeMax(int type) +float ControlMap::rangeMax(Control control) { // The maximum of the range for each type of control - switch(type) { - case FLAP0: return 1; // [-1:1] - case FLAP1: return 1; - case STEER: return 1; - case MAGNETOS: return 3; // [0:3] - case FLAP0EFFECTIVENESS: return 10;// [0:10] - case FLAP1EFFECTIVENESS: return 10;// [0:10] - default: return 1; // [0:1] + switch(control) { + case FLAP0: return 1; // [-1:1] + case FLAP1: return 1; + case STEER: return 1; + case MAGNETOS: return 3; // [0:3] + case FLAP0EFFECTIVENESS: return 10;// [0:10] + case FLAP1EFFECTIVENESS: return 10;// [0:10] + default: return 1; // [0:1] } } -/// duplicate null-terminated string -char* ControlMap::dup(const char* s) -{ - int len=0; - while(s[len++]); - char* s2 = new char[len+1]; - char* p = s2; - while((*p++ = *s++)); - s2[len] = 0; - return s2; -} - -/// compare null-terminated strings -bool ControlMap::eq(const char* a, const char* b) -{ - while(*a && *b && *a == *b) { a++; b++; } - // equal if both a and b points to null chars - return !(*a || *b); -} - /// register property name, return ID (int) -int ControlMap::propertyHandle(const char* name) +int ControlMap::getInputPropertyHandle(const char* name) { - for(int i=0; i < _properties.size(); i++) { - PropHandle* p = (PropHandle*)_properties.get(i); - if(eq(p->name, name)) - return p->handle; - } + for(int i=0; i < _properties.size(); i++) { + PropHandle* p = (PropHandle*)_properties.get(i); + if(!strcmp(p->name, name)) + return p->handle; + } - // create new - PropHandle* p = new PropHandle(); - p->name = dup(name); - - fgGetNode(p->name, true); + // create new + PropHandle* p = new PropHandle(); + p->name = strdup(name); + + fgGetNode(p->name, true); - Vector* v = new Vector(); - p->handle = _inputs.add(v); - _properties.add(p); - return p->handle; + Vector* v = new Vector(); + p->handle = _inputs.add(v); + _properties.add(p); + return p->handle; +} + + +ControlMap::Control ControlMap::parseControl(const char* name) +{ + if(!strcmp(name, "THROTTLE")) return THROTTLE; + if(!strcmp(name, "MIXTURE")) return MIXTURE; + if(!strcmp(name, "CONDLEVER")) return CONDLEVER; + if(!strcmp(name, "STARTER")) return STARTER; + if(!strcmp(name, "MAGNETOS")) return MAGNETOS; + if(!strcmp(name, "ADVANCE")) return ADVANCE; + if(!strcmp(name, "REHEAT")) return REHEAT; + if(!strcmp(name, "BOOST")) return BOOST; + if(!strcmp(name, "VECTOR")) return VECTOR; + if(!strcmp(name, "PROP")) return PROP; + if(!strcmp(name, "BRAKE")) return BRAKE; + if(!strcmp(name, "STEER")) return STEER; + if(!strcmp(name, "EXTEND")) return EXTEND; + if(!strcmp(name, "HEXTEND")) return HEXTEND; + if(!strcmp(name, "LEXTEND")) return LEXTEND; + if(!strcmp(name, "LACCEL")) return LACCEL; + if(!strcmp(name, "INCIDENCE")) return INCIDENCE; + if(!strcmp(name, "FLAP0")) return FLAP0; + if(!strcmp(name, "FLAP0EFFECTIVENESS")) return FLAP0EFFECTIVENESS; + if(!strcmp(name, "FLAP1")) return FLAP1; + if(!strcmp(name, "FLAP1EFFECTIVENESS")) return FLAP1EFFECTIVENESS; + if(!strcmp(name, "SLAT")) return SLAT; + if(!strcmp(name, "SPOILER")) return SPOILER; + if(!strcmp(name, "CASTERING")) return CASTERING; + if(!strcmp(name, "PROPPITCH")) return PROPPITCH; + if(!strcmp(name, "PROPFEATHER")) return PROPFEATHER; + if(!strcmp(name, "COLLECTIVE")) return COLLECTIVE; + if(!strcmp(name, "CYCLICAIL")) return CYCLICAIL; + if(!strcmp(name, "CYCLICELE")) return CYCLICELE; + if(!strcmp(name, "TILTROLL")) return TILTROLL; + if(!strcmp(name, "TILTPITCH")) return TILTPITCH; + if(!strcmp(name, "TILTYAW")) return TILTYAW; + if(!strcmp(name, "ROTORGEARENGINEON")) return ROTORENGINEON; + if(!strcmp(name, "ROTORBRAKE")) return ROTORBRAKE; + if(!strcmp(name, "ROTORENGINEMAXRELTORQUE")) return ROTORENGINEMAXRELTORQUE; + if(!strcmp(name, "ROTORRELTARGET")) return ROTORRELTARGET; + if(!strcmp(name, "ROTORBALANCE")) return ROTORBALANCE; + if(!strcmp(name, "REVERSE_THRUST")) return REVERSE_THRUST; + if(!strcmp(name, "WASTEGATE")) return WASTEGATE; + if(!strcmp(name, "WINCHRELSPEED")) return WINCHRELSPEED; + if(!strcmp(name, "HITCHOPEN")) return HITCHOPEN; + if(!strcmp(name, "PLACEWINCH")) return PLACEWINCH; + if(!strcmp(name, "FINDAITOW")) return FINDAITOW; + SG_LOG(SG_FLIGHT,SG_ALERT,"Unrecognized control type '" << name + << "' in YASim aircraft description."); + exit(1); +} + +ControlMap::ObjectID ControlMap::getObjectID(void* object, int subObj) +{ + assert(object != nullptr); + ObjectID o; + o.object = object; + o.subObj = subObj; + return o; } } // namespace yasim diff --git a/src/FDM/YASim/ControlMap.hpp b/src/FDM/YASim/ControlMap.hpp index dc7f75f79..5c150c1e1 100644 --- a/src/FDM/YASim/ControlMap.hpp +++ b/src/FDM/YASim/ControlMap.hpp @@ -9,42 +9,84 @@ namespace yasim { class ControlMap { public: ~ControlMap(); + + enum Control { + THROTTLE, + MIXTURE, + CONDLEVER, + STARTER, + MAGNETOS, + ADVANCE, + REHEAT, + PROP, + BRAKE, + STEER, + EXTEND, + HEXTEND, + LEXTEND, + LACCEL, + INCIDENCE, + FLAP0, + FLAP1, + SLAT, + SPOILER, + VECTOR, + FLAP0EFFECTIVENESS, + FLAP1EFFECTIVENESS, + BOOST, + CASTERING, + PROPPITCH, + PROPFEATHER, + COLLECTIVE, + CYCLICAIL, + CYCLICELE, + ROTORENGINEON, + TILTYAW, + TILTPITCH, + TILTROLL, + ROTORBRAKE, + ROTORENGINEMAXRELTORQUE, + ROTORRELTARGET, + ROTORBALANCE, + REVERSE_THRUST, + WASTEGATE, + WINCHRELSPEED, + HITCHOPEN, + PLACEWINCH, + FINDAITOW + }; - enum OutputType { THROTTLE, MIXTURE, CONDLEVER, STARTER, MAGNETOS, - ADVANCE, REHEAT, PROP, - BRAKE, STEER, EXTEND, HEXTEND, LEXTEND, LACCEL, - INCIDENCE, FLAP0, FLAP1, SLAT, SPOILER, VECTOR, - FLAP0EFFECTIVENESS, FLAP1EFFECTIVENESS, - BOOST, CASTERING, PROPPITCH, PROPFEATHER, - COLLECTIVE, CYCLICAIL, CYCLICELE, ROTORENGINEON, - TILTYAW, TILTPITCH, TILTROLL, - ROTORBRAKE, ROTORENGINEMAXRELTORQUE, ROTORRELTARGET, - ROTORBALANCE, REVERSE_THRUST, WASTEGATE, - WINCHRELSPEED, HITCHOPEN, PLACEWINCH, FINDAITOW - }; + enum { + OPT_SPLIT = 0x01, + OPT_INVERT = 0x02, + OPT_SQUARE = 0x04 + }; - enum { OPT_SPLIT = 0x01, - OPT_INVERT = 0x02, - OPT_SQUARE = 0x04 }; + struct PropHandle { + char* name {nullptr}; + int handle {0}; + }; + struct ObjectID { + void* object {nullptr}; + int subObj {0}; + }; + + // map control name to int (enum) + Control parseControl(const char* name); + // create ID from object and optional sub index (e.g. for wing section) + ObjectID getObjectID(void* object, int subObj = 0); + + // add input property for a control to an object - struct PropHandle { char* name; int handle; }; - - // Adds a mapping to between input handle and a particular setting - // on an output object. The value of output MUST match the type - // of object! - void addMapping(int input, int output, void* object, int options=0); - - // An additional form to specify a mapping range. Input values - // outside of [src0:src1] are clamped, and are then mapped to - // [dst0:dst1] before being set on the object. - void addMapping(int input, int output, void* object, int options, - float src0, float src1, float dst0, float dst1); + // same with limits. Input values are clamped to [src0:src1] and then mapped to + // [dst0:dst1] before being set on the objects control. + void addMapping(const char* prop, Control control, ObjectID id, int options, float src0, float src1, float dst0, float dst1); // Resets our accumulated input values. Call before any // setInput() invokations. void reset(); - // Sets the specified input (as returned by propertyHandle) to the + // Sets the specified input (as returned by getPropertyHandle()) to the // specified value. void setInput(int propHandle, float value); @@ -55,12 +97,12 @@ public: // Returns the input/output range appropriate for the given // control. Ailerons go from -1 to 1, while throttles are never // lower than zero, etc... - static float rangeMin(int type); - static float rangeMax(int type); + static float rangeMin(Control control); + static float rangeMax(Control control); // Each output record is identified by both an object/type tuple // and a numeric handle. - int getOutputHandle(void* obj, int type); + int getOutputHandle(ObjectID id, Control control); // Sets the transition time for the control output to swing // through its full range. @@ -73,19 +115,29 @@ public: float getOutputR(int handle); // register property name, return handle - int propertyHandle(const char* name); + int getInputPropertyHandle(const char* name); int numProperties() { return _properties.size(); } PropHandle* getProperty(const int i) { return ((PropHandle*)_properties.get(i)); } - // helper - char* dup(const char* s); - bool eq(const char* a, const char* b); - private: - struct OutRec { int type; void* object; Vector maps; - float oldL, oldR, time; }; - struct MapRec { OutRec* out; int idx; int opt; float val; - float src0; float src1; float dst0; float dst1; }; + struct OutRec { + Control control; + ObjectID oid; + Vector maps; + float oldL {0}; + float oldR {0}; + float time {0}; + }; + struct MapRec { + OutRec* out {nullptr}; + int idx {0}; + int opt {0}; + float val {0}; + float src0 {0}; + float src1 {0}; + float dst0 {0}; + float dst1 {0}; + }; // A list of (sub)Vectors containing a bunch of MapRec objects for // each input handle. @@ -95,6 +147,8 @@ private: Vector _outputs; // control properties Vector _properties; + + void* addMapping(const char* prop, Control control, ObjectID id, int options = 0); }; }; // namespace yasim diff --git a/src/FDM/YASim/FGFDM.cpp b/src/FDM/YASim/FGFDM.cpp index 7fe64908b..3c21a5ed7 100644 --- a/src/FDM/YASim/FGFDM.cpp +++ b/src/FDM/YASim/FGFDM.cpp @@ -4,9 +4,11 @@ #include #include +#include #include
+#include "yasim-common.hpp" #include "Math.hpp" #include "Jet.hpp" #include "SimpleJet.hpp" @@ -35,14 +37,10 @@ namespace yasim { FGFDM::FGFDM() { - _vehicle_radius = 0.0f; - - _nextEngine = 0; - // Map /controls/flight/elevator to the approach elevator control. This // should probably be settable, but there are very few aircraft // who trim their approaches using things other than elevator. - _airplane.setElevatorControl(_airplane.getControlMap()->propertyHandle("/controls/flight/elevator-trim")); + _airplane.setElevatorControl("/controls/flight/elevator-trim"); // FIXME: read seed from somewhere? int seed = 0; @@ -218,407 +216,125 @@ void FGFDM::init() } // Not the worlds safest parser. But it's short & sweet. -void FGFDM::startElement(const char* name, const XMLAttributes &atts) +void FGFDM::startElement(const char* name, const XMLAttributes &a) { - XMLAttributes* a = (XMLAttributes*)&atts; - float v[3]; - char buf[64]; - float f = 0; + //XMLAttributes* a = (XMLAttributes*)&atts; + float v[3] {0,0,0}; - if(eq(name, "airplane")) { - if(a->hasAttribute("mass")) { f = attrf(a, "mass") * LBS2KG; } - else if (a->hasAttribute("mass-lbs")) { f = attrf(a, "mass-lbs") * LBS2KG; } - else if (a->hasAttribute("mass-kg")) { f = attrf(a, "mass-kg"); } - else { - SG_LOG(SG_FLIGHT,SG_ALERT,"YASim fatal: missing attribute, airplane needs one of {mass-lbs, mass-kg}"); - exit(1); - } - _airplane.setEmptyWeight(f); - if(a->hasAttribute("version")) { - _airplane.setVersion( a->getValue("version") ); - } - if( !_airplane.isVersionOrNewer( Version::YASIM_VERSION_CURRENT ) ) { - SG_LOG(SG_FLIGHT, SG_DEV_ALERT, "This aircraft does not use the latest yasim configuration version."); - } - _airplane.setDesiredCGRangeInPercentOfMAC(attrf(a, "cg-min", 0.25f), attrf(a, "cg-max", 0.3f)); //FIXME find reasonable defaults - if (attrb(a, "auto-ballast")) { - _airplane.setAutoBallast(true); - } - } else if(eq(name, "approach")) { - float spd, alt = 0; - if (a->hasAttribute("speed")) { spd = attrf(a, "speed") * KTS2MPS; } - else if (a->hasAttribute("speed-kt")) { spd = attrf(a, "speed-kt") * KTS2MPS; } - else if (a->hasAttribute("speed-kmh")) { spd = attrf(a, "speed-kmh") * KMH2MPS; } - else { - SG_LOG(SG_FLIGHT,SG_ALERT,"YASim fatal: missing attribute, approach needs one of {speed-kt, speed-kmh}"); - exit(1); - } - if (a->hasAttribute("alt")) { alt = attrf(a, "alt") * FT2M; } - else if (a->hasAttribute("alt-ft")) { alt = attrf(a, "alt-ft") * FT2M; } - else if (a->hasAttribute("alt-m")) { alt = attrf(a, "alt-m"); } - float aoa = attrf(a, "aoa", 0) * DEG2RAD; - float gla = attrf(a, "glide-angle", 0) * DEG2RAD; - _airplane.setApproach(spd, alt, aoa, attrf(a, "fuel", 0.2), gla); - _cruiseCurr = false; - } else if(eq(name, "cruise")) { - float spd, alt = 0; - if (a->hasAttribute("speed")) { spd = attrf(a, "speed") * KTS2MPS; } - else if (a->hasAttribute("speed-kt")) { spd = attrf(a, "speed-kt") * KTS2MPS; } - else if (a->hasAttribute("speed-kmh")) { spd = attrf(a, "speed-kmh") * KMH2MPS; } - else { - SG_LOG(SG_FLIGHT,SG_ALERT,"YASim fatal: missing attribute, approach needs one of {speed-kt, speed-kmh}"); - exit(1); - } - if (a->hasAttribute("alt")) { alt = attrf(a, "alt") * FT2M; } - else if (a->hasAttribute("alt-ft")) { alt = attrf(a, "alt-ft") * FT2M; } - else if (a->hasAttribute("alt-m")) { alt = attrf(a, "alt-m"); } - float gla = attrf(a, "glide-angle", 0) * DEG2RAD; - _airplane.setCruise(spd, alt, attrf(a, "fuel", 0.5),gla); - _cruiseCurr = true; - } else if(eq(name, "solve-weight")) { - int idx = attri(a, "idx"); - if(a->hasAttribute("weight")) { f = attrf(a, "weight") * LBS2KG; } - else if(a->hasAttribute("weight-lbs")) { f = attrf(a, "weight-lbs") * LBS2KG; } - else if(a->hasAttribute("weight-kg")) { f = attrf(a, "weight-kg"); } - else { - SG_LOG(SG_FLIGHT,SG_ALERT,"YASim fatal: missing attribute, solve-weight needs one of {weight-lbs, weight-kg}"); - exit(1); - } - _airplane.addSolutionWeight(!_cruiseCurr, idx, f); - } else if(eq(name, "cockpit")) { - v[0] = attrf(a, "x"); - v[1] = attrf(a, "y"); - v[2] = attrf(a, "z"); - _airplane.setPilotPos(v); - } else if(eq(name, "rotor")) { - _airplane.getModel()->getRotorgear()->addRotor(parseRotor(a, name)); - } else if(eq(name, "rotorgear")) { - Rotorgear* r = _airplane.getModel()->getRotorgear(); - _currObj = r; - #define p(x) if (a->hasAttribute(#x)) r->setParameter((char *)#x,attrf(a,#x) ); - #define p2(x,y) if (a->hasAttribute(y)) r->setParameter((char *)#x,attrf(a,y) ); - p2(max_power_engine,"max-power-engine") - p2(engine_prop_factor,"engine-prop-factor") - p(yasimdragfactor) - p(yasimliftfactor) - p2(max_power_rotor_brake,"max-power-rotor-brake") - p2(rotorgear_friction,"rotorgear-friction") - p2(engine_accel_limit,"engine-accel-limit") - #undef p - #undef p2 - r->setInUse(); - } else if(eq(name, "wing")) { - _airplane.setWing(parseWing(a, name, &_airplane)); - } else if(eq(name, "hstab")) { - _airplane.setTail(parseWing(a, name, &_airplane)); - } else if(eq(name, "vstab") || eq(name, "mstab")) { - _airplane.addVStab(parseWing(a, name, &_airplane)); - } else if(eq(name, "piston-engine")) { - parsePistonEngine(a); - } else if(eq(name, "turbine-engine")) { - parseTurbineEngine(a); - } else if(eq(name, "propeller")) { - parsePropeller(a); - } else if(eq(name, "thruster")) { - SimpleJet* j = new SimpleJet(); - _currObj = j; - v[0] = attrf(a, "x"); v[1] = attrf(a, "y"); v[2] = attrf(a, "z"); - j->setPosition(v); - _airplane.addThruster(j, 0, v); - v[0] = attrf(a, "vx"); v[1] = attrf(a, "vy"); v[2] = attrf(a, "vz"); - j->setDirection(v); - j->setThrust(attrf(a, "thrust") * LBS2N); - } else if(eq(name, "jet")) { - Jet* j = new Jet(); - _currObj = j; - v[0] = attrf(a, "x"); - v[1] = attrf(a, "y"); - v[2] = attrf(a, "z"); - float mass; - if(a->hasAttribute("mass")) { mass = attrf(a, "mass") * LBS2KG; } - else if(a->hasAttribute("mass-lbs")) { mass = attrf(a, "mass-lbs") * LBS2KG; } - else if(a->hasAttribute("mass-kg")) { mass = attrf(a, "mass-kg"); } - else { - SG_LOG(SG_FLIGHT,SG_ALERT,"YASim fatal: missing attribute, jet needs one of {mass-lbs, mass-kg}"); - exit(1); - } - j->setMaxThrust(attrf(a, "thrust") * LBS2N, - attrf(a, "afterburner", 0) * LBS2N); - j->setVectorAngle(attrf(a, "rotate", 0) * DEG2RAD); - j->setReverseThrust(attrf(a, "reverse", 0.2)); - - float n1min = attrf(a, "n1-idle", 55); - float n1max = attrf(a, "n1-max", 102); - float n2min = attrf(a, "n2-idle", 73); - float n2max = attrf(a, "n2-max", 103); - j->setRPMs(n1min, n1max, n2min, n2max); - - j->setTSFC(attrf(a, "tsfc", 0.8)); - j->setATSFC(attrf(a, "atsfc", 0.0)); - if(a->hasAttribute("egt")) j->setEGT(attrf(a, "egt")); - if(a->hasAttribute("epr")) j->setEPR(attrf(a, "epr")); - if(a->hasAttribute("exhaust-speed")) - j->setVMax(attrf(a, "exhaust-speed") * KTS2MPS); - if(a->hasAttribute("spool-time")) - j->setSpooling(attrf(a, "spool-time")); - - j->setPosition(v); - _airplane.addThruster(j, mass, v); - sprintf(buf, "/engines/engine[%d]", _nextEngine++); - EngRec* er = new EngRec(); - er->eng = j; - er->prefix = dup(buf); - _thrusters.add(er); - } else if(eq(name, "hitch")) { - Hitch* h = new Hitch(a->getValue("name")); - _currObj = h; - v[0] = attrf(a, "x"); - v[1] = attrf(a, "y"); - v[2] = attrf(a, "z"); - h->setPosition(v); - if(a->hasAttribute("force-is-calculated-by-other")) h->setForceIsCalculatedByOther(attrb(a,"force-is-calculated-by-other")); - _airplane.addHitch(h); - } else if(eq(name, "tow")) { - Hitch* h = (Hitch*)_currObj; - if(a->hasAttribute("length")) - h->setTowLength(attrf(a, "length")); - if(a->hasAttribute("elastic-constant")) - h->setTowElasticConstant(attrf(a, "elastic-constant")); - if(a->hasAttribute("break-force")) - h->setTowBreakForce(attrf(a, "break-force")); - if(a->hasAttribute("weight-per-meter")) - h->setTowWeightPerM(attrf(a, "weight-per-meter")); - if(a->hasAttribute("mp-auto-connect-period")) - h->setMpAutoConnectPeriod(attrf(a, "mp-auto-connect-period")); - } else if(eq(name, "winch")) { - Hitch* h = (Hitch*)_currObj; - double pos[3]; - pos[0] = attrd(a, "x",0); - pos[1] = attrd(a, "y",0); - pos[2] = attrd(a, "z",0); - h->setWinchPosition(pos); - if(a->hasAttribute("max-speed")) - h->setWinchMaxSpeed(attrf(a, "max-speed")); - if(a->hasAttribute("power")) - h->setWinchPower(attrf(a, "power") * 1000); - if(a->hasAttribute("max-force")) - h->setWinchMaxForce(attrf(a, "max-force")); - if(a->hasAttribute("initial-tow-length")) - h->setWinchInitialTowLength(attrf(a, "initial-tow-length")); - if(a->hasAttribute("max-tow-length")) - h->setWinchMaxTowLength(attrf(a, "max-tow-length")); - if(a->hasAttribute("min-tow-length")) - h->setWinchMinTowLength(attrf(a, "min-tow-length")); - } else if(eq(name, "gear")) { - Gear* g = new Gear(); - _currObj = g; - v[0] = attrf(a, "x"); - v[1] = attrf(a, "y"); - v[2] = attrf(a, "z"); - g->setPosition(v); - float nrm = Math::mag3(v); - if (_vehicle_radius < nrm) - _vehicle_radius = nrm; - if(a->hasAttribute("upx")) { - v[0] = attrf(a, "upx"); - v[1] = attrf(a, "upy"); - v[2] = attrf(a, "upz"); - Math::unit3(v, v); - } else { - v[0] = 0; - v[1] = 0; - v[2] = 1; - } - for(int i=0; i<3; i++) - v[i] *= attrf(a, "compression", 1); - g->setCompression(v); - g->setBrake(attrf(a, "skid", 0)); - g->setInitialLoad(attrf(a, "initial-load", 0)); - g->setStaticFriction(attrf(a, "sfric", 0.8)); - g->setDynamicFriction(attrf(a, "dfric", 0.7)); - g->setSpring(attrf(a, "spring", 1)); - g->setDamping(attrf(a, "damp", 1)); - if(a->hasAttribute("on-water")) g->setOnWater(attrb(a,"on-water")); - if(a->hasAttribute("on-solid")) g->setOnSolid(attrb(a,"on-solid")); - if(a->hasAttribute("ignored-by-solver")) g->setIgnoreWhileSolving(attrb(a,"ignored-by-solver")); - g->setSpringFactorNotPlaning(attrf(a, "spring-factor-not-planing", 1)); - g->setSpeedPlaning(attrf(a, "speed-planing", 0) * KTS2MPS); - g->setReduceFrictionByExtension(attrf(a, "reduce-friction-by-extension", 0)); - _airplane.addGear(g); - } else if(eq(name, "hook")) { - Hook* h = new Hook(); - _currObj = h; - v[0] = attrf(a, "x"); - v[1] = attrf(a, "y"); - v[2] = attrf(a, "z"); - h->setPosition(v); - float length = attrf(a, "length", 1.0); - h->setLength(length); - float nrm = length+Math::mag3(v); - if (_vehicle_radius < nrm) - _vehicle_radius = nrm; - h->setDownAngle(attrf(a, "down-angle", 70) * DEG2RAD); - h->setUpAngle(attrf(a, "up-angle", 0) * DEG2RAD); - _airplane.addHook(h); - } else if(eq(name, "launchbar")) { - Launchbar* l = new Launchbar(); - _currObj = l; - v[0] = attrf(a, "x"); - v[1] = attrf(a, "y"); - v[2] = attrf(a, "z"); - l->setLaunchbarMount(v); - v[0] = attrf(a, "holdback-x", v[0]); - v[1] = attrf(a, "holdback-y", v[1]); - v[2] = attrf(a, "holdback-z", v[2]); - l->setHoldbackMount(v); - float length = attrf(a, "length", 1.0); - l->setLength(length); - l->setDownAngle(attrf(a, "down-angle", 45) * DEG2RAD); - l->setUpAngle(attrf(a, "up-angle", -45) * DEG2RAD); - l->setHoldbackLength(attrf(a, "holdback-length", 2.0)); - _airplane.addLaunchbar(l); - } else if(eq(name, "fuselage")) { - float b[3]; - v[0] = attrf(a, "ax"); - v[1] = attrf(a, "ay"); - v[2] = attrf(a, "az"); - b[0] = attrf(a, "bx"); - b[1] = attrf(a, "by"); - b[2] = attrf(a, "bz"); - float taper = attrf(a, "taper", 1); - float mid = attrf(a, "midpoint", 0.5); - if (_airplane.isVersionOrNewer(Version::YASIM_VERSION_32)) { - // A fuselage's "midpoint" XML attribute is defined from the - // fuselage's front end, but the Fuselage object's internal - // "mid" attribute is actually defined from the rear end. - // Thus YASim's original interpretation of "midpoint" was wrong. - // Complement the "midpoint" value to ensure the fuselage - // points the right way. - mid = 1 - mid; - } - float cx = attrf(a, "cx", 1); - float cy = attrf(a, "cy", 1); - float cz = attrf(a, "cz", 1); - float idrag = attrf(a, "idrag", 1); - _airplane.addFuselage(v, b, attrf(a, "width"), taper, mid, - cx, cy, cz, idrag); - } else if(eq(name, "tank")) { - v[0] = attrf(a, "x"); - v[1] = attrf(a, "y"); - v[2] = attrf(a, "z"); - float density = 6.0; // gasoline, in lbs/gal - if(a->hasAttribute("jet")) density = 6.72; - density *= LBS2KG*CM2GALS; - float capacity = 0; - if(a->hasAttribute("capacity")) { capacity = attrf(a, "capacity") * LBS2KG; } - else if(a->hasAttribute("capacity-lbs")) { capacity = attrf(a, "capacity-lbs") * LBS2KG; } - else if(a->hasAttribute("capacity-kg")) { capacity = attrf(a, "capacity-kg"); } - else { - SG_LOG(SG_FLIGHT,SG_ALERT,"YASim fatal: missing attribute, tank needs one of {capacity-lbs, capacity-kg}"); - exit(1); - } - _airplane.addTank(v, capacity, density); - } else if(eq(name, "ballast")) { - v[0] = attrf(a, "x"); - v[1] = attrf(a, "y"); - v[2] = attrf(a, "z"); - if(a->hasAttribute("mass")) { f = attrf(a, "mass") * LBS2KG; } - else if (a->hasAttribute("mass-lbs")) { f = attrf(a, "mass-lbs") * LBS2KG; } - else if (a->hasAttribute("mass-kg")) { f = attrf(a, "mass-kg"); } - else { - SG_LOG(SG_FLIGHT,SG_ALERT,"YASim fatal: missing attribute, airplane needs one of {mass-lbs, mass-kg}"); - exit(1); - } - _airplane.addBallast(v, f); - } else if(eq(name, "weight")) { - parseWeight(a); - } else if(eq(name, "stall")) { - Wing* w = (Wing*)_currObj; - w->setStall(attrf(a, "aoa") * DEG2RAD); - w->setStallWidth(attrf(a, "width", 2) * DEG2RAD); - w->setStallPeak(attrf(a, "peak", 1.5)); - } else if(eq(name, "flap0")) { - ((Wing*)_currObj)->setFlap0Params(attrf(a, "start"), attrf(a, "end"), - attrf(a, "lift"), attrf(a, "drag")); - } else if(eq(name, "flap1")) { - ((Wing*)_currObj)->setFlap1Params(attrf(a, "start"), attrf(a, "end"), - attrf(a, "lift"), attrf(a, "drag")); - } else if(eq(name, "slat")) { - ((Wing*)_currObj)->setSlatParams(attrf(a, "start"), attrf(a, "end"), - attrf(a, "aoa"), attrf(a, "drag")); - } else if(eq(name, "spoiler")) { - ((Wing*)_currObj)->setSpoilerParams(attrf(a, "start"), attrf(a, "end"), - attrf(a, "lift"), attrf(a, "drag")); - /* } else if(eq(name, "collective")) { - ((Rotor*)_currObj)->setcollective(attrf(a, "min"), attrf(a, "max")); - } else if(eq(name, "cyclic")) { - ((Rotor*)_currObj)->setcyclic(attrf(a, "ail"), attrf(a, "ele")); - */ - } else if(eq(name, "actionpt")) { - v[0] = attrf(a, "x"); - v[1] = attrf(a, "y"); - v[2] = attrf(a, "z"); - ((Thruster*)_currObj)->setPosition(v); - } else if(eq(name, "dir")) { - v[0] = attrf(a, "x"); - v[1] = attrf(a, "y"); - v[2] = attrf(a, "z"); - ((Thruster*)_currObj)->setDirection(v); - } else if(eq(name, "control-setting")) { - // A cruise or approach control setting - const char* axis = a->getValue("axis"); - float value = attrf(a, "value", 0); - ControlMap* cm = _airplane.getControlMap(); - if(_cruiseCurr) - _airplane.addCruiseControl(cm->propertyHandle(axis), value); - else - _airplane.addApproachControl(cm->propertyHandle(axis), value); - } else if(eq(name, "control-input")) { - ControlMap* cm = _airplane.getControlMap(); - // A mapping of input property to a control - int axis = cm->propertyHandle(a->getValue("axis")); - int control = parseOutput(a->getValue("control")); - int opt = 0; - opt |= a->hasAttribute("split") ? ControlMap::OPT_SPLIT : 0; - opt |= a->hasAttribute("invert") ? ControlMap::OPT_INVERT : 0; - opt |= a->hasAttribute("square") ? ControlMap::OPT_SQUARE : 0; - if(a->hasAttribute("src0")) { - cm->addMapping(axis, control, _currObj, opt, - attrf(a, "src0"), attrf(a, "src1"), - attrf(a, "dst0"), attrf(a, "dst1")); - } else { - cm->addMapping(axis, control, _currObj, opt); - } - } else if(eq(name, "control-output")) { - // A property output for a control on the current object - ControlMap* cm = _airplane.getControlMap(); - int type = parseOutput(a->getValue("control")); - int handle = cm->getOutputHandle(_currObj, type); - - PropOut* p = new PropOut(); - p->prop = fgGetNode(a->getValue("prop"), true); - p->handle = handle; - p->type = type; - p->left = !(a->hasAttribute("side") && - eq("right", a->getValue("side"))); - p->min = attrf(a, "min", cm->rangeMin(type)); - p->max = attrf(a, "max", cm->rangeMax(type)); - _controlProps.add(p); - - } else if(eq(name, "control-speed")) { - ControlMap* cm = _airplane.getControlMap(); - int type = parseOutput(a->getValue("control")); - int handle = cm->getOutputHandle(_currObj, type); - float time = attrf(a, "transition-time", 0); - - cm->setTransitionTime(handle, time); - } else { - SG_LOG(SG_FLIGHT,SG_ALERT,"Unexpected tag '" - << name << "' found in YASim aircraft description"); + if(!strcmp(name, "airplane")) { parseAirplane(&a); } + else if(!strcmp(name, "approach") || !strcmp(name, "cruise")) { + parseApproachCruise(&a, name); + } + else if(!strcmp(name, "solve-weight")) { parseSolveWeight(&a); } + else if(!strcmp(name, "cockpit")) { parseCockpit(&a); } + else if(!strcmp(name, "rotor")) { parseRotor(&a, name); } + else if(!strcmp(name, "rotorgear")) { parseRotorGear(&a); } + else if(!strcmp(name, "wing") || !strcmp(name, "hstab") || !strcmp(name, "vstab") || !strcmp(name, "mstab")) { + parseWing(&a, name, &_airplane); + } + else if(!strcmp(name, "piston-engine")) { parsePistonEngine(&a); } + else if(!strcmp(name, "turbine-engine")) { parseTurbineEngine(&a); } + else if(!strcmp(name, "propeller")) { parsePropeller(&a); } + else if(!strcmp(name, "thruster")) { parseThruster(&a); } + else if(!strcmp(name, "jet")) { parseJet(&a); } + else if(!strcmp(name, "hitch")) { parseHitch(&a); } + else if(!strcmp(name, "tow")) { parseTow(&a); } + else if(!strcmp(name, "winch")) { parseWinch(&a); } + else if(!strcmp(name, "gear")) { parseGear(&a); } + else if(!strcmp(name, "hook")) { parseHook(&a); } + else if(!strcmp(name, "launchbar")) { parseLaunchbar(&a); } + else if(!strcmp(name, "fuselage")) { parseFuselage(&a); } + else if(!strcmp(name, "tank")) { parseTank(&a); } + else if(!strcmp(name, "ballast")) { parseBallast(&a); } + else if(!strcmp(name, "weight")) { parseWeight(&a); } + else if(!strcmp(name, "stall")) { parseStall(&a); } + else if(!strcmp(name, "flap0") || !strcmp(name, "flap1") || !strcmp(name, "spoiler") || !strcmp(name, "slat")) { + parseFlap(&a, name); + } + else if(!strcmp(name, "actionpt")) { + attrf_xyz(&a, v); + ((Thruster*)_currObj)->setPosition(v); + } + else if(!strcmp(name, "dir")) { + attrf_xyz(&a, v); + ((Thruster*)_currObj)->setDirection(v); + } + else if(!strcmp(name, "control-setting")) { parseControlSetting(&a); } + else if(!strcmp(name, "control-input")) { parseControlIn(&a); } + else if(!strcmp(name, "control-output")) { parseControlOut(&a); } + else if(!strcmp(name, "control-speed")) { parseControlSpeed(&a); } + else { + SG_LOG(SG_FLIGHT,SG_ALERT,"Unexpected tag '" << name << "' found in YASim aircraft description"); exit(1); } +} // startElement + +void FGFDM::parseAirplane(const XMLAttributes* a) +{ + float f {0}; + if(a->hasAttribute("mass")) { f = attrf(a, "mass") * LBS2KG; } + else if (a->hasAttribute("mass-lbs")) { f = attrf(a, "mass-lbs") * LBS2KG; } + else if (a->hasAttribute("mass-kg")) { f = attrf(a, "mass-kg"); } + else { + SG_LOG(SG_FLIGHT,SG_ALERT,"YASim fatal: missing attribute, airplane needs one of {mass-lbs, mass-kg}"); + exit(1); + } + _airplane.setEmptyWeight(f); + if(a->hasAttribute("version")) { _airplane.setVersion(a->getValue("version")); } + if( !_airplane.isVersionOrNewer( Version::YASIM_VERSION_CURRENT ) ) { + SG_LOG(SG_FLIGHT, SG_DEV_ALERT, "This aircraft does not use the latest yasim configuration version."); + } + _airplane.setDesiredCGRangeInPercentOfMAC(attrf(a, "cg-min", 0.25f), attrf(a, "cg-max", 0.3f)); + if (attrb(a, "auto-ballast")) { _airplane.setAutoBallast(true); } } +void FGFDM::parseApproachCruise(const XMLAttributes* a, const char* name) +{ + float spd, alt = 0; + if (a->hasAttribute("speed")) { spd = attrf(a, "speed") * KTS2MPS; } + else if (a->hasAttribute("speed-kt")) { spd = attrf(a, "speed-kt") * KTS2MPS; } + else if (a->hasAttribute("speed-kmh")) { spd = attrf(a, "speed-kmh") * KMH2MPS; } + else { + SG_LOG(SG_FLIGHT,SG_ALERT,"YASim fatal: missing attribute, "<< name << " needs one of {speed-kt, speed-kmh}"); + exit(1); + } + if (a->hasAttribute("alt")) { alt = attrf(a, "alt") * FT2M; } + else if (a->hasAttribute("alt-ft")) { alt = attrf(a, "alt-ft") * FT2M; } + else if (a->hasAttribute("alt-m")) { alt = attrf(a, "alt-m"); } + float gla = attrf(a, "glide-angle", 0) * DEG2RAD; + if (!strcmp(name, "approach")) { + float aoa = attrf(a, "aoa", 0) * DEG2RAD; + _airplane.setApproach(spd, alt, aoa, attrf(a, "fuel", 0.2), gla); + _airplaneCfg = Airplane::Configuration::APPROACH; + } + else { + _airplane.setCruise(spd, alt, attrf(a, "fuel", 0.5),gla); + _airplaneCfg = Airplane::Configuration::CRUISE; + } +} + +void FGFDM::parseSolveWeight(const XMLAttributes* a) +{ + float f {0}; + int idx = attri(a, "idx"); + if(a->hasAttribute("weight")) { f = attrf(a, "weight") * LBS2KG; } + else if(a->hasAttribute("weight-lbs")) { f = attrf(a, "weight-lbs") * LBS2KG; } + else if(a->hasAttribute("weight-kg")) { f = attrf(a, "weight-kg"); } + else { + SG_LOG(SG_FLIGHT,SG_ALERT,"YASim fatal: missing attribute, solve-weight needs one of {weight-lbs, weight-kg}"); + exit(1); + } + _airplane.addSolutionWeight(_airplaneCfg, idx, f); +} + +void FGFDM::parseCockpit(const XMLAttributes* a) +{ + float v[3]; + attrf_xyz(a, v); + _airplane.setPilotPos(v); +} + + void FGFDM::getExternalInput(float dt) { char buf[256]; @@ -705,8 +421,8 @@ void FGFDM::setOutputProperties(float dt) float val = (p->left ? cm->getOutput(p->handle) : cm->getOutputR(p->handle)); - float rmin = cm->rangeMin(p->type); - float rmax = cm->rangeMax(p->type); + float rmin = cm->rangeMin(p->control); + float rmax = cm->rangeMax(p->control); float frac = (val - rmin) / (rmax - rmin); val = frac*(p->max - p->min) + p->min; p->prop->setFloatValue(val); @@ -798,64 +514,92 @@ void FGFDM::setOutputProperties(float dt) } } -Wing* FGFDM::parseWing(XMLAttributes* a, const char* type, Version * version) +void FGFDM::parseWing(const XMLAttributes* a, const char* type, Airplane* airplane) { float defDihed = 0; bool mirror = true; - if(eq(type, "vstab")) { - defDihed = 90; - mirror = false; + if(!strcmp(type, "vstab")) { + defDihed = 90; + mirror = false; } - - float base[3]; - base[0] = attrf(a, "x"); - base[1] = attrf(a, "y"); - base[2] = attrf(a, "z"); - + + float base[3] {0,0,0}; + float chord {0}; float length = attrf(a, "length"); - float chord = attrf(a, "chord"); - float sweep = attrf(a, "sweep", 0) * DEG2RAD; - float taper = attrf(a, "taper", 1); - float dihedral = attrf(a, "dihedral", defDihed) * DEG2RAD; // These come in with positive indicating positive AoA, but the // internals expect a rotation about the left-pointing Y axis, so // invert the sign. - float incidence = attrf(a, "incidence", 0) * DEG2RAD * -1; + float incidence {0}; float twist = attrf(a, "twist", 0) * DEG2RAD * -1; + + // if this element is declared as section of a wing, skip attributes + // that are ignored in class Wing anyway because they are calculated + float isSection = attrb(a, "append"); + if (!isSection) { + attrf_xyz(a, base); + chord = attrf(a, "chord"); + incidence = attrf(a, "incidence", 0) * DEG2RAD * -1; + } + + // optional attributes (with defaults) + float sweep = attrf(a, "sweep", 0) * DEG2RAD; + float taper = attrf(a, "taper", 1); + float dihedral = attrf(a, "dihedral", defDihed) * DEG2RAD; + float camber = attrf(a, "camber", 0); - if (!version->isVersionOrNewer(Version::YASIM_VERSION_2017_2) && (camber == 0)) { - SG_LOG(SG_FLIGHT, SG_DEV_WARN, "YASIM warning: versions before 2017.2 are buggy for wings with camber=0"); + if (!airplane->isVersionOrNewer(Version::YASIM_VERSION_2017_2) && (camber == 0)) { + SG_LOG(SG_FLIGHT, SG_DEV_WARN, "YASIM warning: versions before 2017.2 are buggy for wings with camber=0"); } - Wing* w = new Wing(version, mirror, base, chord, length, - taper, sweep, dihedral, twist); - w->setIncidence(incidence); - w->setCamber(camber); - // The 70% is a magic number that sorta kinda seems to match known // throttle settings to approach speed. - w->setInducedDrag(0.7*attrf(a, "idrag", 1)); - - float effect = attrf(a, "effectiveness", 1); - w->setDragScale(w->getDragScale()*effect); + float idrag = 0.7*attrf(a, "idrag", 1); + + // get wing object by type + Wing* w; + if (!strcmp(type, "wing")) + { + w = airplane->getWing(); + } + else if (!strcmp(type, "hstab")) { + w = airplane->getTail(); + } else { + w = new Wing(airplane, mirror); + } + // add section; if wing object has already section, base will be overridden + // by tip of last section + _wingSection = w->addWingSection(base, chord, length, taper, sweep, dihedral, twist, camber, idrag, incidence); + if (!strcmp(type, "vstab") || !strcmp(type, "mstab")) + { + airplane->addVStab(w); + } + float dragFactor = attrf(a, "pdrag", 1); + if (a->hasAttribute("effectiveness")) { +/* FIXME: + * check if all attibutes have "good" names and update parser AND documentation together + * only after that issue warnings + SG_LOG(SG_FLIGHT, SG_ALERT, "Warning: " << + "deprecated attribute 'effectiveness' in YASim configuration file. " << + "Use 'pdrag' instead to add parasitic drag."); +*/ + dragFactor = attrf(a, "effectiveness", 1); + } + w->setSectionDrag(_wingSection, dragFactor); _currObj = w; - return w; } -Rotor* FGFDM::parseRotor(XMLAttributes* a, const char* type) +void FGFDM::parseRotor(const XMLAttributes* a, const char* type) { Rotor* w = new Rotor(); // float defDihed = 0; float pos[3]; - pos[0] = attrf(a, "x"); - pos[1] = attrf(a, "y"); - pos[2] = attrf(a, "z"); + attrf_xyz(a, pos); w->setBase(pos); float normal[3]; @@ -912,24 +656,21 @@ Rotor* FGFDM::parseRotor(XMLAttributes* a, const char* type) if(attrb(a,"sharedflaphinge")) w->setSharedFlapHinge(true); - if(a->hasAttribute("name")) - w->setName(a->getValue("name") ); - if(a->hasAttribute("alphaout0")) - w->setAlphaoutput(0,a->getValue("alphaout0") ); - if(a->hasAttribute("alphaout1")) w->setAlphaoutput(1,a->getValue("alphaout1") ); - if(a->hasAttribute("alphaout2")) w->setAlphaoutput(2,a->getValue("alphaout2") ); - if(a->hasAttribute("alphaout3")) w->setAlphaoutput(3,a->getValue("alphaout3") ); - if(a->hasAttribute("coneout")) w->setAlphaoutput(4,a->getValue("coneout") ); - if(a->hasAttribute("yawout")) w->setAlphaoutput(5,a->getValue("yawout") ); - if(a->hasAttribute("rollout")) w->setAlphaoutput(6,a->getValue("rollout") ); + if(a->hasAttribute("name")) w->setName(a->getValue("name")); + if(a->hasAttribute("alphaout0")) w->setAlphaoutput(0,a->getValue("alphaout0")); + if(a->hasAttribute("alphaout1")) w->setAlphaoutput(1,a->getValue("alphaout1")); + if(a->hasAttribute("alphaout2")) w->setAlphaoutput(2,a->getValue("alphaout2")); + if(a->hasAttribute("alphaout3")) w->setAlphaoutput(3,a->getValue("alphaout3")); + if(a->hasAttribute("coneout")) w->setAlphaoutput(4,a->getValue("coneout")); + if(a->hasAttribute("yawout")) w->setAlphaoutput(5,a->getValue("yawout")); + if(a->hasAttribute("rollout")) w->setAlphaoutput(6,a->getValue("rollout")); w->setPitchA(attrf(a, "pitch-a", 10)); w->setPitchB(attrf(a, "pitch-b", 10)); w->setForceAtPitchA(attrf(a, "forceatpitch-a", 3000)); w->setPowerAtPitch0(attrf(a, "poweratpitch-0", 300)); w->setPowerAtPitchB(attrf(a, "poweratpitch-b", 3000)); - if(attrb(a,"notorque")) - w->setNotorque(1); + if(attrb(a,"notorque")) w->setNotorque(1); #define p(x) if (a->hasAttribute(#x)) w->setParameter((char *)#x,attrf(a,#x) ); #define p2(x,y) if (a->hasAttribute(y)) w->setParameter((char *)#x,attrf(a,y) ); @@ -963,10 +704,28 @@ Rotor* FGFDM::parseRotor(XMLAttributes* a, const char* type) #undef p #undef p2 _currObj = w; - return w; + _airplane.getModel()->getRotorgear()->addRotor(w); +} //parseRotor + +void FGFDM::parseRotorGear(const XMLAttributes* a) +{ + Rotorgear* r = _airplane.getModel()->getRotorgear(); + _currObj = r; + #define p(x) if (a->hasAttribute(#x)) r->setParameter((char *)#x,attrf(a,#x) ); + #define p2(x,y) if (a->hasAttribute(y)) r->setParameter((char *)#x,attrf(a,y) ); + p2(max_power_engine,"max-power-engine") + p2(engine_prop_factor,"engine-prop-factor") + p(yasimdragfactor) + p(yasimliftfactor) + p2(max_power_rotor_brake,"max-power-rotor-brake") + p2(rotorgear_friction,"rotorgear-friction") + p2(engine_accel_limit,"engine-accel-limit") + #undef p + #undef p2 + r->setInUse(); } -void FGFDM::parsePistonEngine(XMLAttributes* a) +void FGFDM::parsePistonEngine(const XMLAttributes* a) { float engP = attrf(a, "eng-power") * HP2W; float engS = attrf(a, "eng-rpm") * RPM2RAD; @@ -995,7 +754,7 @@ void FGFDM::parsePistonEngine(XMLAttributes* a) ((PropEngine*)_currObj)->setEngine(eng); } -void FGFDM::parseTurbineEngine(XMLAttributes* a) +void FGFDM::parseTurbineEngine(const XMLAttributes* a) { float power = attrf(a, "eng-power") * HP2W; float omega = attrf(a, "eng-rpm") * RPM2RAD; @@ -1014,7 +773,7 @@ void FGFDM::parseTurbineEngine(XMLAttributes* a) ((PropEngine*)_currObj)->setEngine(eng); } -void FGFDM::parsePropeller(XMLAttributes* a) +void FGFDM::parsePropeller(const XMLAttributes* a) { // Legacy Handling for the old engines syntax: PistonEngine* eng = 0; @@ -1038,9 +797,7 @@ void FGFDM::parsePropeller(XMLAttributes* a) // Now parse the actual propeller definition: float cg[3]; - cg[0] = attrf(a, "x"); - cg[1] = attrf(a, "y"); - cg[2] = attrf(a, "z"); + attrf_xyz(a, cg); float mass = attrf(a, "mass") * LBS2KG; float moment = attrf(a, "moment"); float radius = attrf(a, "radius"); @@ -1083,101 +840,364 @@ void FGFDM::parsePropeller(XMLAttributes* a) sprintf(buf, "/engines/engine[%d]", _nextEngine++); EngRec* er = new EngRec(); er->eng = thruster; - er->prefix = dup(buf); + er->prefix = strdup(buf); _thrusters.add(er); _currObj = thruster; } -/// map identifier (string) to int (enum in ControlMap) -int FGFDM::parseOutput(const char* name) +void FGFDM::parseThruster(const XMLAttributes* a) { - if(eq(name, "THROTTLE")) return ControlMap::THROTTLE; - if(eq(name, "MIXTURE")) return ControlMap::MIXTURE; - if(eq(name, "CONDLEVER")) return ControlMap::CONDLEVER; - if(eq(name, "STARTER")) return ControlMap::STARTER; - if(eq(name, "MAGNETOS")) return ControlMap::MAGNETOS; - if(eq(name, "ADVANCE")) return ControlMap::ADVANCE; - if(eq(name, "REHEAT")) return ControlMap::REHEAT; - if(eq(name, "BOOST")) return ControlMap::BOOST; - if(eq(name, "VECTOR")) return ControlMap::VECTOR; - if(eq(name, "PROP")) return ControlMap::PROP; - if(eq(name, "BRAKE")) return ControlMap::BRAKE; - if(eq(name, "STEER")) return ControlMap::STEER; - if(eq(name, "EXTEND")) return ControlMap::EXTEND; - if(eq(name, "HEXTEND")) return ControlMap::HEXTEND; - if(eq(name, "LEXTEND")) return ControlMap::LEXTEND; - if(eq(name, "LACCEL")) return ControlMap::LACCEL; - if(eq(name, "INCIDENCE")) return ControlMap::INCIDENCE; - if(eq(name, "FLAP0")) return ControlMap::FLAP0; - if(eq(name, "FLAP0EFFECTIVENESS")) return ControlMap::FLAP0EFFECTIVENESS; - if(eq(name, "FLAP1")) return ControlMap::FLAP1; - if(eq(name, "FLAP1EFFECTIVENESS")) return ControlMap::FLAP1EFFECTIVENESS; - if(eq(name, "SLAT")) return ControlMap::SLAT; - if(eq(name, "SPOILER")) return ControlMap::SPOILER; - if(eq(name, "CASTERING")) return ControlMap::CASTERING; - if(eq(name, "PROPPITCH")) return ControlMap::PROPPITCH; - if(eq(name, "PROPFEATHER")) return ControlMap::PROPFEATHER; - if(eq(name, "COLLECTIVE")) return ControlMap::COLLECTIVE; - if(eq(name, "CYCLICAIL")) return ControlMap::CYCLICAIL; - if(eq(name, "CYCLICELE")) return ControlMap::CYCLICELE; - if(eq(name, "TILTROLL")) return ControlMap::TILTROLL; - if(eq(name, "TILTPITCH")) return ControlMap::TILTPITCH; - if(eq(name, "TILTYAW")) return ControlMap::TILTYAW; - if(eq(name, "ROTORGEARENGINEON")) return ControlMap::ROTORENGINEON; - if(eq(name, "ROTORBRAKE")) return ControlMap::ROTORBRAKE; - if(eq(name, "ROTORENGINEMAXRELTORQUE")) - return ControlMap::ROTORENGINEMAXRELTORQUE; - if(eq(name, "ROTORRELTARGET")) return ControlMap::ROTORRELTARGET; - if(eq(name, "ROTORBALANCE")) return ControlMap::ROTORBALANCE; - if(eq(name, "REVERSE_THRUST")) return ControlMap::REVERSE_THRUST; - if(eq(name, "WASTEGATE")) return ControlMap::WASTEGATE; - if(eq(name, "WINCHRELSPEED")) return ControlMap::WINCHRELSPEED; - if(eq(name, "HITCHOPEN")) return ControlMap::HITCHOPEN; - if(eq(name, "PLACEWINCH")) return ControlMap::PLACEWINCH; - if(eq(name, "FINDAITOW")) return ControlMap::FINDAITOW; - - SG_LOG(SG_FLIGHT,SG_ALERT,"Unrecognized control type '" - << name << "' in YASim aircraft description."); - exit(1); - + float v[3]; + SimpleJet* j = new SimpleJet(); + _currObj = j; + attrf_xyz(a, v); + j->setPosition(v); + _airplane.addThruster(j, 0, v); + v[0] = attrf(a, "vx"); v[1] = attrf(a, "vy"); v[2] = attrf(a, "vz"); + j->setDirection(v); + j->setThrust(attrf(a, "thrust") * LBS2N); } -void FGFDM::parseWeight(XMLAttributes* a) +void FGFDM::parseJet(const XMLAttributes* a) +{ + float v[3]; + Jet* j = new Jet(); + _currObj = j; + attrf_xyz(a, v); + float mass; + if(a->hasAttribute("mass")) { mass = attrf(a, "mass") * LBS2KG; } + else if(a->hasAttribute("mass-lbs")) { mass = attrf(a, "mass-lbs") * LBS2KG; } + else if(a->hasAttribute("mass-kg")) { mass = attrf(a, "mass-kg"); } + else { + SG_LOG(SG_FLIGHT,SG_ALERT,"YASim fatal: missing attribute, jet needs one of {mass-lbs, mass-kg}"); + exit(1); + } + j->setMaxThrust(attrf(a, "thrust") * LBS2N, attrf(a, "afterburner", 0) * LBS2N); + j->setVectorAngle(attrf(a, "rotate", 0) * DEG2RAD); + j->setReverseThrust(attrf(a, "reverse", 0.2)); + + float n1min = attrf(a, "n1-idle", 55); + float n1max = attrf(a, "n1-max", 102); + float n2min = attrf(a, "n2-idle", 73); + float n2max = attrf(a, "n2-max", 103); + j->setRPMs(n1min, n1max, n2min, n2max); + + j->setTSFC(attrf(a, "tsfc", 0.8)); + j->setATSFC(attrf(a, "atsfc", 0.0)); + if(a->hasAttribute("egt")) j->setEGT(attrf(a, "egt")); + if(a->hasAttribute("epr")) j->setEPR(attrf(a, "epr")); + if(a->hasAttribute("exhaust-speed")) + j->setVMax(attrf(a, "exhaust-speed") * KTS2MPS); + if(a->hasAttribute("spool-time")) + j->setSpooling(attrf(a, "spool-time")); + + j->setPosition(v); + _airplane.addThruster(j, mass, v); + char buf[64]; + sprintf(buf, "/engines/engine[%d]", _nextEngine++); + EngRec* er = new EngRec(); + er->eng = j; + er->prefix = strdup(buf); + _thrusters.add(er); +} + +void FGFDM::parseHitch(const XMLAttributes* a) +{ + float v[3]; + Hitch* h = new Hitch(a->getValue("name")); + _currObj = h; + attrf_xyz(a, v); + h->setPosition(v); + if(a->hasAttribute("force-is-calculated-by-other")) h->setForceIsCalculatedByOther(attrb(a,"force-is-calculated-by-other")); + _airplane.addHitch(h); +} + +void FGFDM::parseTow(const XMLAttributes* a) +{ + Hitch* h = (Hitch*)_currObj; + if(a->hasAttribute("length")) + h->setTowLength(attrf(a, "length")); + if(a->hasAttribute("elastic-constant")) + h->setTowElasticConstant(attrf(a, "elastic-constant")); + if(a->hasAttribute("break-force")) + h->setTowBreakForce(attrf(a, "break-force")); + if(a->hasAttribute("weight-per-meter")) + h->setTowWeightPerM(attrf(a, "weight-per-meter")); + if(a->hasAttribute("mp-auto-connect-period")) + h->setMpAutoConnectPeriod(attrf(a, "mp-auto-connect-period")); +} + +void FGFDM::parseWinch(const XMLAttributes* a) +{ + Hitch* h = (Hitch*)_currObj; + double pos[3]; + pos[0] = attrd(a, "x",0); + pos[1] = attrd(a, "y",0); + pos[2] = attrd(a, "z",0); + h->setWinchPosition(pos); + if(a->hasAttribute("max-speed")) + h->setWinchMaxSpeed(attrf(a, "max-speed")); + if(a->hasAttribute("power")) + h->setWinchPower(attrf(a, "power") * 1000); + if(a->hasAttribute("max-force")) + h->setWinchMaxForce(attrf(a, "max-force")); + if(a->hasAttribute("initial-tow-length")) + h->setWinchInitialTowLength(attrf(a, "initial-tow-length")); + if(a->hasAttribute("max-tow-length")) + h->setWinchMaxTowLength(attrf(a, "max-tow-length")); + if(a->hasAttribute("min-tow-length")) + h->setWinchMinTowLength(attrf(a, "min-tow-length")); +} + +void FGFDM::parseGear(const XMLAttributes* a) +{ + float v[3]; + Gear* g = new Gear(); + _currObj = g; + attrf_xyz(a, v); + g->setPosition(v); + float nrm = Math::mag3(v); + if (_vehicle_radius < nrm) + _vehicle_radius = nrm; + if(a->hasAttribute("upx")) { + v[0] = attrf(a, "upx"); + v[1] = attrf(a, "upy"); + v[2] = attrf(a, "upz"); + Math::unit3(v, v); + } else { + v[0] = 0; + v[1] = 0; + v[2] = 1; + } + for(int i=0; i<3; i++) + v[i] *= attrf(a, "compression", 1); + g->setCompression(v); + g->setBrake(attrf(a, "skid", 0)); + g->setInitialLoad(attrf(a, "initial-load", 0)); + g->setStaticFriction(attrf(a, "sfric", 0.8)); + g->setDynamicFriction(attrf(a, "dfric", 0.7)); + g->setSpring(attrf(a, "spring", 1)); + g->setDamping(attrf(a, "damp", 1)); + if(a->hasAttribute("on-water")) g->setOnWater(attrb(a,"on-water")); + if(a->hasAttribute("on-solid")) g->setOnSolid(attrb(a,"on-solid")); + if(a->hasAttribute("ignored-by-solver")) g->setIgnoreWhileSolving(attrb(a,"ignored-by-solver")); + g->setSpringFactorNotPlaning(attrf(a, "spring-factor-not-planing", 1)); + g->setSpeedPlaning(attrf(a, "speed-planing", 0) * KTS2MPS); + g->setReduceFrictionByExtension(attrf(a, "reduce-friction-by-extension", 0)); + _airplane.addGear(g); +} + +void FGFDM::parseHook(const XMLAttributes* a) +{ + float v[3]; + Hook* h = new Hook(); + _currObj = h; + attrf_xyz(a, v); + h->setPosition(v); + float length = attrf(a, "length", 1.0); + h->setLength(length); + float nrm = length+Math::mag3(v); + if (_vehicle_radius < nrm) + _vehicle_radius = nrm; + h->setDownAngle(attrf(a, "down-angle", 70) * DEG2RAD); + h->setUpAngle(attrf(a, "up-angle", 0) * DEG2RAD); + _airplane.addHook(h); +} + +void FGFDM::parseLaunchbar(const XMLAttributes* a) +{ + float v[3]; + Launchbar* l = new Launchbar(); + _currObj = l; + attrf_xyz(a, v); + l->setLaunchbarMount(v); + v[0] = attrf(a, "holdback-x", v[0]); + v[1] = attrf(a, "holdback-y", v[1]); + v[2] = attrf(a, "holdback-z", v[2]); + l->setHoldbackMount(v); + float length = attrf(a, "length", 1.0); + l->setLength(length); + l->setDownAngle(attrf(a, "down-angle", 45) * DEG2RAD); + l->setUpAngle(attrf(a, "up-angle", -45) * DEG2RAD); + l->setHoldbackLength(attrf(a, "holdback-length", 2.0)); + _airplane.addLaunchbar(l); +} + +void FGFDM::parseFuselage(const XMLAttributes* a) +{ + float v[3]; + float b[3]; + v[0] = attrf(a, "ax"); + v[1] = attrf(a, "ay"); + v[2] = attrf(a, "az"); + b[0] = attrf(a, "bx"); + b[1] = attrf(a, "by"); + b[2] = attrf(a, "bz"); + float taper = attrf(a, "taper", 1); + float mid = attrf(a, "midpoint", 0.5); + if (_airplane.isVersionOrNewer(Version::YASIM_VERSION_32)) { + // A fuselage's "midpoint" XML attribute is defined from the + // fuselage's front end, but the Fuselage object's internal + // "mid" attribute is actually defined from the rear end. + // Thus YASim's original interpretation of "midpoint" was wrong. + // Complement the "midpoint" value to ensure the fuselage + // points the right way. + mid = 1 - mid; + } + float cx = attrf(a, "cx", 1); + float cy = attrf(a, "cy", 1); + float cz = attrf(a, "cz", 1); + float idrag = attrf(a, "idrag", 1); + _airplane.addFuselage(v, b, attrf(a, "width"), taper, mid, cx, cy, cz, idrag); +} + +void FGFDM::parseTank(const XMLAttributes* a) +{ + float v[3]; + attrf_xyz(a, v); + float density = 6.0; // gasoline, in lbs/gal + if(a->hasAttribute("jet")) density = 6.72; + density *= LBS2KG*CM2GALS; + float capacity = 0; + if(a->hasAttribute("capacity")) { capacity = attrf(a, "capacity") * LBS2KG; } + else if(a->hasAttribute("capacity-lbs")) { capacity = attrf(a, "capacity-lbs") * LBS2KG; } + else if(a->hasAttribute("capacity-kg")) { capacity = attrf(a, "capacity-kg"); } + else { + SG_LOG(SG_FLIGHT,SG_ALERT,"YASim fatal: missing attribute, tank needs one of {capacity-lbs, capacity-kg}"); + exit(1); + } + _airplane.addTank(v, capacity, density); +} + +void FGFDM::parseBallast(const XMLAttributes* a) +{ + float v[3]; + float f; + attrf_xyz(a, v); + if(a->hasAttribute("mass")) { f = attrf(a, "mass") * LBS2KG; } + else if (a->hasAttribute("mass-lbs")) { f = attrf(a, "mass-lbs") * LBS2KG; } + else if (a->hasAttribute("mass-kg")) { f = attrf(a, "mass-kg"); } + else { + SG_LOG(SG_FLIGHT,SG_ALERT,"YASim fatal: missing attribute, airplane needs one of {mass-lbs, mass-kg}"); + exit(1); + } + _airplane.addBallast(v, f); +} + +/* +void FGFDM::parseXXX(const XMLAttributes* a) +{ + float v[3]; +} +*/ + +void FGFDM::parseWeight(const XMLAttributes* a) { WeightRec* wr = new WeightRec(); float v[3]; - v[0] = attrf(a, "x"); - v[1] = attrf(a, "y"); - v[2] = attrf(a, "z"); - - wr->prop = dup(a->getValue("mass-prop")); + attrf_xyz(a, v); + wr->prop = strdup(a->getValue("mass-prop")); wr->size = attrf(a, "size", 0); wr->handle = _airplane.addWeight(v, wr->size); - _weights.add(wr); } -bool FGFDM::eq(const char* a, const char* b) +void FGFDM::parseStall(const XMLAttributes* a) { - // Figure it out for yourself. :) - while(*a && *b && *a == *b) { a++; b++; } - return !(*a || *b); + Wing* w = (Wing*)_currObj; + StallParams sp; + sp.aoa = attrf(a, "aoa") * DEG2RAD; + sp.width = attrf(a, "width", 2) * DEG2RAD; + sp.peak = attrf(a, "peak", 1.5); + w->setSectionStallParams(_wingSection, sp); } -char* FGFDM::dup(const char* s) +void FGFDM::parseFlap(const XMLAttributes* a, const char* name) { - int len=0; - while(s[len++]); - char* s2 = new char[len+1]; - char* p = s2; - while((*p++ = *s++)); - s2[len] = 0; - return s2; + FlapParams fp; + fp.start = attrf(a, "start"); + fp.end = attrf(a, "end"); + if (!strcmp(name, "slat")) { + fp.aoa = attrf(a, "aoa"); + } + else { + fp.lift = attrf(a, "lift"); + } + fp.drag = attrf(a, "drag"); + if (!strcmp(name, "flap0")) { + ((Wing*)_currObj)->setFlapParams(_wingSection, WING_FLAP0, fp); + } + if (!strcmp(name, "flap1")) { + ((Wing*)_currObj)->setFlapParams(_wingSection, WING_FLAP1, fp); + } + if (!strcmp(name, "spoiler")) { + ((Wing*)_currObj)->setFlapParams(_wingSection, WING_SPOILER, fp); + } + if (!strcmp(name, "slat")) { + ((Wing*)_currObj)->setFlapParams(_wingSection, WING_SLAT, fp); + } } -int FGFDM::attri(XMLAttributes* atts, const char* attr) +void FGFDM::parseControlSetting(const XMLAttributes* a) +{ + // A cruise or approach control setting + float value = attrf(a, "value", 0); + _airplane.addControlSetting(_airplaneCfg, a->getValue("axis"), value); +} + +void FGFDM::parseControlIn(const XMLAttributes* a) +{ + // map input property to a YASim control + ControlMap* cm = _airplane.getControlMap(); + ControlMap::Control control = cm->parseControl(a->getValue("control")); + ControlMap::ObjectID oid = cm->getObjectID(_currObj, _wingSection); + int opt = 0; + opt |= a->hasAttribute("split") ? ControlMap::OPT_SPLIT : 0; + opt |= a->hasAttribute("invert") ? ControlMap::OPT_INVERT : 0; + opt |= a->hasAttribute("square") ? ControlMap::OPT_SQUARE : 0; + float src0, src1, dst0, dst1; + src0 = dst0 = cm->rangeMin(control); + src1 = dst1 = cm->rangeMax(control); + if(a->hasAttribute("src0")) { + src0 = attrf(a, "src0"); + src1 = attrf(a, "src1"); + dst0 = attrf(a, "dst0"); + dst1 = attrf(a, "dst1"); + } + cm->addMapping(a->getValue("axis"), control, oid, opt, src0, src1, dst0, dst1); +} + +void FGFDM::parseControlOut(const XMLAttributes* a) +{ + // A property output for a control on the current object + ControlMap* cm = _airplane.getControlMap(); + ControlMap::Control control = cm->parseControl(a->getValue("control")); + ControlMap::ObjectID oid = cm->getObjectID(_currObj, _wingSection); + + PropOut* p = new PropOut(); + p->prop = fgGetNode(a->getValue("prop"), true); + p->handle = cm->getOutputHandle(oid, control); + p->control = control; + p->left = !(a->hasAttribute("side") && + !strcmp("right", a->getValue("side"))); + p->min = attrf(a, "min", cm->rangeMin(control)); + p->max = attrf(a, "max", cm->rangeMax(control)); + _controlProps.add(p); +} + +void FGFDM::parseControlSpeed(const XMLAttributes* a) +{ + ControlMap* cm = _airplane.getControlMap(); + ControlMap::Control control = cm->parseControl(a->getValue("control")); + ControlMap::ObjectID oid = cm->getObjectID(_currObj, _wingSection); + int handle = cm->getOutputHandle(oid, control); + float time = attrf(a, "transition-time", 0); + cm->setTransitionTime(handle, time); +} + +int FGFDM::attri(const XMLAttributes* atts, const char* attr) { if(!atts->hasAttribute(attr)) { SG_LOG(SG_FLIGHT,SG_ALERT,"Missing '" << attr << @@ -1187,14 +1207,14 @@ int FGFDM::attri(XMLAttributes* atts, const char* attr) return attri(atts, attr, 0); } -int FGFDM::attri(XMLAttributes* atts, const char* attr, int def) +int FGFDM::attri(const XMLAttributes* atts, const char* attr, int def) { const char* val = atts->getValue(attr); if(val == 0) return def; else return atol(val); } -float FGFDM::attrf(XMLAttributes* atts, const char* attr) +float FGFDM::attrf(const XMLAttributes* atts, const char* attr) { if(!atts->hasAttribute(attr)) { SG_LOG(SG_FLIGHT,SG_ALERT,"Missing '" << attr << @@ -1204,14 +1224,21 @@ float FGFDM::attrf(XMLAttributes* atts, const char* attr) return attrf(atts, attr, 0); } -float FGFDM::attrf(XMLAttributes* atts, const char* attr, float def) +float FGFDM::attrf(const XMLAttributes* atts, const char* attr, float def) { const char* val = atts->getValue(attr); if(val == 0) return def; else return (float)atof(val); } -double FGFDM::attrd(XMLAttributes* atts, const char* attr) +void FGFDM::attrf_xyz(const XMLAttributes* atts, float* out) +{ + out[0] = attrf(atts, "x"); + out[1] = attrf(atts, "y"); + out[2] = attrf(atts, "z"); +} + +double FGFDM::attrd(const XMLAttributes* atts, const char* attr) { if(!atts->hasAttribute(attr)) { SG_LOG(SG_FLIGHT,SG_ALERT,"Missing '" << attr << @@ -1221,7 +1248,7 @@ double FGFDM::attrd(XMLAttributes* atts, const char* attr) return attrd(atts, attr, 0); } -double FGFDM::attrd(XMLAttributes* atts, const char* attr, double def) +double FGFDM::attrd(const XMLAttributes* atts, const char* attr, double def) { const char* val = atts->getValue(attr); if(val == 0) return def; @@ -1239,12 +1266,12 @@ double FGFDM::attrd(XMLAttributes* atts, const char* attr, double def) // Unfortunately, this usage creeped into existing configuration files // while I wasn't active, and it's going to be hard to remove. Issue // a warning to nag people into changing their ways for now... -bool FGFDM::attrb(XMLAttributes* atts, const char* attr) +bool FGFDM::attrb(const XMLAttributes* atts, const char* attr) { const char* val = atts->getValue(attr); if(val == 0) return false; - if(eq(val,"true")) { + if(!strcmp(val,"true")) { SG_LOG(SG_FLIGHT, SG_ALERT, "Warning: " << "deprecated 'true' boolean in YASim configuration file. " << "Use numeric booleans (attribute=\"1\") instead"); diff --git a/src/FDM/YASim/FGFDM.hpp b/src/FDM/YASim/FGFDM.hpp index 28eabba21..a546d1262 100644 --- a/src/FDM/YASim/FGFDM.hpp +++ b/src/FDM/YASim/FGFDM.hpp @@ -4,7 +4,6 @@ #include #include -#include "yasim-common.hpp" #include "Airplane.hpp" #include "Vector.hpp" @@ -32,30 +31,68 @@ public: float getVehicleRadius(void) const { return _vehicle_radius; } private: - struct EngRec { char* prefix; Thruster* eng; }; - struct WeightRec { char* prop; float size; int handle; }; - struct PropOut { SGPropertyNode* prop; int handle, type; bool left; - float min, max; }; + struct EngRec { + char* prefix {nullptr}; + Thruster* eng {nullptr}; + }; + struct WeightRec { + char* prop {nullptr}; + float size {0}; + int handle {0}; + }; + struct PropOut { + SGPropertyNode* prop {nullptr}; + int handle {0}; + ControlMap::Control control; + bool left {false}; + float min {0}; + float max {0}; + }; + void parseAirplane(const XMLAttributes* a); + void parseApproachCruise(const XMLAttributes* a, const char* name); + void parseSolveWeight(const XMLAttributes* a); + void parseCockpit(const XMLAttributes* a); + + void setOutputProperties(float dt); - Rotor* parseRotor(XMLAttributes* a, const char* name); - Wing* parseWing(XMLAttributes* a, const char* name, Version * version); + void parseRotor(const XMLAttributes* a, const char* name); + void parseRotorGear(const XMLAttributes* a); + void parseWing(const XMLAttributes* a, const char* name, Airplane* airplane); int parseOutput(const char* name); - void parseWeight(XMLAttributes* a); - void parseTurbineEngine(XMLAttributes* a); - void parsePistonEngine(XMLAttributes* a); - void parsePropeller(XMLAttributes* a); - bool eq(const char* a, const char* b); - bool caseeq(const char* a, const char* b); - char* dup(const char* s); - int attri(XMLAttributes* atts, const char* attr); - int attri(XMLAttributes* atts, const char* attr, int def); - float attrf(XMLAttributes* atts, const char* attr); - float attrf(XMLAttributes* atts, const char* attr, float def); - double attrd(XMLAttributes* atts, const char* attr); - double attrd(XMLAttributes* atts, const char* attr, double def); - bool attrb(XMLAttributes* atts, const char* attr); + void parseWeight(const XMLAttributes* a); + void parseStall(const XMLAttributes* a); + void parseFlap(const XMLAttributes* a, const char* name); + + void parseTurbineEngine(const XMLAttributes* a); + void parsePistonEngine(const XMLAttributes* a); + void parsePropeller(const XMLAttributes* a); + void parseThruster(const XMLAttributes* a); + void parseJet(const XMLAttributes* a); + void parseHitch(const XMLAttributes* a); + void parseTow(const XMLAttributes* a); + void parseWinch(const XMLAttributes* a); + void parseGear(const XMLAttributes* a); + void parseHook(const XMLAttributes* a); + void parseLaunchbar(const XMLAttributes* a); + void parseFuselage(const XMLAttributes* a); + void parseTank(const XMLAttributes* a); + void parseBallast(const XMLAttributes* a); + void parseControlSetting(const XMLAttributes* a); + void parseControlIn(const XMLAttributes* a); + void parseControlOut(const XMLAttributes* a); + void parseControlSpeed(const XMLAttributes* a); + + + int attri(const XMLAttributes* atts, const char* attr); + int attri(const XMLAttributes* atts, const char* attr, int def); + float attrf(const XMLAttributes* atts, const char* attr); + float attrf(const XMLAttributes* atts, const char* attr, float def); + void attrf_xyz(const XMLAttributes* atts, float* out); + double attrd(const XMLAttributes* atts, const char* attr); + double attrd(const XMLAttributes* atts, const char* attr, double def); + bool attrb(const XMLAttributes* atts, const char* attr); // The core Airplane object we manage. Airplane _airplane; @@ -73,12 +110,13 @@ private: Vector _controlProps; // Radius of the vehicle, for intersection testing. - float _vehicle_radius; + float _vehicle_radius {0}; // Parsing temporaries - void* _currObj; - bool _cruiseCurr; - int _nextEngine; + void* _currObj {nullptr}; + Airplane::Configuration _airplaneCfg; + int _nextEngine {0}; + int _wingSection {0}; class FuelProps { diff --git a/src/FDM/YASim/Math.hpp b/src/FDM/YASim/Math.hpp index 1aca16478..d8610bd8b 100644 --- a/src/FDM/YASim/Math.hpp +++ b/src/FDM/YASim/Math.hpp @@ -50,6 +50,8 @@ public: out[2] = v[2]; } + static void zero3(float* out) { out[0] = out[1] = out[2] = 0; } + static inline float dot3(const float* a, const float* b) { return a[0]*b[0] + a[1]*b[1] + a[2]*b[2]; } diff --git a/src/FDM/YASim/Model.cpp b/src/FDM/YASim/Model.cpp index a5d442fe4..5c4e50f9f 100644 --- a/src/FDM/YASim/Model.cpp +++ b/src/FDM/YASim/Model.cpp @@ -61,6 +61,12 @@ Model::Model() _fAeroXN = _modelN->getNode("f-x-drag", true); _fAeroYN = _modelN->getNode("f-y-side", true); _fAeroZN = _modelN->getNode("f-z-lift", true); + _fGravXN = _modelN->getNode("gravity-x", true); + _fGravYN = _modelN->getNode("gravity-y", true); + _fGravZN = _modelN->getNode("gravity-z", true); + _fSumXN = _modelN->getNode("f-sum-x", true); + _fSumYN = _modelN->getNode("f-sum-y", true); + _fSumZN = _modelN->getNode("f-sum-z", true); _gefxN = fgGetNode("/fdm/yasim/debug/ground-effect/ge-f-x", true); _gefyN = fgGetNode("/fdm/yasim/debug/ground-effect/ge-f-y", true); @@ -293,8 +299,7 @@ void Model::calcForces(State* s) // Do each surface, remembering that the local velocity at each // point is different due to rotation. - float faero[3]; - faero[0] = faero[1] = faero[2] = 0; + float faero[3] {0,0,0}; for(i=0; i<_surfaces.size(); i++) { Surface* sf = (Surface*)_surfaces.get(i); @@ -374,6 +379,12 @@ void Model::calcForces(State* s) _fAeroXN->setFloatValue(faero[0]); _fAeroYN->setFloatValue(faero[1]); _fAeroZN->setFloatValue(faero[2]); + _fGravXN->setFloatValue(grav[0]); + _fGravYN->setFloatValue(grav[1]); + _fGravZN->setFloatValue(grav[2]); + _fSumXN->setFloatValue(faero[0]+grav[0]); + _fSumYN->setFloatValue(faero[1]+grav[1]); + _fSumZN->setFloatValue(faero[2]+grav[2]); } // Convert the velocity and rotation vectors to local coordinates float lrot[3], lv[3]; @@ -382,18 +393,18 @@ void Model::calcForces(State* s) // The landing gear for(i=0; i<_gears.size(); i++) { - float force[3], contact[3]; - Gear* g = (Gear*)_gears.get(i); + float force[3], contact[3]; + Gear* g = (Gear*)_gears.get(i); - g->calcForce(&_body, s, lv, lrot); - g->getForce(force, contact); - _body.addForce(contact, force); + g->calcForce(&_body, s, lv, lrot); + g->getForce(force, contact); + _body.addForce(contact, force); } // The arrester hook if(_hook) { _hook->calcForce(_ground_cb, &_body, s, lv, lrot); - float force[3], contact[3]; + float force[3], contact[3]; _hook->getForce(force, contact); _body.addForce(contact, force); } @@ -401,13 +412,13 @@ void Model::calcForces(State* s) // The launchbar/holdback if(_launchbar) { _launchbar->calcForce(_ground_cb, &_body, s, lv, lrot); - float forcelb[3], contactlb[3], forcehb[3], contacthb[3]; + float forcelb[3], contactlb[3], forcehb[3], contacthb[3]; _launchbar->getForce(forcelb, contactlb, forcehb, contacthb); _body.addForce(contactlb, forcelb); _body.addForce(contacthb, forcehb); } -// The hitches + // The hitches for(i=0; i<_hitches.size(); i++) { float force[3], contact[3]; Hitch* h = (Hitch*)_hitches.get(i); diff --git a/src/FDM/YASim/Model.hpp b/src/FDM/YASim/Model.hpp index 75e2de16e..a455b9133 100644 --- a/src/FDM/YASim/Model.hpp +++ b/src/FDM/YASim/Model.hpp @@ -118,6 +118,12 @@ private: SGPropertyNode_ptr _fAeroXN; SGPropertyNode_ptr _fAeroYN; SGPropertyNode_ptr _fAeroZN; + SGPropertyNode_ptr _fSumXN; + SGPropertyNode_ptr _fSumYN; + SGPropertyNode_ptr _fSumZN; + SGPropertyNode_ptr _fGravXN; + SGPropertyNode_ptr _fGravYN; + SGPropertyNode_ptr _fGravZN; SGPropertyNode_ptr _gefxN; SGPropertyNode_ptr _gefyN; SGPropertyNode_ptr _gefzN; diff --git a/src/FDM/YASim/RigidBody.cpp b/src/FDM/YASim/RigidBody.cpp index 2824b5e9e..bf635ab16 100644 --- a/src/FDM/YASim/RigidBody.cpp +++ b/src/FDM/YASim/RigidBody.cpp @@ -69,9 +69,7 @@ void RigidBody::setMass(int handle, float mass, const float* pos, bool isStatic) void RigidBody::getMassPosition(int handle, float* out) const { - out[0] = _masses[handle].p[0]; - out[1] = _masses[handle].p[1]; - out[2] = _masses[handle].p[2]; + Math::set3(_masses[handle].p, out); } // Calcualtes the rotational velocity of a particular point. All @@ -84,56 +82,54 @@ void RigidBody::pointVelocity(const float* pos, const float* rot, float* out) void RigidBody::_recalcStatic() { - // aggregate all masses that do not change (e.g. fuselage, wings) into one point mass - _staticMass.m = 0; - _staticMass.p[0] = 0; - _staticMass.p[1] = 0; - _staticMass.p[2] = 0; - int i; - int s = 0; - for(i=0; i<_nMasses; i++) { - if (_masses[i].isStatic) { - s++; - float m = _masses[i].m; - _staticMass.m += m; - _staticMass.p[0] += m * _masses[i].p[0]; - _staticMass.p[1] += m * _masses[i].p[1]; - _staticMass.p[2] += m * _masses[i].p[2]; - } - } - Math::mul3(1/_staticMass.m, _staticMass.p, _staticMass.p); - if (_bodyN != 0) { - _bodyN->getNode("aggregated-mass", true)->setFloatValue(_staticMass.m); - _bodyN->getNode("aggregated-count", true)->setIntValue(s); - _bodyN->getNode("aggregated-pos-x", true)->setFloatValue(_staticMass.p[0]); - _bodyN->getNode("aggregated-pos-y", true)->setFloatValue(_staticMass.p[1]); - _bodyN->getNode("aggregated-pos-z", true)->setFloatValue(_staticMass.p[2]); - } - // Now the inertia tensor: - for(i=0; i<9; i++) - _tI_static[i] = 0; + // aggregate all masses that do not change (e.g. fuselage, wings) into one point mass + _staticMass.m = 0; + Math::zero3(_staticMass.p); + int i; + int s = 0; + for(i=0; i<_nMasses; i++) { + if (_masses[i].isStatic) { + s++; + float mass = _masses[i].m; + _staticMass.m += mass; + float momentum[3]; + Math::mul3(mass, _masses[i].p, momentum); + Math::add3(momentum, _staticMass.p, _staticMass.p); + } + } + Math::mul3(1/_staticMass.m, _staticMass.p, _staticMass.p); + if (_bodyN != 0) { + _bodyN->getNode("aggregated-mass", true)->setFloatValue(_staticMass.m); + _bodyN->getNode("aggregated-count", true)->setIntValue(s); + _bodyN->getNode("aggregated-pos-x", true)->setFloatValue(_staticMass.p[0]); + _bodyN->getNode("aggregated-pos-y", true)->setFloatValue(_staticMass.p[1]); + _bodyN->getNode("aggregated-pos-z", true)->setFloatValue(_staticMass.p[2]); + } + // Now the inertia tensor: + for(i=0; i<9; i++) + _tI_static[i] = 0; - for(i=0; i<_nMasses; i++) { - if (_masses[i].isStatic) { - float m = _masses[i].m; + for(i=0; i<_nMasses; i++) { + if (_masses[i].isStatic) { + float m = _masses[i].m; - float x = _masses[i].p[0] - _staticMass.p[0]; - float y = _masses[i].p[1] - _staticMass.p[1]; - float z = _masses[i].p[2] - _staticMass.p[2]; + float x = _masses[i].p[0] - _staticMass.p[0]; + float y = _masses[i].p[1] - _staticMass.p[1]; + float z = _masses[i].p[2] - _staticMass.p[2]; - float xy = m*x*y; float yz = m*y*z; float zx = m*z*x; - float x2 = m*x*x; float y2 = m*y*y; float z2 = m*z*z; + float xy = m*x*y; float yz = m*y*z; float zx = m*z*x; + float x2 = m*x*x; float y2 = m*y*y; float z2 = m*z*z; - // tensor is symmetric, so we can save some calculations in the loop - _tI_static[0] += y2+z2; _tI_static[1] -= xy; _tI_static[2] -= zx; - _tI_static[4] += x2+z2; _tI_static[5] -= yz; - _tI_static[8] += x2+y2; - } - } - // copy symmetric elements - _tI_static[3] = _tI_static[1]; - _tI_static[6] = _tI_static[2]; - _tI_static[7] = _tI_static[5]; + // tensor is symmetric, so we can save some calculations in the loop + _tI_static[0] += y2+z2; _tI_static[1] -= xy; _tI_static[2] -= zx; + _tI_static[4] += x2+z2; _tI_static[5] -= yz; + _tI_static[8] += x2+y2; + } + } + // copy symmetric elements + _tI_static[3] = _tI_static[1]; + _tI_static[6] = _tI_static[2]; + _tI_static[7] = _tI_static[5]; } /// calculate the total mass, centre of gravity and inertia tensor @@ -151,44 +147,42 @@ void RigidBody::recalc() // Calculate the c.g and total mass // init with pre-calculated static mass _totalMass = _staticMass.m; - _cg[0] = _staticMass.m * _staticMass.p[0]; - _cg[1] = _staticMass.m * _staticMass.p[1]; - _cg[2] = _staticMass.m * _staticMass.p[2]; + Math::mul3(_staticMass.m, _staticMass.p, _cg); int i; for(i=0; i<_nMasses; i++) { - // only masses we did not aggregate - if (!_masses[i].isStatic) { - float m = _masses[i].m; - _totalMass += m; - _cg[0] += m * _masses[i].p[0]; - _cg[1] += m * _masses[i].p[1]; - _cg[2] += m * _masses[i].p[2]; - } + // only masses we did not aggregate + if (!_masses[i].isStatic) { + float mass = _masses[i].m; + _totalMass += mass; + float momentum[3]; + Math::mul3(mass, _masses[i].p, momentum); + Math::add3(momentum, _cg, _cg); + } } Math::mul3(1/_totalMass, _cg, _cg); // Now the inertia tensor: for(i=0; i<9; i++) - _tI[i] = _tI_static[i]; + _tI[i] = _tI_static[i]; for(i=0; i<_nMasses; i++) { - if (!_masses[i].isStatic) { - float m = _masses[i].m; + if (!_masses[i].isStatic) { + float m = _masses[i].m; - float x = _masses[i].p[0] - _cg[0]; - float y = _masses[i].p[1] - _cg[1]; - float z = _masses[i].p[2] - _cg[2]; - float mx = m*x; - float my = m*y; - float mz = m*z; - - float xy = mx*y; float yz = my*z; float zx = mz*x; - float x2 = mx*x; float y2 = my*y; float z2 = mz*z; + float x = _masses[i].p[0] - _cg[0]; + float y = _masses[i].p[1] - _cg[1]; + float z = _masses[i].p[2] - _cg[2]; + float mx = m*x; + float my = m*y; + float mz = m*z; + + float xy = mx*y; float yz = my*z; float zx = mz*x; + float x2 = mx*x; float y2 = my*y; float z2 = mz*z; - _tI[0] += y2+z2; _tI[1] -= xy; _tI[2] -= zx; - _tI[4] += x2+z2; _tI[5] -= yz; - _tI[8] += x2+y2; - } + _tI[0] += y2+z2; _tI[1] -= xy; _tI[2] -= zx; + _tI[4] += x2+z2; _tI[5] -= yz; + _tI[8] += x2+y2; + } } // copy symmetric elements _tI[3] = _tI[1]; @@ -201,8 +195,8 @@ void RigidBody::recalc() void RigidBody::reset() { - _torque[0] = _torque[1] = _torque[2] = 0; - _force[0] = _force[1] = _force[2] = 0; + Math::zero3(_torque); + Math::zero3(_force); } void RigidBody::addForce(const float* pos, const float* force) diff --git a/src/FDM/YASim/Surface.cpp b/src/FDM/YASim/Surface.cpp index 6f01dc836..2865b72c6 100644 --- a/src/FDM/YASim/Surface.cpp +++ b/src/FDM/YASim/Surface.cpp @@ -1,11 +1,14 @@ #include
+#include "yasim-common.hpp" +#include "Math.hpp" #include "Surface.hpp" namespace yasim { int Surface::s_idGenerator = 0; -Surface::Surface( Version * version ) : - _version(version) +Surface::Surface(Version* version, float* pos, float dragCoefficient = 1 ) : + _version(version), + _c0(dragCoefficient) { _id = s_idGenerator++; @@ -13,36 +16,59 @@ Surface::Surface( Version * version ) : _orient[3] = 0; _orient[4] = 1; _orient[5] = 0; _orient[6] = 0; _orient[7] = 0; _orient[8] = 1; + Math::set3(pos, _pos); + _surfN = fgGetNode("/fdm/yasim/debug/surfaces", true); if (_surfN != 0) { - _surfN = _surfN->getChild("surface", _id, true); - _fxN = _surfN->getNode("f-x", true); - _fyN = _surfN->getNode("f-y", true); - _fzN = _surfN->getNode("f-z", true); - _fabsN = _surfN->getNode("f-abs", true); - _alphaN = _surfN->getNode("alpha", true); - _stallAlphaN = _surfN->getNode("stall-alpha", true); - _flapN = _surfN->getNode("flap-pos", true); - _slatN = _surfN->getNode("slat-pos", true); - _spoilerN = _surfN->getNode("spoiler-pos", true); + _surfN = _surfN->getChild("surface", _id, true); + _fxN = _surfN->getNode("f-x", true); + _fyN = _surfN->getNode("f-y", true); + _fzN = _surfN->getNode("f-z", true); + _fabsN = _surfN->getNode("f-abs", true); + _alphaN = _surfN->getNode("alpha", true); + _stallAlphaN = _surfN->getNode("stall-alpha", true); + _flapN = _surfN->getNode("flap-pos", true); + _slatN = _surfN->getNode("slat-pos", true); + _spoilerN = _surfN->getNode("spoiler-pos", true); + _surfN->getNode("pos-x", true)->setFloatValue(pos[0]); + _surfN->getNode("pos-y", true)->setFloatValue(pos[1]); + _surfN->getNode("pos-z", true)->setFloatValue(pos[2]); + _surfN->getNode("chord",true)->setFloatValue(0); + _surfN->getNode("axis-x", true)->setFloatValue(0); + _surfN->getNode("axis-y", true)->setFloatValue(0); + _surfN->getNode("axis-z", true)->setFloatValue(0); } } -void Surface::setPosition(const float* p) +void Surface::setPosition(const float* pos) { - int i; - for(i=0; i<3; i++) _pos[i] = p[i]; + Math::set3(pos, _pos); if (_surfN != 0) { - _surfN->getNode("pos-x", true)->setFloatValue(p[0]); - _surfN->getNode("pos-y", true)->setFloatValue(p[1]); - _surfN->getNode("pos-z", true)->setFloatValue(p[2]); + _surfN->getNode("pos-x", true)->setFloatValue(pos[0]); + _surfN->getNode("pos-y", true)->setFloatValue(pos[1]); + _surfN->getNode("pos-z", true)->setFloatValue(pos[2]); + } +} + +void Surface::setChord(float chord) +{ + _chord = chord; + if (_surfN != 0) { + _surfN->getNode("chord",true)->setFloatValue(_chord); } } void Surface::setOrientation(const float* o) { - for(int i=0; i<9; i++) _orient[i] = o[i]; + for(int i=0; i<9; i++) _orient[i] = o[i]; + if (_surfN) { + float xaxis[3] {-1,0,0}; + Math::tmul33(_orient,xaxis, xaxis); + _surfN->getNode("axis-x", true)->setFloatValue(xaxis[0]); + _surfN->getNode("axis-y", true)->setFloatValue(xaxis[1]); + _surfN->getNode("axis-z", true)->setFloatValue(xaxis[2]); + } } @@ -67,8 +93,10 @@ void Surface::setSpoilerParams(float liftPenalty, float dragPenalty) void Surface::setFlapPos(float pos) { if (_flapPos != pos) { - _flapPos = pos; - if (_surfN != 0) _flapN->setFloatValue(pos); + _flapPos = pos; + if (_surfN != 0) { + _flapN->setFloatValue(pos); + } } } @@ -76,7 +104,9 @@ void Surface::setSlatPos(float pos) { if (_slatPos != pos) { _slatPos = pos; - if (_surfN != 0) _slatN->setFloatValue(pos); + if (_surfN != 0) { + _slatN->setFloatValue(pos); + } } } @@ -90,23 +120,24 @@ void Surface::setSpoilerPos(float pos) // Calculate the aerodynamic force given a wind vector v (in the // aircraft's "local" coordinates) and an air density rho. Returns a -// torque about the Y axis, too. +// torque about the Y axis ("pitch"), too. void Surface::calcForce(const float* v, const float rho, float* out, float* torque) { + // initialize outputs to zero + Math::zero3(out); + Math::zero3(torque); + // Split v into magnitude and direction: float vel = Math::mag3(v); // Zero velocity means zero force by definition (also prevents div0). if(vel == 0) { - int i; - for(i=0; i<3; i++) out[i] = torque[i] = 0; - return; + return; } // special case this so the logic below doesn't produce a non-zero // force; should probably have a "no force" flag instead... if(_cx == 0. && _cy == 0. && _cz == 0.) { - for(int i=0; i<3; i++) out[i] = torque[i] = 0.; return; } @@ -116,10 +147,10 @@ void Surface::calcForce(const float* v, const float rho, float* out, float* torq // "Rotate" by the incidence angle. Assume small angles, so we // need to diddle only the Z component, X is relatively unchanged - // by small rotations. + // by small rotations. sin(a) ~ a, cos(a) ~ 1 for small a float incidence = _incidence + _twist; out[2] += incidence * out[0]; // z' = z + incidence * x - + // Hold onto the local wind vector so we can multiply the induced // drag at the end. float lwind[3]; @@ -161,9 +192,9 @@ void Surface::calcForce(const float* v, const float rho, float* out, float* torq // roughly parallel with Z, the small-angle approximation // must change its X component. if( _version->isVersionOrNewer( Version::YASIM_VERSION_32 )) { - out[0] += incidence * out[2]; + out[0] += incidence * out[2]; } else { - out[2] -= incidence * out[0]; + out[2] -= incidence * out[0]; } // Convert back to external coordinates diff --git a/src/FDM/YASim/Surface.hpp b/src/FDM/YASim/Surface.hpp index 278c05a83..bd5c35f04 100644 --- a/src/FDM/YASim/Surface.hpp +++ b/src/FDM/YASim/Surface.hpp @@ -16,7 +16,7 @@ class Surface int _id; //index for property tree public: - Surface( Version * version ); + Surface(Version * version, float* pos, float dragCoefficient); int getID() const { return _id; }; static void resetIDgen() { s_idGenerator = 0; }; @@ -25,8 +25,8 @@ public: void setPosition(const float* p); void getPosition(float* out) const { Math::set3(_pos, out); } - // Distance scale along the X axis - void setChord(float chord) { _chord = chord; } + // Distance scale along the X axis used for torque (pitch) calculation + void setChord(float chord); // Slats act to move the stall peak by the specified angle, and // increase drag by the multiplier specified. @@ -59,16 +59,18 @@ public: // The offset from base incidence for this surface. void setTwist(float angle) { _twist = angle; } - void setTotalDrag(float c0) { _c0 = c0; } - float getTotalDrag() const { return _c0; } + void setTotalForceCoefficient(float c0) { _c0 = c0; } + void mulTotalForceCoefficient(float factor) { _c0 *= factor; } + float getTotalForceCoefficient() const { return _c0; } - void setXDrag(float cx) { _cx = cx; } - void setYDrag(float cy) { _cy = cy; } - void setZDrag(float cz) { _cz = cz; } - float getXDrag() const { return _cx; } + void setDragCoefficient(float cx) { _cx = cx; } + float getDragCoefficient() const { return _cx; } + void setYDrag(float cy) { _cy = cy; } + void setLiftCoefficient(float cz) { _cz = cz; } + float getLiftCoefficient() const { return _cz; } // zero-alpha Z drag ("camber") specified as a fraction of cz - void setBaseZDrag(float cz0) { _cz0 = cz0; } + void setZeroAlphaLift(float cz0) { _cz0 = cz0; } // i: 0 == forward, 1 == backwards void setStallPeak(int i, float peak) { _peaks[i] = peak; } @@ -87,6 +89,7 @@ public: private: SGPropertyNode_ptr _surfN; + Version * _version; float stallFunc(float* v); float flapLift(float alpha); @@ -123,7 +126,6 @@ private: float _stallAlpha {0}; float _alpha {0}; - Version * _version; SGPropertyNode* _fxN; SGPropertyNode* _fyN; SGPropertyNode* _fzN; diff --git a/src/FDM/YASim/Vector.hpp b/src/FDM/YASim/Vector.hpp index 33079558b..5dbf8b42f 100644 --- a/src/FDM/YASim/Vector.hpp +++ b/src/FDM/YASim/Vector.hpp @@ -1,6 +1,10 @@ #ifndef _VECTOR_HPP #define _VECTOR_HPP +#include +#include + +namespace yasim { // // Excruciatingly simple vector-of-pointers class. Easy & useful. // No support for addition of elements anywhere but at the end of the @@ -19,9 +23,9 @@ public: private: void realloc(); - int _nelem; - int _sz; - void** _array; + int _nelem {0}; + int _sz {0}; + void** _array {nullptr}; }; inline Vector::Vector() @@ -46,11 +50,14 @@ inline int Vector::add(void* p) inline void* Vector::get(int i) const { + assert(i >= 0 and i < _sz); return _array[i]; + } inline void Vector::set(int i, void* p) { + assert(i >= 0 and i < _sz); _array[i] = p; } @@ -73,5 +80,5 @@ inline void Vector::realloc() delete[] _array; _array = array; } - +}; //namespace yasim #endif // _VECTOR_HPP diff --git a/src/FDM/YASim/Version.cpp b/src/FDM/YASim/Version.cpp index c449db034..c54f207df 100644 --- a/src/FDM/YASim/Version.cpp +++ b/src/FDM/YASim/Version.cpp @@ -18,6 +18,8 @@ void Version::setVersion( const char * version ) _version = YASIM_VERSION_32; } else if( v == "2017.2" ) { _version = YASIM_VERSION_2017_2; + } else if( v == "2018.1" ) { + _version = YASIM_VERSION_2018_1; } else if( v == "YASIM_VERSION_CURRENT" ) { _version = YASIM_VERSION_CURRENT; } else { diff --git a/src/FDM/YASim/Version.hpp b/src/FDM/YASim/Version.hpp index 10e7e3e01..169379962 100644 --- a/src/FDM/YASim/Version.hpp +++ b/src/FDM/YASim/Version.hpp @@ -12,7 +12,8 @@ public: YASIM_VERSION_ORIGINAL = 0, YASIM_VERSION_32, YASIM_VERSION_2017_2, - YASIM_VERSION_CURRENT = YASIM_VERSION_2017_2 + YASIM_VERSION_2018_1, + YASIM_VERSION_CURRENT = YASIM_VERSION_2018_1 } YASIM_VERSION; void setVersion( const char * version ); diff --git a/src/FDM/YASim/Wing.cpp b/src/FDM/YASim/Wing.cpp index 81aa4e3e9..6b8ad1a90 100644 --- a/src/FDM/YASim/Wing.cpp +++ b/src/FDM/YASim/Wing.cpp @@ -1,135 +1,104 @@ #include "yasim-common.hpp" +#include "Model.hpp" #include "Surface.hpp" #include "Wing.hpp" namespace yasim { -Wing::Wing(Version *ver, bool mirror, float* base, float chord, - float length, float taper, float sweep, float dihedral, float twist) : +Wing::Wing(Version *ver, bool mirror) : _version(ver), - _mirror(mirror), - _chord(chord), - _length(length), - _taper(taper), - _sweep(sweep), - _dihedral(dihedral), - _twist(twist) + _mirror(mirror) { - Math::set3(base, _base); - _meanChord = _chord*(_taper+1)*0.5f; - calculateWingCoordinateSystem(); - calculateTip(); - calculateSpan(); - calculateMAC(); } Wing::~Wing() { - for(int i=0; i<_surfs.size(); i++) { - SurfRec* s = (SurfRec*)_surfs.get(i); - delete s->surface; - delete s; + WingSection* ws; + for (int s=0; s < _sections.size(); s++){ + ws = (WingSection*)_sections.get(s); + for(int i=0; i_surfs.size(); i++) { + SurfRec* s = (SurfRec*)ws->_surfs.get(i); + delete s->surface; + delete s; + } } } -void Wing::setIncidence(float incidence) +int Wing::addWingSection(float* base, float chord, float wingLength, float taper, + float sweep, float dihedral, float twist, float camber, + float idrag, float incidence) { - _incidence = incidence; - for(int i=0; i<_surfs.size(); i++) - ((SurfRec*)_surfs.get(i))->surface->setIncidence(incidence); -} - -void Wing::setFlap0Params(float start, float end, float lift, float drag) -{ - _flap0Start = start; - _flap0End = end; - _flap0Lift = lift; - _flap0Drag = drag; -} - -void Wing::setFlap1Params(float start, float end, float lift, float drag) -{ - _flap1Start = start; - _flap1End = end; - _flap1Lift = lift; - _flap1Drag = drag; -} - -void Wing::setSlatParams(float start, float end, float aoa, float drag) -{ - _slatStart = start; - _slatEnd = end; - _slatAoA = aoa; - _slatDrag = drag; -} - -void Wing::setSpoilerParams(float start, float end, float lift, float drag) -{ - _spoilerStart = start; - _spoilerEnd = end; - _spoilerLift = lift; - _spoilerDrag = drag; -} - -void Wing::setFlap0Pos(float lval, float rval) -{ - lval = Math::clamp(lval, -1, 1); - rval = Math::clamp(rval, -1, 1); - for(int i=0; i<_flap0Surfs.size(); i++) { - ((Surface*)_flap0Surfs.get(i))->setFlapPos(lval); - if(_mirror) ((Surface*)_flap0Surfs.get(++i))->setFlapPos(rval); + WingSection* ws = new WingSection; + if (_sections.size() == 0) { + // first section + Math::set3(base, _base); + ws->_rootChord = _float2chord(base, chord); + ws->_sectionIncidence = incidence; + } else { + WingSection* prev = (WingSection*)_sections.get(_sections.size()-1); + //use old wing tip instead of base argument + ws->_rootChord = prev->_tipChord; + ws->_sectionIncidence = prev->_sectionIncidence + prev->_twist; } -} - -void Wing::setFlap0Effectiveness(float lval) -{ - lval = Math::clamp(lval, 1, 10); - for(int i=0; i<_flap0Surfs.size(); i++) { - ((Surface*)_flap0Surfs.get(i))->setFlapEffectiveness(lval); + ws->_length = wingLength; + ws->_taper = taper; + ws->_sweepAngleCenterLine = sweep; + ws->_dihedral = dihedral; + ws->_twist = twist; + ws->_camber = camber; + ws->_inducedDrag = idrag; + ws->calculateGeometry(); + int idx = _sections.add(ws); + // first / only section + if (idx == 0) { + _mac = ws->getMAC(); + _wingspan = ws->_wingspan; + _area = ws->getArea(); + _meanChord = ws->_meanChord; } -} - -void Wing::setFlap1Pos(float lval, float rval) -{ - lval = Math::clamp(lval, -1, 1); - rval = Math::clamp(rval, -1, 1); - for(int i=0; i<_flap1Surfs.size(); i++) { - ((Surface*)_flap1Surfs.get(i))->setFlapPos(lval); - if(_mirror) ((Surface*)_flap1Surfs.get(++i))->setFlapPos(rval); + // append section: Calculate wing MAC from MACs of section and prev wing + else { + _mac = Wing::calculateMAC(_mac, ws->getMAC()); + _wingspan += ws->_wingspan; + _area += ws->getArea(); + _meanChord = _meanChord * ws->_meanChord * 0.5f; } + _chord2float(ws->_tipChord, _tip); + return idx; } -void Wing::setFlap1Effectiveness(float lval) +Chord Wing::_float2chord(float* pos, float lenght) { - lval = Math::clamp(lval, 1, 10); - for(int i=0; i<_flap1Surfs.size(); i++) { - ((Surface*)_flap1Surfs.get(i))->setFlapEffectiveness(lval); - } + Chord c; + c.x = pos[0]; + c.y = pos[1]; + c.z = pos[2]; + c.length = lenght; + return c; } -void Wing::setSpoilerPos(float lval, float rval) +void Wing::_chord2float(Chord c, float* pos) { - lval = Math::clamp(lval, 0, 1); - rval = Math::clamp(rval, 0, 1); - for(int i=0; i<_spoilerSurfs.size(); i++) { - ((Surface*)_spoilerSurfs.get(i))->setSpoilerPos(lval); - if(_mirror) ((Surface*)_spoilerSurfs.get(++i))->setSpoilerPos(rval); - } + pos[0] = c.x; + pos[1] = c.y; + pos[2] = c.z; } -void Wing::setSlatPos(float val) +void Wing::WingSection::calculateGeometry() { - val = Math::clamp(val, 0, 1); - for(int i=0; i<_slatSurfs.size(); i++) - ((Surface*)_slatSurfs.get(i))->setSlatPos(val); + _meanChord = _rootChord.length*(_taper+1)*0.5f; + calculateWingCoordinateSystem(); + calculateTipChord(); + calculateSpan(); + calculateMAC(); } -void Wing::calculateWingCoordinateSystem() { +void Wing::WingSection::calculateWingCoordinateSystem() { // prepare wing coordinate system, ignoring incidence and twist for now // (tail incidence is varied by the solver) // Generating a unit vector pointing out the left wing. float left[3]; - left[0] = -Math::tan(_sweep); + left[0] = -Math::tan(_sweepAngleCenterLine); left[1] = Math::cos(_dihedral); left[2] = Math::sin(_dihedral); Math::unit3(left, left); @@ -155,180 +124,311 @@ void Wing::calculateWingCoordinateSystem() { for(i=3; i<6; i++) _rightOrient[i] = -_rightOrient[i]; } -void Wing::calculateTip() { +void Wing::WingSection::calculateTipChord() { float *y = _orient+3; - Math::mul3(_length, y, _tip); - Math::add3(_base, _tip, _tip); + _tipChord.x = _rootChord.x + _length * y[0]; + _tipChord.y = _rootChord.y + _length * y[1]; + _tipChord.z = _rootChord.z + _length * y[2]; + _tipChord.length = _rootChord.length * _taper; } -void Wing::calculateSpan() +void Wing::WingSection::calculateSpan() { // wingspan in y-direction (not for vstab) - _wingspan = Math::abs(2*_tip[1]); - _netSpan = Math::abs(2*(_tip[1]-_base[1])); + _wingspan = Math::abs(2*_tipChord.y); _aspectRatio = _wingspan / _meanChord; } -void Wing::calculateMAC() +void Wing::WingSection::calculateMAC() { + //FIXME call static method, use absolute y values + // http://www.nasascale.org/p2/wp-content/uploads/mac-calculator.htm - const float commonFactor = _chord*(0.5+_taper)/(3*_chord*(1+_taper)); - _mac = _chord-(2*_chord*(1-_taper)*commonFactor); - _macRootDistance = _netSpan*commonFactor; - _macX = _base[0]-Math::tan(_sweep) * _macRootDistance + _mac/2; + const float commonFactor = _rootChord.length*(0.5+_taper)/(3*_rootChord.length*(1+_taper)); + _mac.length = _rootChord.length-(2*_rootChord.length*(1-_taper)*commonFactor); + _mac.y = Math::abs(2*(_tipChord.y-_rootChord.y))*commonFactor; + _mac.x = _rootChord.x-Math::tan(_sweepAngleCenterLine)*_mac.y + _mac.length/2; + _mac.y += _rootChord.y; } -void Wing::compile() +float Wing::WingSection::calculateSweepAngleLeadingEdge() { - // Have we already been compiled? - if(! _surfs.empty()) return; - - // Assemble the start/end coordinates of all control surfaces - // and the wing itself into an array, sort them, - // and remove duplicates. This gives us the boundaries of our - // segments. - const int NUM_BOUNDS {10}; - float bounds[NUM_BOUNDS]; - bounds[0] = _flap0Start; bounds[1] = _flap0End; - bounds[2] = _flap1Start; bounds[3] = _flap1End; - bounds[4] = _spoilerStart; bounds[5] = _spoilerEnd; - bounds[6] = _slatStart; bounds[7] = _slatEnd; - //and don't forget the root and the tip of the wing itself - bounds[8] = 0; bounds[9] = 1; - - // Sort in increasing order - int i; - for(i=0; isurface->setIncidence(incidence + _sectionIncidence); +} + +// root and tip (x,y) coordinates are on leading edge +Chord Wing::calculateMAC(Chord root, Chord tip) +{ + assert(root.length > 0); + //taper = tip.length / root.length; + const float commonFactor = (root.length*0.5+tip.length)/(3*(root.length+tip.length)); + Chord m; + m.length = root.length-(2*(root.length - tip.length)*commonFactor); + m.y = Math::abs(2*(tip.y - root.y))*commonFactor + root.y; + m.x = root.x - (root.x - tip.x)*(root.y - m.y)/(root.y - tip.y); + return m; +} + +void Wing::setFlapParams(int section, WingFlaps type, FlapParams fp) +{ + ((WingSection*)_sections.get(section))->_flapParams[type] = fp; +} + +void Wing::setSectionDrag(int section, float pdrag) +{ + ((WingSection*)_sections.get(section))->_dragScale = pdrag; +} + +void Wing::setSectionStallParams(int section, StallParams sp) +{ + ((WingSection*)_sections.get(section))->_stallParams = sp; +} + +void Wing::setFlapPos(WingFlaps type,float lval, float rval) +{ + float min {-1}; + if (type == WING_SPOILER || type == WING_SLAT) { + min = 0; } - - // Calculate a "nominal" segment length equal to an average chord, - // normalized to lie within 0-1 over the length of the wing. - float segLen = _meanChord / _length; - - // Now go through each boundary and make segments - for(i=0; i<(nbounds-1); i++) { - float start = bounds[i]; - float end = bounds[i+1]; - float mid = (start+end)/2; - - bool hasFlap0=0, hasFlap1=0, hasSlat=0, hasSpoiler=0; - if(_flap0Start < mid && mid < _flap0End) hasFlap0 = 1; - if(_flap1Start < mid && mid < _flap1End) hasFlap1 = 1; - if(_slatStart < mid && mid < _slatEnd) hasSlat = 1; - if(_spoilerStart < mid && mid < _spoilerEnd) hasSpoiler = 1; - - // FIXME: Should probably detect an error here if both flap0 - // and flap1 are set. Right now flap1 overrides. - - int nSegs = (int)Math::ceil((end-start)/segLen); - if (_twist != 0 && nSegs < 8) // more segments if twisted - nSegs = 8; - float segWid = _length * (end - start)/nSegs; - - int j; - for(j=0; j_flapSurfs[type].size(); i++) { + switch (type) { + case WING_FLAP0: + case WING_FLAP1: + ((Surface*)ws->_flapSurfs[type].get(i))->setFlapPos(lval); + if(_mirror) ((Surface*)ws->_flapSurfs[type].get(++i))->setFlapPos(rval); + break; + case WING_SLAT: + ((Surface*)ws->_flapSurfs[type].get(i))->setSlatPos(lval); + break; + case WING_SPOILER: + ((Surface*)ws->_flapSurfs[type].get(i))->setSpoilerPos(lval); + if(_mirror) ((Surface*)ws->_flapSurfs[type].get(++i))->setSpoilerPos(rval); + break; } } } - // Last of all, re-set the incidence in case setIncidence() was - // called before we were compiled. - setIncidence(_incidence); } -void Wing::addSurface(Surface* s, float weight, float twist) +void Wing::setFlapEffectiveness(WingFlaps f, float lval) { - SurfRec *sr = new SurfRec(); - sr->surface = s; - sr->weight = weight; - s->setTotalDrag(sr->weight); - s->setTwist(twist); - _surfs.add(sr); + lval = Math::clamp(lval, 1, 10); + WingSection* ws; + for (int section=0; section < _sections.size(); section++) + { + ws = (WingSection*)_sections.get(section); + for(int i=0; i_flapSurfs[f].size(); i++) { + ((Surface*)ws->_flapSurfs[f].get(i))->setFlapEffectiveness(lval); + } + } } -void Wing::setDragScale(float scale) + +void Wing::compile() +{ + WingSection* ws; + for (int section=0; section < _sections.size(); section++) + { + ws = (WingSection*)_sections.get(section); + // Have we already been compiled? + if(! ws->_surfs.empty()) return; + + // Assemble the start/end coordinates of all control surfaces + // and the wing itself into an array, sort them, + // and remove duplicates. This gives us the boundaries of our + // segments. + const int NUM_BOUNDS {10}; + float bounds[NUM_BOUNDS]; + bounds[0] = ws->_flapParams[WING_FLAP0].start; + bounds[1] = ws->_flapParams[WING_FLAP0].end; + bounds[2] = ws->_flapParams[WING_FLAP1].start; + bounds[3] = ws->_flapParams[WING_FLAP1].end; + bounds[4] = ws->_flapParams[WING_SPOILER].start; + bounds[5] = ws->_flapParams[WING_SPOILER].end; + bounds[6] = ws->_flapParams[WING_SLAT].start; + bounds[7] = ws->_flapParams[WING_SLAT].end; + //and don't forget the root and the tip of the wing itself + bounds[8] = 0; bounds[9] = 1; + + // Sort in increasing order + for(int i=0; i_meanChord / ws->_length; + + // Now go through each boundary and make segments + for(int i=0; i<(nbounds-1); i++) { + float start = bounds[i]; + float end = bounds[i+1]; + float mid = (start+end)/2; + + bool hasFlap0=0, hasFlap1=0, hasSlat=0, hasSpoiler=0; + if(ws->_flapParams[WING_FLAP0].start < mid && mid < ws->_flapParams[WING_FLAP0].end) + hasFlap0 = 1; + if(ws->_flapParams[WING_FLAP1].start < mid && mid < ws->_flapParams[WING_FLAP1].end) + hasFlap1 = 1; + if(ws->_flapParams[WING_SLAT].start < mid && mid < ws->_flapParams[WING_SLAT].end) + hasSlat = 1; + if(ws->_flapParams[WING_SPOILER].start < mid && mid < ws->_flapParams[WING_SPOILER].end) + hasSpoiler = 1; + + // FIXME: Should probably detect an error here if both flap0 + // and flap1 are set. Right now flap1 overrides. + + int nSegs = (int)Math::ceil((end-start)/segLen); + if (ws->_twist != 0 && nSegs < 8) // more segments if twisted + nSegs = 8; + float segWid = ws->_length * (end - start)/nSegs; + + for(int j=0; j_rootChord.length * (1 - (1-ws->_taper)*frac); + float weight = chord * segWid; + float twist = ws->_twist * frac; + + ws->newSurface(_version, pos, ws->_orient, chord, hasFlap0, hasFlap1, hasSlat, hasSpoiler, weight, twist); + + if(_mirror) { + pos[1] = -pos[1]; + ws->newSurface(_version, pos, ws->_rightOrient, chord, + hasFlap0, hasFlap1, hasSlat, hasSpoiler, weight, twist); + } + } + } + // Last of all, re-set the incidence in case setIncidence() was + // called before we were compiled. + setIncidence(_incidence); + } + writeInfoToProptree(); +} + +void Wing::multiplyLiftRatio(float factor) +{ + WingSection* ws; + for (int section=0; section < _sections.size(); section++) + { + ws = (WingSection*)_sections.get(section); + ws->multiplyLiftRatio(factor); + } +} + +void Wing::multiplyDragCoefficient(float factor) +{ + WingSection* ws; + for (int section=0; section < _sections.size(); section++) + { + ws = (WingSection*)_sections.get(section); + ws->multiplyDragCoefficient(factor); + } +} + +void Wing::setIncidence(float incidence) +{ + WingSection* ws; + for (int section=0; section < _sections.size(); section++) + { + ws = (WingSection*)_sections.get(section); + ws->setIncidence(incidence); + } +} + +void Wing::WingSection::setDragCoefficient(float scale) { _dragScale = scale; for(int i=0; i<_surfs.size(); i++) { SurfRec* s = (SurfRec*)_surfs.get(i); - s->surface->setTotalDrag(scale * s->weight); + s->surface->setTotalForceCoefficient(scale * s->weight); } } -void Wing::setLiftRatio(float ratio) +void Wing::WingSection::multiplyDragCoefficient(float factor) +{ + setDragCoefficient(_dragScale * factor); +} + +void Wing::WingSection::setLiftRatio(float ratio) { _liftRatio = ratio; for(int i=0; i<_surfs.size(); i++) - ((SurfRec*)_surfs.get(i))->surface->setZDrag(ratio); + ((SurfRec*)_surfs.get(i))->surface->setLiftCoefficient(ratio); } -Surface* Wing::newSurface(float* pos, float* orient, float chord, - bool hasFlap0, bool hasFlap1, bool hasSlat, bool hasSpoiler) +void Wing::WingSection::multiplyLiftRatio(float factor) { - Surface* s = new Surface(_version); + setLiftRatio(_liftRatio * factor); +} + +void Wing::WingSection::newSurface(Version* _version, float* pos, float* orient, float chord, bool hasFlap0, bool hasFlap1, bool hasSlat, bool hasSpoiler, float weight, float twist) +{ + Surface* s = new Surface(_version, pos, weight); - s->setPosition(pos); s->setOrientation(orient); s->setChord(chord); // Camber is expressed as a fraction of stall peak, so convert. - s->setBaseZDrag(_camber*_stallPeak); + s->setZeroAlphaLift(_camber*_stallParams.peak); // The "main" (i.e. normal) stall angle - float stallAoA = _stall - _stallWidth/4; + float stallAoA = _stallParams.aoa - _stallParams.width/4; s->setStall(0, stallAoA); - s->setStallWidth(0, _stallWidth); - s->setStallPeak(0, _stallPeak); + s->setStallWidth(0, _stallParams.width); + s->setStallPeak(0, _stallParams.peak); // The negative AoA stall is the same if we're using an symmetric // airfoil, otherwise a "little worse". if(_camber > 0) { s->setStall(1, stallAoA * 0.8f); - s->setStallWidth(1, _stallWidth * 0.5f); + s->setStallWidth(1, _stallParams.width * 0.5f); } else { s->setStall(1, stallAoA); if( _version->isVersionOrNewer( Version::YASIM_VERSION_2017_2 )) { // what was presumably meant - s->setStallWidth(1, _stallWidth); + s->setStallWidth(1, _stallParams.width); } else { // old code; presumably a copy&paste error - s->setStall(1, _stallWidth); + s->setStall(1, _stallParams.width); } } @@ -341,19 +441,23 @@ Surface* Wing::newSurface(float* pos, float* orient, float chord, s->setStallWidth(i, 0.01); } - if(hasFlap0) s->setFlapParams(_flap0Lift, _flap0Drag); - if(hasFlap1) s->setFlapParams(_flap1Lift, _flap1Drag); - if(hasSlat) s->setSlatParams(_slatAoA, _slatDrag); - if(hasSpoiler) s->setSpoilerParams(_spoilerLift, _spoilerDrag); + if(hasFlap0) s->setFlapParams(_flapParams[WING_FLAP0].lift, _flapParams[WING_FLAP0].drag); + if(hasFlap1) s->setFlapParams(_flapParams[WING_FLAP1].lift, _flapParams[WING_FLAP1].drag); + if(hasSlat) s->setSlatParams(_flapParams[WING_SLAT].aoa, _flapParams[WING_SLAT].drag); + if(hasSpoiler) s->setSpoilerParams(_flapParams[WING_SPOILER].lift, _flapParams[WING_SPOILER].drag); - if(hasFlap0) _flap0Surfs.add(s); - if(hasFlap1) _flap1Surfs.add(s); - if(hasSlat) _slatSurfs.add(s); - if(hasSpoiler) _spoilerSurfs.add(s); + if(hasFlap0) _flapSurfs[WING_FLAP0].add(s); + if(hasFlap1) _flapSurfs[WING_FLAP1].add(s); + if(hasSlat) _flapSurfs[WING_SLAT].add(s); + if(hasSpoiler) _flapSurfs[WING_SPOILER].add(s); s->setInducedDrag(_inducedDrag); - return s; + SurfRec *sr = new SurfRec(); + sr->surface = s; + sr->weight = weight; + s->setTwist(twist); + _surfs.add(sr); } void Wing::interp(const float* v1, const float* v2, const float frac, float* out) @@ -363,4 +467,81 @@ void Wing::interp(const float* v1, const float* v2, const float frac, float* out out[2] = v1[2] + frac*(v2[2]-v1[2]); } +void Wing::writeInfoToProptree() +{ + if (_wingN == nullptr) + return; + WingSection* ws = (WingSection*)_sections.get(0); + float chord = ws->_rootChord.length; + ws = (WingSection*)_sections.get(_sections.size()-1); + float taper = ws->_rootChord.length * ws->_taper; + _wingN->getNode("tip-x", true)->setFloatValue(_tip[0]); + _wingN->getNode("tip-y", true)->setFloatValue(_tip[1]); + _wingN->getNode("tip-z", true)->setFloatValue(_tip[2]); + _wingN->getNode("base-x", true)->setFloatValue(_base[0]); + _wingN->getNode("base-y", true)->setFloatValue(_base[1]); + _wingN->getNode("base-z", true)->setFloatValue(_base[2]); + _wingN->getNode("chord", true)->setFloatValue(chord); + _wingN->getNode("taper", true)->setFloatValue(taper); + _wingN->getNode("wing-span", true)->setFloatValue(_wingspan); + _wingN->getNode("wing-area", true)->setFloatValue(_wingspan*_meanChord); + _wingN->getNode("aspect-ratio", true)->setFloatValue(_aspectRatio); + _wingN->getNode("standard-mean-chord", true)->setFloatValue(_meanChord); + _wingN->getNode("mac", true)->setFloatValue(_mac.length); + _wingN->getNode("mac-x", true)->setFloatValue(_mac.x); + _wingN->getNode("mac-y", true)->setFloatValue(_mac.y); + + float wgt = 0; + float dragSum = 0; + + for (int section=0; section < _sections.size(); section++) { + ws = (WingSection*)_sections.get(section); + for (int surf=0; surf < ws->numSurfaces(); surf++) { + Surface* s = ws->getSurface(surf); + float drag = s->getTotalForceCoefficient(); + dragSum += drag; + + float mass = ws->getSurfaceWeight(surf); + mass = mass * Math::sqrt(mass); + wgt += mass; + } + } + _wingN->getNode("weight", true)->setFloatValue(wgt); + _wingN->getNode("drag", true)->setFloatValue(dragSum); +} + +// estimate a mass distibution and add masses to the model +// they will be scaled to match total mass of aircraft later +float Wing::updateModel(Model* model) +{ + _weight = 0; + WingSection* ws; + for (int section=0; section < _sections.size(); section++) { + ws = (WingSection*)_sections.get(section); + for(int surf=0; surf < ws->numSurfaces(); surf++) { + Surface* s = ws->getSurface(surf); + model->addSurface(s); + + float weight = ws->getSurfaceWeight(surf); + weight = weight * Math::sqrt(weight); + _weight += weight; + + float pos[3]; + s->getPosition(pos); + int mid = model->getBody()->addMass(weight, pos, true); + if (_wingN != nullptr) { + SGPropertyNode_ptr n = _wingN->getNode("surfaces", true)->getChild("surface", s->getID(), true); + n->getNode("c0", true)->setFloatValue(s->getTotalForceCoefficient()); + n->getNode("cdrag", true)->setFloatValue(s->getDragCoefficient()); + n->getNode("clift", true)->setFloatValue(s->getLiftCoefficient()); + n->getNode("mass-id", true)->setIntValue(mid); + } + } + } + if (_wingN != nullptr) { + _wingN->getNode("weight", true)->setFloatValue(_weight); + } + return _weight; +} + }; // namespace yasim diff --git a/src/FDM/YASim/Wing.hpp b/src/FDM/YASim/Wing.hpp index e7496c61d..6481ba16c 100644 --- a/src/FDM/YASim/Wing.hpp +++ b/src/FDM/YASim/Wing.hpp @@ -4,159 +4,171 @@ #include "Vector.hpp" #include "Version.hpp" #include "Math.hpp" +#include namespace yasim { class Surface; +class Model; + +struct FlapParams { + float start {0}; + float end {0}; + float lift {0}; + float drag {0}; + float aoa {0}; +}; + +struct StallParams { + float aoa {0}; + float width {0}; + float peak {0}; +}; + +// position and length of a chord line +struct Chord { + float x {0}; + float y {0}; + float z {0}; + float length {0}; +}; + +enum WingFlaps { + WING_FLAP0, + WING_FLAP1, + WING_SPOILER, + WING_SLAT, +}; -// FIXME: need to handle "inverted" controls for mirrored wings. class Wing { + SGPropertyNode_ptr _wingN {nullptr}; + + struct SurfRec { + Surface* surface; + float weight; + }; + + struct WingSection { + int _id; + Chord _rootChord; + // length is distance from base to tip, not wing span + float _length {0}; + float _taper {1}; + // sweep of center line, not leading edge! + float _sweepAngleCenterLine {0}; + float _dihedral {0}; + float _twist {0}; + float _camber {0}; + float _inducedDrag {1}; + + StallParams _stallParams; + + //fixed incidence of section as given in config XML + float _sectionIncidence {0}; + float _dragScale {1}; + float _liftRatio {1}; + + FlapParams _flapParams[sizeof(WingFlaps)]; + + // calculated from above + float _orient[9]; + float _rightOrient[9]; + Chord _tipChord; + float _meanChord {0}; // std. mean chord + Chord _mac; // mean aerodynamic chord (x,y) leading edge + float _wingspan {0}; + float _aspectRatio {1}; + // all SurfRec of this wing + Vector _surfs; + // surfaces having a certain type of flap (flap, slat, spoiler) + Vector _flapSurfs[sizeof(WingFlaps)]; + + void calculateGeometry(); + void calculateWingCoordinateSystem(); + void calculateTipChord(); + void calculateSpan(); + void calculateMAC(); + float calculateSweepAngleLeadingEdge(); + //set incidence value to all surfaces of this section + void setIncidence(float incidence); + // parameters for stall curve + void setStallParams(StallParams sp) { _stallParams = sp; } + + // valid only after Wing::compile() was called + Chord getMAC() const { return _mac; }; + float getArea() const { return _wingspan*_meanChord; }; + void setDragCoefficient(float scale); + void multiplyDragCoefficient(float factor); + // The ratio of force along the Z (lift) direction of each wing + // segment to that along the X (drag) direction. + void setLiftRatio(float ratio); + void multiplyLiftRatio(float factor); + + void newSurface(Version* _version, float* pos, float* orient, float chord, + bool hasFlap0, bool hasFlap1, bool hasSlat, bool hasSpoiler, + float weight, float twist); + int numSurfaces() const { return _surfs.size(); } + Surface* getSurface(int n) { return ((SurfRec*)_surfs.get(n))->surface; } + float getSurfaceWeight(int n) const { return ((SurfRec*)_surfs.get(n))->weight; } + }; //struct WingSection + + //-- wing member variables -- + Version* _version; + bool _mirror {false}; + Vector _sections; + Chord _mac; + float _base[3] {0,0,0}; + float _tip[3] {0,0,0}; + float _wingspan {0}; + float _area {0}; + float _aspectRatio {0}; + float _meanChord {0}; + float _incidence {0}; + float _weight {0}; + + //-- private methods + Chord _float2chord(float* pos, float lenght = 0); + void _chord2float(Chord c, float* pos); + void interp(const float* v1, const float* v2, const float frac, float* out); + void writeInfoToProptree(); + public: - Wing(Version *ver, bool mirror, float* base, float chord, float length, - float taper = 1, float sweep = 0, float dihedral = 0, float twist = 0); + Wing(Version* ver, bool mirror); ~Wing(); - // Do we mirror ourselves about the XZ plane? - void setMirror(bool mirror) { _mirror = mirror; } - const bool isMirrored() { return _mirror; }; - - // Wing geometry in local coordinates: - - // base point of wing - void getBase(float* base) const { Math::set3(_base, base); }; - // dist. ALONG wing (not span!) - float getLength() const { return _length; }; - // at base, measured along X axis - float getChord() const { return _chord; }; - // fraction of chord at wing tip, 0..1 - float getTaper() const { return _taper; }; - // radians - float getSweep() const { return _sweep; }; - // radians, positive is "up" - void setDihedral(float dihedral) { _dihedral = dihedral; } - float getDihedral() const { return _dihedral; }; - - void setIncidence(float incidence); - - - // parameters for stall curve - void setStall(float aoa) { _stall = aoa; } - void setStallWidth(float angle) { _stallWidth = angle; } - void setStallPeak(float fraction) { _stallPeak = fraction; } - void setCamber(float camber) { _camber = camber; } - void setInducedDrag(float drag) { _inducedDrag = drag; } - - - void setFlap0Params(float start, float end, float lift, float drag); - void setFlap1Params(float start, float end, float lift, float drag); - void setSpoilerParams(float start, float end, float lift, float drag); - void setSlatParams(float start, float end, float aoa, float drag); + int addWingSection(float* base, float chord, float wingLength, + float taper = 1, float sweep = 0, float dihedral = 0, float twist = 0, float camber = 0, float idrag = 1, float incidence = 0); - // Set the control axes for the sub-surfaces - void setFlap0Pos(float lval, float rval); - void setFlap1Pos(float lval, float rval); - void setSpoilerPos(float lval, float rval); - void setSlatPos(float val); - void setFlap0Effectiveness(float lval); - void setFlap1Effectiveness(float lval); + static Chord calculateMAC(Chord root, Chord tip); + void setFlapParams(int section, WingFlaps type, FlapParams fp); + void setSectionDrag(int section, float pdrag); + void setSectionStallParams(int section, StallParams sp); + // Compile the thing into a bunch of Surface objects void compile(); - void getTip(float* tip) const { Math::set3(_tip, tip);}; - - // valid only after Wing::compile() was called + void multiplyLiftRatio(float factor); + void multiplyDragCoefficient(float factor); + void setIncidence(float incidence); + + bool isMirrored() const { return _mirror; }; + void getBase(float* base) const { Math::set3(_base, base); }; + void getTip(float* tip) const { Math::set3(_tip, tip); }; float getSpan() const { return _wingspan; }; - float getArea() const { return _wingspan*_meanChord; }; + float getArea() const { return _area; }; float getAspectRatio() const { return _aspectRatio; }; float getSMC() const { return _meanChord; }; - float getMAC() const { return _mac; }; // get length of MAC - float getMACx() const { return _macX; }; // get x-coord of MAC leading edge - float getMACy() const { return _base[1]+_macRootDistance; }; // get y-coord of MAC leading edge - - - int numSurfaces() const { return _surfs.size(); } - Surface* getSurface(int n) { return ((SurfRec*)_surfs.get(n))->surface; } - float getSurfaceWeight(int n) const { return ((SurfRec*)_surfs.get(n))->weight; } + float getMACLength() const { return _mac.length; }; // get length of MAC + float getMACx() const { return _mac.x; }; // get x-coord of MAC leading edge + float getMACy() const { return _mac.y; }; // get y-coord of MAC leading edge + +//----------------------------- + // propergate the control axes value for the sub-surfaces + void setFlapPos(WingFlaps type, float lval, float rval = 0); + void setFlapEffectiveness(WingFlaps f, float lval); - // The overall drag coefficient for the wing as a whole. Units are - // arbitrary. - void setDragScale(float scale); - float getDragScale() const { return _dragScale; } - - // The ratio of force along the Z (lift) direction of each wing - // segment to that along the X (drag) direction. - void setLiftRatio(float ratio); - float getLiftRatio() const { return _liftRatio; } - -private: - void interp(const float* v1, const float* v2, const float frac, float* out); - Surface* newSurface(float* pos, float* orient, float chord, - bool hasFlap0, bool hasFlap1, bool hasSlat, bool hasSpoiler); - void calculateWingCoordinateSystem(); - void calculateTip(); - void calculateSpan(); - void calculateMAC(); - void addSurface(Surface* s, float weight, float twist); - - struct SurfRec { Surface * surface; float weight; }; - - Vector _surfs; - Vector _flap0Surfs; - Vector _flap1Surfs; - Vector _slatSurfs; - Vector _spoilerSurfs; - - Version * _version; - bool _mirror {false}; - float _base[3] {0,0,0}; - float _chord {0}; - float _length {0}; - float _taper {1}; - float _sweep {0}; - float _dihedral {0}; - - // calculated from above - float _orient[9]; - float _rightOrient[9]; - float _tip[3] {0,0,0}; - float _meanChord {0}; // std. mean chord - float _mac {0}; // mean aerodynamic chord length - float _macRootDistance {0}; // y-distance of mac from root - float _macX {0}; // x-coordinate of mac (leading edge) - float _netSpan {0}; - float _wingspan {0}; - float _aspectRatio {1}; - - float _stall {0}; - float _stallWidth {0}; - float _stallPeak {0}; - float _twist {0}; - float _camber {0}; - float _incidence {0}; - float _inducedDrag {1}; - - float _dragScale {1}; - float _liftRatio {1}; - - float _flap0Start {0}; - float _flap0End {0}; - float _flap0Lift {0}; - float _flap0Drag {0}; - - float _flap1Start {0}; - float _flap1End {0}; - float _flap1Lift {0}; - float _flap1Drag {0}; - - float _spoilerStart {0}; - float _spoilerEnd {0}; - float _spoilerLift {0}; - float _spoilerDrag {0}; - - float _slatStart {0}; - float _slatEnd {0}; - float _slatAoA {0}; - float _slatDrag {0}; + void setPropertyNode(SGPropertyNode_ptr n) { _wingN = n; }; + float updateModel(Model* model); }; }; // namespace yasim diff --git a/src/FDM/YASim/proptest.cpp b/src/FDM/YASim/proptest.cpp index c32c849e3..aaeb87d6f 100644 --- a/src/FDM/YASim/proptest.cpp +++ b/src/FDM/YASim/proptest.cpp @@ -5,6 +5,7 @@ #include #include +#include "yasim-common.hpp" #include "Math.hpp" #include "FGFDM.hpp" #include "PropEngine.hpp" diff --git a/src/FDM/YASim/yasim-common.cpp b/src/FDM/YASim/yasim-common.cpp new file mode 100644 index 000000000..79273bdf8 --- /dev/null +++ b/src/FDM/YASim/yasim-common.cpp @@ -0,0 +1,5 @@ +#include "yasim-common.hpp" + +namespace yasim { + +}; //namespace yasim diff --git a/src/FDM/YASim/yasim-common.hpp b/src/FDM/YASim/yasim-common.hpp index ed1c4f018..aceb3e834 100644 --- a/src/FDM/YASim/yasim-common.hpp +++ b/src/FDM/YASim/yasim-common.hpp @@ -1,34 +1,36 @@ #ifndef _YASIM_COMMON_HPP #define _YASIM_COMMON_HPP +/* + common file for YASim wide constants and static helper functions + */ namespace yasim { - static const float YASIM_PI = 3.14159265358979323846f; - static const float PI2 = YASIM_PI*2; - static const float RAD2DEG = 180/YASIM_PI; - static const float DEG2RAD = YASIM_PI/180; - static const float RPM2RAD = YASIM_PI/30; + static const float YASIM_PI = 3.14159265358979323846f; + static const float PI2 = YASIM_PI*2; + static const float RAD2DEG = 180/YASIM_PI; + static const float DEG2RAD = YASIM_PI/180; + static const float RPM2RAD = YASIM_PI/30; - static const float KTS2MPS = 1852.0f/3600.0f; - static const float MPS2KTS = 3600.0f/1852.0f; - static const float KMH2MPS = 1/3.6f; + static const float KTS2MPS = 1852.0f/3600.0f; + static const float MPS2KTS = 3600.0f/1852.0f; + static const float KMH2MPS = 1/3.6f; - static const float FT2M = 0.3048f; - static const float M2FT = 1/FT2M; + static const float FT2M = 0.3048f; + static const float M2FT = 1/FT2M; - static const float LBS2N = 4.44822f; - static const float N2LB = 1/LBS2N; - static const float LBS2KG = 0.45359237f; - static const float KG2LBS = 1/LBS2KG; - static const float CM2GALS = 264.172037284f; - static const float HP2W = 745.700f; - static const float INHG2PA = 3386.389f; - static const float K2DEGF = 1.8f; - static const float K2DEGFOFFSET = -459.4f; - static const float CIN2CM = 1.6387064e-5f; - - static const float NM2FTLB = (1/(LBS2N*FT2M)); - static const float SLUG2KG = 14.59390f; + static const float LBS2N = 4.44822f; + static const float N2LB = 1/LBS2N; + static const float LBS2KG = 0.45359237f; + static const float KG2LBS = 1/LBS2KG; + static const float CM2GALS = 264.172037284f; + static const float HP2W = 745.700f; + static const float INHG2PA = 3386.389f; + static const float K2DEGF = 1.8f; + static const float K2DEGFOFFSET = -459.4f; + static const float CIN2CM = 1.6387064e-5f; -}; + static const float NM2FTLB = (1/(LBS2N*FT2M)); + static const float SLUG2KG = 14.59390f; +}; //namespace yasim #endif // ifndef _YASIM_COMMON_HPP diff --git a/src/FDM/YASim/yasim-test.cpp b/src/FDM/YASim/yasim-test.cpp index 8c4f69d63..4ac88869c 100644 --- a/src/FDM/YASim/yasim-test.cpp +++ b/src/FDM/YASim/yasim-test.cpp @@ -95,7 +95,7 @@ void yasim_graph(Airplane* a, const float alt, const float kts, int cfg = CONFIG ld_max= ld; ld_max_deg = deg; } - printf("%d %g %g %g\n", deg, lift, drag, ld); + printf("%d %.4g %.4g %.4g\n", deg, lift, drag, ld); } printf("# cl_max %g at %d deg\n", cl_max, cl_max_deg); printf("# cd_min %g at %d deg\n", cd_min, cd_min_deg); @@ -246,11 +246,12 @@ int main(int argc, char** argv) float SI_inertia[9]; a->getModel()->getBody()->getInertiaMatrix(SI_inertia); float MAC = 0, MACx = 0, MACy = 0; - Wing* wing = a->getWing(); - if (wing) { - MAC = a->getWing()->getMAC(); - MACx = a->getWing()->getMACx(); - MACy = a->getWing()->getMACy(); + Wing* wing {nullptr}; + if (a->hasWing()) { + wing = a->getWing(); + MAC = wing->getMACLength(); + MACx = wing->getMACx(); + MACy = wing->getMACy(); } printf(" Iterations: %d\n", a->getSolutionIterations()); printf(" Drag Coefficient: %.3f\n", drag); @@ -260,16 +261,18 @@ int main(int argc, char** argv) printf("Approach Elevator: %.3f\n\n", a->getApproachElevator()); printf(" CG: x:%.3f, y:%.3f, z:%.3f\n", cg[0], cg[1], cg[2]); if (wing) { - printf(" Wing MAC (*1): x:%.2f, y:%.2f, length:%.1f \n", MACx, MACy, MAC); - printf(" CG-x rel. MAC: %.3f\n", a->getCGMAC()); - printf(" CG-x desired: %.3f < %.3f < %.3f \n", a->getCGSoftLimitXMin(), cg[0], a->getCGSoftLimitXMax()); + printf(" Wing MAC: (x:%.2f, y:%.2f), length:%.1f \n", MACx, MACy, MAC); + printf(" hard limit CG-x: %.3f \n", a->getCGHardLimitXMax()); + printf(" soft limit CG-x: %.3f \n", a->getCGSoftLimitXMax()); + printf(" CG-x: %.3f \n", cg[0]); + printf(" CG-x rel. MAC: %3.0f%%\n", a->getCGMAC()*100); + printf(" soft limit CG-x: %.3f \n", a->getCGSoftLimitXMin()); + printf(" hard limit CG-x: %.3f \n", a->getCGHardLimitXMin()); } - printf("\nInertia tensor [kg*m^2], origo at CG:\n\n"); - printf(" %7.3f, %7.3f, %7.3f\n", SI_inertia[0], SI_inertia[1], SI_inertia[2]); - printf(" %7.3f, %7.3f, %7.3f\n", SI_inertia[3], SI_inertia[4], SI_inertia[5]); - printf(" %7.3f, %7.3f, %7.3f\n", SI_inertia[6], SI_inertia[7], SI_inertia[8]); - printf("\n(*1) MAC calculation works on only! Numbers will be wrong for segmented wings, e.g. +.\n"); + printf(" %7.0f, %7.0f, %7.0f\n", SI_inertia[0], SI_inertia[1], SI_inertia[2]); + printf(" %7.0f, %7.0f, %7.0f\n", SI_inertia[3], SI_inertia[4], SI_inertia[5]); + printf(" %7.0f, %7.0f, %7.0f\n", SI_inertia[6], SI_inertia[7], SI_inertia[8]); } delete fdm; return 0;