Merge /u/jsb1685/flightgear/ branch yasim2 into next
https://sourceforge.net/p/flightgear/flightgear/merge-requests/110/
This commit is contained in:
commit
edc1d63b26
23 changed files with 2039 additions and 1554 deletions
|
@ -60,9 +60,9 @@ Airplane::~Airplane()
|
||||||
for(i=0; i<_solveWeights.size(); i++)
|
for(i=0; i<_solveWeights.size(); i++)
|
||||||
delete (SolveWeight*)_solveWeights.get(i);
|
delete (SolveWeight*)_solveWeights.get(i);
|
||||||
for(i=0; i<_cruiseConfig.controls.size(); 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++) {
|
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)
|
if(c != &_approachElevator)
|
||||||
delete c;
|
delete c;
|
||||||
}
|
}
|
||||||
|
@ -111,15 +111,31 @@ void Airplane::getPilotAccel(float* out)
|
||||||
// FIXME: rotational & centripetal acceleration needed
|
// 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()
|
void Airplane::updateGearState()
|
||||||
{
|
{
|
||||||
for(int i=0; i<_gears.size(); i++) {
|
for(int i=0; i<_gears.size(); i++) {
|
||||||
GearRec* gr = (GearRec*)_gears.get(i);
|
GearRec* gr = (GearRec*)_gears.get(i);
|
||||||
float ext = gr->gear->getExtension();
|
float ext = gr->gear->getExtension();
|
||||||
|
|
||||||
gr->surf->setXDrag(ext);
|
gr->surf->setDragCoefficient(ext);
|
||||||
gr->surf->setYDrag(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;
|
_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;
|
_approachElevator.val = 0;
|
||||||
_approachConfig.controls.add(&_approachElevator);
|
_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();
|
ControlSetting* c = new ControlSetting();
|
||||||
c->control = control;
|
c->propHandle = getControlMap()->getInputPropertyHandle(prop);
|
||||||
c->val = val;
|
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)
|
void Airplane::addSolutionWeight(Configuration cfg, int idx, float wgt)
|
||||||
{
|
|
||||||
Control* c = new Control();
|
|
||||||
c->control = control;
|
|
||||||
c->val = val;
|
|
||||||
_cruiseConfig.controls.add(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Airplane::addSolutionWeight(bool approach, int idx, float wgt)
|
|
||||||
{
|
{
|
||||||
SolveWeight* w = new SolveWeight();
|
SolveWeight* w = new SolveWeight();
|
||||||
w->approach = approach;
|
w->approach = (cfg == APPROACH);
|
||||||
w->idx = idx;
|
w->idx = idx;
|
||||||
w->wgt = wgt;
|
w->wgt = wgt;
|
||||||
_solveWeights.add(w);
|
_solveWeights.add(w);
|
||||||
|
@ -238,9 +253,7 @@ int Airplane::addWeight(float* pos, float size)
|
||||||
WeightRec* wr = new WeightRec();
|
WeightRec* wr = new WeightRec();
|
||||||
wr->handle = _model.getBody()->addMass(0, pos);
|
wr->handle = _model.getBody()->addMass(0, pos);
|
||||||
|
|
||||||
wr->surf = new Surface(this);
|
wr->surf = new Surface(this, pos, size*size);
|
||||||
wr->surf->setPosition(pos);
|
|
||||||
wr->surf->setTotalDrag(size*size);
|
|
||||||
_model.addSurface(wr->surf);
|
_model.addSurface(wr->surf);
|
||||||
_surfs.add(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
|
// Kill the aerodynamic drag if the mass is exactly zero. This is
|
||||||
// how we simulate droppable stores.
|
// how we simulate droppable stores.
|
||||||
if(mass == 0) {
|
if(mass == 0) {
|
||||||
wr->surf->setXDrag(0);
|
wr->surf->setDragCoefficient(0);
|
||||||
wr->surf->setYDrag(0);
|
wr->surf->setYDrag(0);
|
||||||
wr->surf->setZDrag(0);
|
wr->surf->setLiftCoefficient(0);
|
||||||
} else {
|
} else {
|
||||||
wr->surf->setXDrag(1);
|
wr->surf->setDragCoefficient(1);
|
||||||
wr->surf->setYDrag(1);
|
wr->surf->setYDrag(1);
|
||||||
wr->surf->setZDrag(1);
|
wr->surf->setLiftCoefficient(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -308,49 +321,9 @@ float Airplane::compileWing(Wing* w)
|
||||||
addContactPoint(tip);
|
addContactPoint(tip);
|
||||||
tip[1] *= -1; //undo mirror
|
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 wgt = 0;
|
||||||
float dragSum = 0;
|
wgt = w->updateModel(&_model);
|
||||||
for(int i=0; i<w->numSurfaces(); 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);
|
|
||||||
}
|
|
||||||
return wgt;
|
return wgt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -371,7 +344,7 @@ float Airplane::compileFuselage(Fuselage* f)
|
||||||
float len = Math::mag3(fwd);
|
float len = Math::mag3(fwd);
|
||||||
if (len == 0) {
|
if (len == 0) {
|
||||||
_failureMsg = "Zero length fuselage";
|
_failureMsg = "Zero length fuselage";
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
float wid = f->width;
|
float wid = f->width;
|
||||||
int segs = (int)Math::ceil(len/wid);
|
int segs = (int)Math::ceil(len/wid);
|
||||||
|
@ -379,19 +352,18 @@ float Airplane::compileFuselage(Fuselage* f)
|
||||||
int j;
|
int j;
|
||||||
for(j=0; j<segs; j++) {
|
for(j=0; j<segs; j++) {
|
||||||
float frac = (j+0.5f) / segs;
|
float frac = (j+0.5f) / segs;
|
||||||
|
|
||||||
float scale = 1;
|
float scale = 1;
|
||||||
if(frac < f->mid)
|
if(frac < f->mid)
|
||||||
scale = f->taper+(1-f->taper) * (frac / f->mid);
|
scale = f->taper+(1-f->taper) * (frac / f->mid);
|
||||||
else {
|
else {
|
||||||
if( isVersionOrNewer( YASIM_VERSION_32 ) ) {
|
if( isVersionOrNewer( YASIM_VERSION_32 ) ) {
|
||||||
// Correct calculation of width for fuselage taper.
|
// Correct calculation of width for fuselage taper.
|
||||||
scale = 1 - (1-f->taper) * (frac - f->mid) / (1 - f->mid);
|
scale = 1 - (1-f->taper) * (frac - f->mid) / (1 - f->mid);
|
||||||
} else {
|
} else {
|
||||||
// Original, incorrect calculation of width for fuselage taper.
|
// Original, incorrect calculation of width for fuselage taper.
|
||||||
scale = f->taper+(1-f->taper) * (frac - f->mid) / (1 - f->mid);
|
scale = f->taper+(1-f->taper) * (frac - f->mid) / (1 - f->mid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Where are we?
|
// Where are we?
|
||||||
float pos[3];
|
float pos[3];
|
||||||
|
@ -403,44 +375,42 @@ float Airplane::compileFuselage(Fuselage* f)
|
||||||
_model.getBody()->addMass(mass, pos, true);
|
_model.getBody()->addMass(mass, pos, true);
|
||||||
wgt += mass;
|
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
|
// Make a Surface too
|
||||||
Surface* s = new Surface(this);
|
Surface* s = new Surface(this, pos, dragCoefficient);
|
||||||
s->setPosition(pos);
|
if( isVersionOrNewer( YASIM_VERSION_32 ) ) {
|
||||||
|
s->setDragCoefficient(f->_cx);
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
s->setYDrag(sideDrag*f->_cy);
|
s->setYDrag(sideDrag*f->_cy);
|
||||||
s->setZDrag(sideDrag*f->_cz);
|
s->setLiftCoefficient(sideDrag*f->_cz);
|
||||||
if( isVersionOrNewer( YASIM_VERSION_32 ) ) {
|
|
||||||
s->setTotalDrag(scale*segWgt);
|
|
||||||
} else {
|
|
||||||
s->setTotalDrag(scale*segWgt*f->_cx);
|
|
||||||
}
|
|
||||||
s->setInducedDrag(f->_idrag);
|
s->setInducedDrag(f->_idrag);
|
||||||
|
|
||||||
// FIXME: fails for fuselages aligned along the Y axis
|
// FIXME: fails for fuselages aligned along the Y axis
|
||||||
|
@ -449,8 +419,8 @@ float Airplane::compileFuselage(Fuselage* f)
|
||||||
Math::unit3(fwd, x);
|
Math::unit3(fwd, x);
|
||||||
y[0] = 0; y[1] = 1; y[2] = 0;
|
y[0] = 0; y[1] = 1; y[2] = 0;
|
||||||
Math::cross3(x, y, z);
|
Math::cross3(x, y, z);
|
||||||
Math::unit3(z, z);
|
Math::unit3(z, z);
|
||||||
Math::cross3(z, x, y);
|
Math::cross3(z, x, y);
|
||||||
s->setOrientation(o);
|
s->setOrientation(o);
|
||||||
|
|
||||||
_model.addSurface(s);
|
_model.addSurface(s);
|
||||||
|
@ -465,9 +435,6 @@ void Airplane::compileGear(GearRec* gr)
|
||||||
{
|
{
|
||||||
Gear* g = gr->gear;
|
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
|
// 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
|
// 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::mul3(0.5, cmp, cmp);
|
||||||
Math::add3(pos, cmp, pos);
|
Math::add3(pos, cmp, pos);
|
||||||
|
|
||||||
s->setPosition(pos);
|
// Make a Surface object for the aerodynamic behavior
|
||||||
s->setTotalDrag(length*length);
|
Surface* s = new Surface(this, pos, length*length);
|
||||||
|
gr->surf = s;
|
||||||
|
|
||||||
_model.addGear(g);
|
_model.addGear(g);
|
||||||
_model.addSurface(s);
|
_model.addSurface(s);
|
||||||
|
@ -543,28 +511,38 @@ void Airplane::compile()
|
||||||
// The Wing objects
|
// The Wing objects
|
||||||
if (_wing)
|
if (_wing)
|
||||||
{
|
{
|
||||||
if (baseN != 0) _wingsN = baseN->getChild("wing", 0, true);
|
if (baseN != 0) {
|
||||||
aeroWgt += compileWing(_wing);
|
_wingsN = baseN->getChild("wing", 0, true);
|
||||||
|
_wing->setPropertyNode(_wingsN);
|
||||||
// convert % to absolute x coordinates
|
}
|
||||||
_cgDesiredFront = _wing->getMACx() - _wing->getMAC()*_cgDesiredMin;
|
aeroWgt += compileWing(_wing);
|
||||||
_cgDesiredAft = _wing->getMACx() - _wing->getMAC()*_cgDesiredMax;
|
|
||||||
if (baseN != 0) {
|
// convert % to absolute x coordinates
|
||||||
SGPropertyNode_ptr n = fgGetNode("/fdm/yasim/model", true);
|
_cgDesiredFront = _wing->getMACx() - _wing->getMACLength()*_cgDesiredMin;
|
||||||
n->getNode("cg-x-range-front", true)->setFloatValue(_cgDesiredFront);
|
_cgDesiredAft = _wing->getMACx() - _wing->getMACLength()*_cgDesiredMax;
|
||||||
n->getNode("cg-x-range-aft", true)->setFloatValue(_cgDesiredAft);
|
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 (_tail)
|
||||||
{
|
{
|
||||||
if (baseN != 0) _wingsN = baseN->getChild("tail", 0, true);
|
if (baseN != 0) {
|
||||||
aeroWgt += compileWing(_tail);
|
_wingsN = baseN->getChild("tail", 0, true);
|
||||||
|
_tail->setPropertyNode(_wingsN);
|
||||||
|
}
|
||||||
|
aeroWgt += compileWing(_tail);
|
||||||
}
|
}
|
||||||
int i;
|
int i;
|
||||||
for(i=0; i<_vstabs.size(); i++)
|
for(i=0; i<_vstabs.size(); i++)
|
||||||
{
|
{
|
||||||
if (baseN != 0) _wingsN = baseN->getChild("stab", i, true);
|
Wing* vs = (Wing*)_vstabs.get(i);
|
||||||
aeroWgt += compileWing((Wing*)_vstabs.get(i));
|
if (baseN != 0) {
|
||||||
|
_wingsN = baseN->getChild("stab", i, true);
|
||||||
|
vs->setPropertyNode(_wingsN);
|
||||||
|
}
|
||||||
|
aeroWgt += compileWing(vs);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The fuselage(s)
|
// The fuselage(s)
|
||||||
|
@ -578,9 +556,13 @@ void Airplane::compile()
|
||||||
|
|
||||||
// Rescale to the specified empty weight
|
// Rescale to the specified empty weight
|
||||||
float wscale = (_emptyWeight-nonAeroWgt)/aeroWgt;
|
float wscale = (_emptyWeight-nonAeroWgt)/aeroWgt;
|
||||||
for(i=firstMass; i<body->numMasses(); i++)
|
for(i=firstMass; i<body->numMasses(); i++) {
|
||||||
body->setMass(i, body->getMass(i)*wscale);
|
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
|
// Add the thruster masses
|
||||||
for(i=0; i<_thrusters.size(); i++) {
|
for(i=0; i<_thrusters.size(); i++) {
|
||||||
ThrustRec* t = (ThrustRec*)_thrusters.get(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)
|
void Airplane::loadControls(const Vector& controls)
|
||||||
{
|
{
|
||||||
_controls.reset();
|
_controls.reset();
|
||||||
for(int i=0; i < controls.size(); i++) {
|
for(int i=0; i < controls.size(); i++) {
|
||||||
Control* c = (Control*)controls.get(i);
|
ControlSetting* c = (ControlSetting*)controls.get(i);
|
||||||
_controls.setInput(c->control, c->val);
|
_controls.setInput(c->propHandle, c->val);
|
||||||
}
|
}
|
||||||
_controls.applyControls();
|
_controls.applyControls();
|
||||||
}
|
}
|
||||||
|
@ -800,43 +782,43 @@ void Airplane::applyDragFactor(float factor)
|
||||||
float applied = Math::pow(factor, SOLVE_TWEAK);
|
float applied = Math::pow(factor, SOLVE_TWEAK);
|
||||||
_dragFactor *= applied;
|
_dragFactor *= applied;
|
||||||
if(_wing)
|
if(_wing)
|
||||||
_wing->setDragScale(_wing->getDragScale() * applied);
|
_wing->multiplyDragCoefficient(applied);
|
||||||
if(_tail)
|
if(_tail)
|
||||||
_tail->setDragScale(_tail->getDragScale() * applied);
|
_tail->multiplyDragCoefficient(applied);
|
||||||
int i;
|
int i;
|
||||||
for(i=0; i<_vstabs.size(); i++) {
|
for(i=0; i<_vstabs.size(); i++) {
|
||||||
Wing* w = (Wing*)_vstabs.get(i);
|
Wing* w = (Wing*)_vstabs.get(i);
|
||||||
w->setDragScale(w->getDragScale() * applied);
|
w->multiplyDragCoefficient(applied);
|
||||||
}
|
}
|
||||||
for(i=0; i<_fuselages.size(); i++) {
|
for(i=0; i<_fuselages.size(); i++) {
|
||||||
Fuselage* f = (Fuselage*)_fuselages.get(i);
|
Fuselage* f = (Fuselage*)_fuselages.get(i);
|
||||||
int j;
|
int j;
|
||||||
for(j=0; j<f->surfs.size(); j++) {
|
for(j=0; j<f->surfs.size(); j++) {
|
||||||
Surface* s = (Surface*)f->surfs.get(j);
|
Surface* s = (Surface*)f->surfs.get(j);
|
||||||
if( isVersionOrNewer( YASIM_VERSION_32 ) ) {
|
if( isVersionOrNewer( YASIM_VERSION_32 ) ) {
|
||||||
// For new YASim, the solver drag factor is only applied to
|
// For new YASim, the solver drag factor is only applied to
|
||||||
// the X axis for Fuselage Surfaces.
|
// the X axis for Fuselage Surfaces.
|
||||||
// The solver is tuning the coefficient for longitudinal drag,
|
// The solver is tuning the coefficient for longitudinal drag,
|
||||||
// along the direction of flight. A fuselage's lateral drag
|
// along the direction of flight. A fuselage's lateral drag
|
||||||
// is completely independent and is normally much higher;
|
// is completely independent and is normally much higher;
|
||||||
// it won't be affected by the streamlining done to reduce
|
// it won't be affected by the streamlining done to reduce
|
||||||
// longitudinal drag. So the solver should only adjust the
|
// longitudinal drag. So the solver should only adjust the
|
||||||
// fuselage's longitudinal (X axis) drag coefficient.
|
// fuselage's longitudinal (X axis) drag coefficient.
|
||||||
s->setXDrag(s->getXDrag() * applied);
|
s->setDragCoefficient(s->getDragCoefficient() * applied);
|
||||||
} else {
|
} else {
|
||||||
// Originally YASim applied the drag factor to all axes
|
// Originally YASim applied the drag factor to all axes
|
||||||
// for Fuselage Surfaces.
|
// for Fuselage Surfaces.
|
||||||
s->setTotalDrag(s->getTotalDrag() * applied);
|
s->mulTotalForceCoefficient(applied);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for(i=0; i<_weights.size(); i++) {
|
for(i=0; i<_weights.size(); i++) {
|
||||||
WeightRec* wr = (WeightRec*)_weights.get(i);
|
WeightRec* wr = (WeightRec*)_weights.get(i);
|
||||||
wr->surf->setTotalDrag(wr->surf->getTotalDrag() * applied);
|
wr->surf->mulTotalForceCoefficient(applied);
|
||||||
}
|
}
|
||||||
for(i=0; i<_gears.size(); i++) {
|
for(i=0; i<_gears.size(); i++) {
|
||||||
GearRec* gr = (GearRec*)_gears.get(i);
|
GearRec* gr = (GearRec*)_gears.get(i);
|
||||||
gr->surf->setTotalDrag(gr->surf->getTotalDrag() * applied);
|
gr->surf->mulTotalForceCoefficient(applied);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -846,13 +828,13 @@ void Airplane::applyLiftRatio(float factor)
|
||||||
float applied = Math::pow(factor, SOLVE_TWEAK);
|
float applied = Math::pow(factor, SOLVE_TWEAK);
|
||||||
_liftRatio *= applied;
|
_liftRatio *= applied;
|
||||||
if(_wing)
|
if(_wing)
|
||||||
_wing->setLiftRatio(_wing->getLiftRatio() * applied);
|
_wing->multiplyLiftRatio(applied);
|
||||||
if(_tail)
|
if(_tail)
|
||||||
_tail->setLiftRatio(_tail->getLiftRatio() * applied);
|
_tail->multiplyLiftRatio(applied);
|
||||||
int i;
|
int i;
|
||||||
for(i=0; i<_vstabs.size(); i++) {
|
for(i=0; i<_vstabs.size(); i++) {
|
||||||
Wing* w = (Wing*)_vstabs.get(i);
|
Wing* w = (Wing*)_vstabs.get(i);
|
||||||
w->setLiftRatio(w->getLiftRatio() * applied);
|
w->multiplyLiftRatio(applied);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -878,57 +860,57 @@ void Airplane::solve()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run an iteration at cruise, and extract the needed numbers:
|
// Run an iteration at cruise, and extract the needed numbers:
|
||||||
runConfig(_cruiseConfig);
|
runConfig(_cruiseConfig);
|
||||||
|
|
||||||
_model.getThrust(tmp);
|
_model.getThrust(tmp);
|
||||||
float thrust = tmp[0] + _cruiseConfig.weight * Math::sin(_cruiseConfig.glideAngle) * 9.81;
|
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);
|
_cruiseConfig.state.localToGlobal(tmp, tmp);
|
||||||
float xforce = _cruiseConfig.weight * tmp[0];
|
float xforce = _cruiseConfig.weight * tmp[0];
|
||||||
float clift0 = _cruiseConfig.weight * tmp[2];
|
float clift0 = _cruiseConfig.weight * tmp[2];
|
||||||
|
|
||||||
_model.getBody()->getAngularAccel(tmp);
|
_model.getBody()->getAngularAccel(tmp);
|
||||||
_cruiseConfig.state.localToGlobal(tmp, 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);
|
runConfig(_approachConfig);
|
||||||
|
|
||||||
_model.getBody()->getAngularAccel(tmp);
|
_model.getBody()->getAngularAccel(tmp);
|
||||||
_approachConfig.state.localToGlobal(tmp, 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);
|
_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
|
// Modify the cruise AoA a bit to get a derivative
|
||||||
_cruiseConfig.aoa += ARCMIN;
|
_cruiseConfig.aoa += ARCMIN;
|
||||||
runConfig(_cruiseConfig);
|
runConfig(_cruiseConfig);
|
||||||
_cruiseConfig.aoa -= ARCMIN;
|
_cruiseConfig.aoa -= ARCMIN;
|
||||||
|
|
||||||
_model.getBody()->getAccel(tmp);
|
_model.getBody()->getAccel(tmp);
|
||||||
_cruiseConfig.state.localToGlobal(tmp, 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
|
// Do the same with the tail incidence
|
||||||
_tail->setIncidence(_tailIncidence + ARCMIN);
|
_tail->setIncidence(_tailIncidence + ARCMIN);
|
||||||
runConfig(_cruiseConfig);
|
runConfig(_cruiseConfig);
|
||||||
_tail->setIncidence(_tailIncidence);
|
_tail->setIncidence(_tailIncidence);
|
||||||
|
|
||||||
_model.getBody()->getAngularAccel(tmp);
|
_model.getBody()->getAngularAccel(tmp);
|
||||||
_cruiseConfig.state.localToGlobal(tmp, tmp);
|
_cruiseConfig.state.localToGlobal(tmp, tmp);
|
||||||
float pitch1 = tmp[1];
|
float pitch1 = tmp[1];
|
||||||
|
|
||||||
// Now calculate:
|
// Now calculate:
|
||||||
float awgt = 9.8f * _approachConfig.weight;
|
float awgt = 9.8f * _approachConfig.weight;
|
||||||
|
|
||||||
float dragFactor = thrust / (thrust-xforce);
|
float dragFactor = thrust / (thrust-xforce);
|
||||||
float liftFactor = awgt / (awgt+alift);
|
float liftFactor = awgt / (awgt+alift);
|
||||||
float aoaDelta = -clift0 * (ARCMIN/(clift1-clift0));
|
float aoaDelta = -clift0 * (ARCMIN/(clift1-clift0));
|
||||||
float tailDelta = -pitch0 * (ARCMIN/(pitch1-pitch0));
|
float tailDelta = -pitch0 * (ARCMIN/(pitch1-pitch0));
|
||||||
|
|
||||||
// Sanity:
|
// Sanity:
|
||||||
if(dragFactor <= 0 || liftFactor <= 0)
|
if(dragFactor <= 0 || liftFactor <= 0)
|
||||||
|
@ -943,36 +925,36 @@ void Airplane::solve()
|
||||||
runConfig(_approachConfig);
|
runConfig(_approachConfig);
|
||||||
_approachElevator.val -= ELEVDIDDLE;
|
_approachElevator.val -= ELEVDIDDLE;
|
||||||
|
|
||||||
_model.getBody()->getAngularAccel(tmp);
|
_model.getBody()->getAngularAccel(tmp);
|
||||||
_approachConfig.state.localToGlobal(tmp, tmp);
|
_approachConfig.state.localToGlobal(tmp, tmp);
|
||||||
double apitch1 = tmp[1];
|
double apitch1 = tmp[1];
|
||||||
float elevDelta = -apitch0 * (ELEVDIDDLE/(apitch1-apitch0));
|
float elevDelta = -apitch0 * (ELEVDIDDLE/(apitch1-apitch0));
|
||||||
|
|
||||||
// Now apply the values we just computed. Note that the
|
// Now apply the values we just computed. Note that the
|
||||||
// "minor" variables are deferred until we get the lift/drag
|
// "minor" variables are deferred until we get the lift/drag
|
||||||
// numbers in the right ballpark.
|
// numbers in the right ballpark.
|
||||||
|
|
||||||
applyDragFactor(dragFactor);
|
applyDragFactor(dragFactor);
|
||||||
applyLiftRatio(liftFactor);
|
applyLiftRatio(liftFactor);
|
||||||
|
|
||||||
// DON'T do the following until the above are sane
|
// DON'T do the following until the above are sane
|
||||||
if(normFactor(dragFactor) > STHRESH*1.0001
|
if(normFactor(dragFactor) > STHRESH*1.0001
|
||||||
|| normFactor(liftFactor) > STHRESH*1.0001)
|
|| normFactor(liftFactor) > STHRESH*1.0001)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// OK, now we can adjust the minor variables:
|
// OK, now we can adjust the minor variables:
|
||||||
_cruiseConfig.aoa += SOLVE_TWEAK*aoaDelta;
|
_cruiseConfig.aoa += SOLVE_TWEAK*aoaDelta;
|
||||||
_tailIncidence += SOLVE_TWEAK*tailDelta;
|
_tailIncidence += SOLVE_TWEAK*tailDelta;
|
||||||
|
|
||||||
_cruiseConfig.aoa = Math::clamp(_cruiseConfig.aoa, -0.175f, 0.175f);
|
_cruiseConfig.aoa = Math::clamp(_cruiseConfig.aoa, -0.175f, 0.175f);
|
||||||
_tailIncidence = Math::clamp(_tailIncidence, -0.175f, 0.175f);
|
_tailIncidence = Math::clamp(_tailIncidence, -0.175f, 0.175f);
|
||||||
|
|
||||||
if(abs(xforce/_cruiseConfig.weight) < STHRESH*0.0001 &&
|
if(abs(xforce/_cruiseConfig.weight) < STHRESH*0.0001 &&
|
||||||
abs(alift/_approachConfig.weight) < STHRESH*0.0001 &&
|
abs(alift/_approachConfig.weight) < STHRESH*0.0001 &&
|
||||||
abs(aoaDelta) < STHRESH*.000017 &&
|
abs(aoaDelta) < STHRESH*.000017 &&
|
||||||
abs(tailDelta) < STHRESH*.000017)
|
abs(tailDelta) < STHRESH*.000017)
|
||||||
{
|
{
|
||||||
// If this finaly value is OK, then we're all done
|
// If this finaly value is OK, then we're all done
|
||||||
if(abs(elevDelta) < STHRESH*0.0001)
|
if(abs(elevDelta) < STHRESH*0.0001)
|
||||||
|
@ -988,17 +970,17 @@ void Airplane::solve()
|
||||||
}
|
}
|
||||||
|
|
||||||
if(_dragFactor < 1e-06 || _dragFactor > 1e6) {
|
if(_dragFactor < 1e-06 || _dragFactor > 1e6) {
|
||||||
_failureMsg = "Drag factor beyond reasonable bounds.";
|
_failureMsg = "Drag factor beyond reasonable bounds.";
|
||||||
return;
|
return;
|
||||||
} else if(_liftRatio < 1e-04 || _liftRatio > 1e4) {
|
} else if(_liftRatio < 1e-04 || _liftRatio > 1e4) {
|
||||||
_failureMsg = "Lift ratio beyond reasonable bounds.";
|
_failureMsg = "Lift ratio beyond reasonable bounds.";
|
||||||
return;
|
return;
|
||||||
} else if(Math::abs(_cruiseConfig.aoa) >= .17453293) {
|
} else if(Math::abs(_cruiseConfig.aoa) >= .17453293) {
|
||||||
_failureMsg = "Cruise AoA > 10 degrees";
|
_failureMsg = "Cruise AoA > 10 degrees";
|
||||||
return;
|
return;
|
||||||
} else if(Math::abs(_tailIncidence) >= .17453293) {
|
} else if(Math::abs(_tailIncidence) >= .17453293) {
|
||||||
_failureMsg = "Tail incidence > 10 degrees";
|
_failureMsg = "Tail incidence > 10 degrees";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1034,7 +1016,7 @@ float Airplane::getCGMAC()
|
||||||
if (_wing) {
|
if (_wing) {
|
||||||
float cg[3];
|
float cg[3];
|
||||||
_model.getBody()->getCG(cg);
|
_model.getBody()->getCG(cg);
|
||||||
return (_wing->getMACx() - cg[0]) / _wing->getMAC();
|
return (_wing->getMACx() - cg[0]) / _wing->getMACLength();
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,11 @@ public:
|
||||||
Airplane();
|
Airplane();
|
||||||
~Airplane();
|
~Airplane();
|
||||||
|
|
||||||
|
enum Configuration {
|
||||||
|
APPROACH,
|
||||||
|
CRUISE,
|
||||||
|
};
|
||||||
|
|
||||||
void iterate(float dt);
|
void iterate(float dt);
|
||||||
void calcFuelWeights();
|
void calcFuelWeights();
|
||||||
|
|
||||||
|
@ -37,9 +42,9 @@ public:
|
||||||
|
|
||||||
void setEmptyWeight(float weight) { _emptyWeight = weight; }
|
void setEmptyWeight(float weight) { _emptyWeight = weight; }
|
||||||
|
|
||||||
void setWing(Wing* wing) { _wing = wing; }
|
Wing* getWing();
|
||||||
Wing* getWing() { return _wing; }
|
bool hasWing() const { return (_wing != nullptr); }
|
||||||
void setTail(Wing* tail) { _tail = tail; }
|
Wing* getTail();
|
||||||
void addVStab(Wing* vstab) { _vstabs.add(vstab); }
|
void addVStab(Wing* vstab) { _vstabs.add(vstab); }
|
||||||
|
|
||||||
void addFuselage(float* front, float* back, float width,
|
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 setApproach(float speed, float altitude, float aoa, float fuel, float gla);
|
||||||
void setCruise(float speed, float altitude, float fuel, float gla);
|
void setCruise(float speed, float altitude, float fuel, float gla);
|
||||||
|
|
||||||
void setElevatorControl(int control);
|
void setElevatorControl(const char* prop);
|
||||||
void addApproachControl(int control, float val);
|
void addControlSetting(Configuration cfg, const char* prop, float val);
|
||||||
void addCruiseControl(int control, float val);
|
|
||||||
|
|
||||||
void addSolutionWeight(bool approach, int idx, float wgt);
|
void addSolutionWeight(Configuration cfg, int idx, float wgt);
|
||||||
|
|
||||||
int numGear() const { return _gears.size(); }
|
int numGear() const { return _gears.size(); }
|
||||||
Gear* getGear(int g) { return ((GearRec*)_gears.get(g))->gear; }
|
Gear* getGear(int g) { return ((GearRec*)_gears.get(g))->gear; }
|
||||||
|
@ -134,8 +138,8 @@ private:
|
||||||
float cg[3];
|
float cg[3];
|
||||||
float mass;
|
float mass;
|
||||||
};
|
};
|
||||||
struct Control {
|
struct ControlSetting {
|
||||||
int control;
|
int propHandle;
|
||||||
float val;
|
float val;
|
||||||
};
|
};
|
||||||
struct WeightRec {
|
struct WeightRec {
|
||||||
|
@ -210,7 +214,7 @@ private:
|
||||||
float _dragFactor {1};
|
float _dragFactor {1};
|
||||||
float _liftRatio {1};
|
float _liftRatio {1};
|
||||||
float _tailIncidence {0};
|
float _tailIncidence {0};
|
||||||
Control _approachElevator;
|
ControlSetting _approachElevator;
|
||||||
const char* _failureMsg {0};
|
const char* _failureMsg {0};
|
||||||
|
|
||||||
float _cgMax {-1e6}; // hard limits for cg from gear position
|
float _cgMax {-1e6}; // hard limits for cg from gear position
|
||||||
|
|
|
@ -211,7 +211,7 @@ bool Atmosphere::test() {
|
||||||
fprintf(stderr, "Columns = %d\n", numColumns);
|
fprintf(stderr, "Columns = %d\n", numColumns);
|
||||||
fprintf(stderr, "Rows = %d\n", rows);
|
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 density = calcStdDensity(data[alt][PRESSURE], data[alt][TEMPERATURE]);
|
||||||
float delta = data[alt][DENSITY] - density;
|
float delta = data[alt][DENSITY] - density;
|
||||||
fprintf(stderr, "%d : %f \n", alt, delta);
|
fprintf(stderr, "%d : %f \n", alt, delta);
|
||||||
|
|
|
@ -26,6 +26,7 @@ set(COMMON
|
||||||
Turbulence.cpp
|
Turbulence.cpp
|
||||||
Wing.cpp
|
Wing.cpp
|
||||||
Version.cpp
|
Version.cpp
|
||||||
|
yasim-common.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(SOURCES
|
set(SOURCES
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
# include "config.h"
|
# include "config.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
#include "Jet.hpp"
|
#include "Jet.hpp"
|
||||||
#include "Thruster.hpp"
|
#include "Thruster.hpp"
|
||||||
#include "PropEngine.hpp"
|
#include "PropEngine.hpp"
|
||||||
|
@ -41,18 +43,14 @@ ControlMap::~ControlMap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
input : index to _inputs
|
prop: name of input property
|
||||||
type: identifier (see enum OutputType)
|
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, float src0, float src1, float dst0, float dst1)
|
||||||
float src0, float src1, float dst0, float dst1)
|
|
||||||
{
|
{
|
||||||
addMapping(input, type, object, options);
|
MapRec* m = (MapRec*)addMapping(prop, control, id, 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);
|
|
||||||
|
|
||||||
m->src0 = src0;
|
m->src0 = src0;
|
||||||
m->src1 = src1;
|
m->src1 = src1;
|
||||||
m->dst0 = dst0;
|
m->dst0 = dst0;
|
||||||
|
@ -60,29 +58,33 @@ void ControlMap::addMapping(int input, int type, void* object, int options,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
input : index to _inputs
|
prop: name of input property
|
||||||
type: identifier (see enum OutputType)
|
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
|
// See if the output object already exists
|
||||||
OutRec* out = 0;
|
OutRec* out {nullptr};
|
||||||
int i;
|
int i;
|
||||||
for(i=0; i<_outputs.size(); i++) {
|
for(i = 0; i < _outputs.size(); i++) {
|
||||||
OutRec* o = (OutRec*)_outputs.get(i);
|
OutRec* o = (OutRec*)_outputs.get(i);
|
||||||
if(o->object == object && o->type == type) {
|
if(o->oid.object == id.object && o->oid.subObj == id.subObj
|
||||||
out = o;
|
&& o->control == control)
|
||||||
break;
|
{
|
||||||
}
|
out = o;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create one if it doesn't
|
// Create one if it doesn't
|
||||||
if(out == 0) {
|
if(out == nullptr) {
|
||||||
out = new OutRec();
|
out = new OutRec();
|
||||||
out->type = type;
|
out->control = control;
|
||||||
out->object = object;
|
out->oid = id;
|
||||||
out->oldL = out->oldR = out->time = 0;
|
_outputs.add(out);
|
||||||
_outputs.add(out);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make a new input record
|
// 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);
|
map->idx = out->maps.add(map);
|
||||||
|
|
||||||
// The default ranges differ depending on type!
|
// The default ranges differ depending on type!
|
||||||
map->src1 = map->dst1 = rangeMax(type);
|
map->src1 = map->dst1 = rangeMax(control);
|
||||||
map->src0 = map->dst0 = rangeMin(type);
|
map->src0 = map->dst0 = rangeMin(control);
|
||||||
|
|
||||||
// And add it to the approproate vectors.
|
// And add it to the approproate vectors.
|
||||||
Vector* maps = (Vector*)_inputs.get(input);
|
Vector* maps = (Vector*)_inputs.get(inputPropHandle);
|
||||||
maps->add(map);
|
maps->add(map);
|
||||||
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ControlMap::reset()
|
void ControlMap::reset()
|
||||||
{
|
{
|
||||||
// Set all the values to zero
|
// Set all the values to zero
|
||||||
for(int i=0; i<_outputs.size(); i++) {
|
for(int i = 0; i < _outputs.size(); i++) {
|
||||||
OutRec* o = (OutRec*)_outputs.get(i);
|
OutRec* o = (OutRec*)_outputs.get(i);
|
||||||
for(int j=0; j<o->maps.size(); j++)
|
for(int j = 0; j < o->maps.size(); j++) {
|
||||||
((MapRec*)(o->maps.get(j)))->val = 0;
|
((MapRec*)(o->maps.get(j)))->val = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ControlMap::setInput(int input, float val)
|
void ControlMap::setInput(int input, float val)
|
||||||
{
|
{
|
||||||
Vector* maps = (Vector*)_inputs.get(input);
|
Vector* maps = (Vector*)_inputs.get(input);
|
||||||
for(int i=0; i<maps->size(); i++) {
|
for(int i = 0; i < maps->size(); i++) {
|
||||||
MapRec* m = (MapRec*)maps->get(i);
|
MapRec* m = (MapRec*)maps->get(i);
|
||||||
|
float val2 = val;
|
||||||
float val2 = val;
|
// Do the scaling operation. Clamp to [src0:src1], rescale to
|
||||||
|
// [0:1] within that range, then map to [dst0:dst1].
|
||||||
// Do the scaling operation. Clamp to [src0:src1], rescale to
|
val2 = Math::clamp(val2, m->src0, m->src1);
|
||||||
// [0:1] within that range, then map to [dst0:dst1].
|
val2 = (val2 - m->src0) / (m->src1 - m->src0);
|
||||||
if(val2 < m->src0) val2 = m->src0;
|
m->val = m->dst0 + val2 * (m->dst1 - m->dst0);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int ControlMap::getOutputHandle(void* obj, int type)
|
int ControlMap::getOutputHandle(ObjectID id, Control control)
|
||||||
{
|
{
|
||||||
for(int i=0; i<_outputs.size(); i++) {
|
for(int i = 0; i < _outputs.size(); i++) {
|
||||||
OutRec* o = (OutRec*)_outputs.get(i);
|
OutRec* o = (OutRec*)_outputs.get(i);
|
||||||
if(o->object == obj && o->type == type)
|
if(o->oid.object == id.object && o->oid.subObj == id.subObj
|
||||||
return i;
|
&& o->control == control)
|
||||||
|
return i;
|
||||||
}
|
}
|
||||||
return 0;
|
fprintf(stderr, "ControlMap::getOutputHandle cannot find *%ld, control %d \nMissing <control-input ...> in XML?!", (long)id.object, control);
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ControlMap::setTransitionTime(int handle, float time)
|
void ControlMap::setTransitionTime(int handle, float time)
|
||||||
|
@ -157,27 +158,28 @@ float ControlMap::getOutputR(int handle)
|
||||||
void ControlMap::applyControls(float dt)
|
void ControlMap::applyControls(float dt)
|
||||||
{
|
{
|
||||||
int outrec;
|
int outrec;
|
||||||
for(outrec=0; outrec<_outputs.size(); outrec++) {
|
for(outrec=0; outrec<_outputs.size(); outrec++)
|
||||||
OutRec* o = (OutRec*)_outputs.get(outrec);
|
{
|
||||||
|
OutRec* o = (OutRec*)_outputs.get(outrec);
|
||||||
// Generate a summed value. Note the check for "split"
|
|
||||||
// control axes like ailerons.
|
// Generate a summed value. Note the check for "split"
|
||||||
float lval = 0, rval = 0;
|
// control axes like ailerons.
|
||||||
int i;
|
float lval = 0, rval = 0;
|
||||||
for(i=0; i<o->maps.size(); i++) {
|
int i;
|
||||||
MapRec* m = (MapRec*)o->maps.get(i);
|
for(i=0; i<o->maps.size(); i++) {
|
||||||
float val = m->val;
|
MapRec* m = (MapRec*)o->maps.get(i);
|
||||||
|
float val = m->val;
|
||||||
|
|
||||||
if(m->opt & OPT_SQUARE)
|
if(m->opt & OPT_SQUARE)
|
||||||
val = val * Math::abs(val);
|
val = val * Math::abs(val);
|
||||||
if(m->opt & OPT_INVERT)
|
if(m->opt & OPT_INVERT)
|
||||||
val = -val;
|
val = -val;
|
||||||
lval += val;
|
lval += val;
|
||||||
if(m->opt & OPT_SPLIT)
|
if(m->opt & OPT_SPLIT)
|
||||||
rval -= val;
|
rval -= val;
|
||||||
else
|
else
|
||||||
rval += val;
|
rval += val;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there is a finite transition time, clamp the values to
|
// If there is a finite transition time, clamp the values to
|
||||||
// the maximum travel allowed in this dt.
|
// the maximum travel allowed in this dt.
|
||||||
|
@ -187,7 +189,7 @@ void ControlMap::applyControls(float dt)
|
||||||
float adl = Math::abs(dl);
|
float adl = Math::abs(dl);
|
||||||
float adr = Math::abs(dr);
|
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(adl > max) dl = dl*max/adl;
|
||||||
if(adr > max) dr = dr*max/adr;
|
if(adr > max) dr = dr*max/adr;
|
||||||
|
|
||||||
|
@ -198,133 +200,250 @@ void ControlMap::applyControls(float dt)
|
||||||
o->oldL = lval;
|
o->oldL = lval;
|
||||||
o->oldR = rval;
|
o->oldR = rval;
|
||||||
|
|
||||||
void* obj = o->object;
|
void* obj = o->oid.object;
|
||||||
switch(o->type) {
|
switch(o->control) {
|
||||||
case THROTTLE: ((Thruster*)obj)->setThrottle(lval); break;
|
case THROTTLE:
|
||||||
case MIXTURE: ((Thruster*)obj)->setMixture(lval); break;
|
((Thruster*)obj)->setThrottle(lval);
|
||||||
case CONDLEVER: ((TurbineEngine*)((PropEngine*)
|
break;
|
||||||
obj)->getEngine())->setCondLever(lval); break;
|
case MIXTURE:
|
||||||
case STARTER: ((Thruster*)obj)->setStarter(lval != 0.0); break;
|
((Thruster*)obj)->setMixture(lval);
|
||||||
case MAGNETOS: ((PropEngine*)obj)->setMagnetos((int)lval); break;
|
break;
|
||||||
case ADVANCE: ((PropEngine*)obj)->setAdvance(lval); break;
|
case CONDLEVER:
|
||||||
case PROPPITCH: ((PropEngine*)obj)->setPropPitch(lval); break;
|
((TurbineEngine*)((PropEngine*)obj)->getEngine())->setCondLever(lval);
|
||||||
case PROPFEATHER: ((PropEngine*)obj)->setPropFeather((int)lval); break;
|
break;
|
||||||
case REHEAT: ((Jet*)obj)->setReheat(lval); break;
|
case STARTER:
|
||||||
case VECTOR: ((Jet*)obj)->setRotation(lval); break;
|
((Thruster*)obj)->setStarter(lval != 0.0);
|
||||||
case BRAKE: ((Gear*)obj)->setBrake(lval); break;
|
break;
|
||||||
case STEER: ((Gear*)obj)->setRotation(lval); break;
|
case MAGNETOS:
|
||||||
case EXTEND: ((Gear*)obj)->setExtension(lval); break;
|
((PropEngine*)obj)->setMagnetos((int)lval);
|
||||||
case HEXTEND: ((Hook*)obj)->setExtension(lval); break;
|
break;
|
||||||
case LEXTEND: ((Launchbar*)obj)->setExtension(lval); break;
|
case ADVANCE:
|
||||||
case LACCEL: ((Launchbar*)obj)->setAcceleration(lval); break;
|
((PropEngine*)obj)->setAdvance(lval);
|
||||||
case CASTERING:((Gear*)obj)->setCastering(lval != 0); break;
|
break;
|
||||||
case SLAT: ((Wing*)obj)->setSlatPos(lval); break;
|
case PROPPITCH:
|
||||||
case FLAP0: ((Wing*)obj)->setFlap0Pos(lval, rval); break;
|
((PropEngine*)obj)->setPropPitch(lval);
|
||||||
case FLAP0EFFECTIVENESS: ((Wing*)obj)->setFlap0Effectiveness(lval); break;
|
break;
|
||||||
case FLAP1: ((Wing*)obj)->setFlap1Pos(lval, rval); break;
|
case PROPFEATHER:
|
||||||
case FLAP1EFFECTIVENESS: ((Wing*)obj)->setFlap1Effectiveness(lval); break;
|
((PropEngine*)obj)->setPropFeather((int)lval);
|
||||||
case SPOILER: ((Wing*)obj)->setSpoilerPos(lval, rval); break;
|
break;
|
||||||
case COLLECTIVE: ((Rotor*)obj)->setCollective(lval); break;
|
case REHEAT:
|
||||||
case CYCLICAIL: ((Rotor*)obj)->setCyclicail(lval,rval); break;
|
((Jet*)obj)->setReheat(lval);
|
||||||
case CYCLICELE: ((Rotor*)obj)->setCyclicele(lval,rval); break;
|
break;
|
||||||
case TILTPITCH: ((Rotor*)obj)->setTiltPitch(lval); break;
|
case VECTOR:
|
||||||
case TILTYAW: ((Rotor*)obj)->setTiltYaw(lval); break;
|
((Jet*)obj)->setRotation(lval);
|
||||||
case TILTROLL: ((Rotor*)obj)->setTiltRoll(lval); break;
|
break;
|
||||||
case ROTORBALANCE:
|
case BRAKE:
|
||||||
((Rotor*)obj)->setRotorBalance(lval); break;
|
((Gear*)obj)->setBrake(lval);
|
||||||
case ROTORBRAKE: ((Rotorgear*)obj)->setRotorBrake(lval); break;
|
break;
|
||||||
case ROTORENGINEON:
|
case STEER:
|
||||||
((Rotorgear*)obj)->setEngineOn((int)lval); break;
|
((Gear*)obj)->setRotation(lval);
|
||||||
case ROTORENGINEMAXRELTORQUE:
|
break;
|
||||||
((Rotorgear*)obj)->setRotorEngineMaxRelTorque(lval); break;
|
case EXTEND:
|
||||||
case ROTORRELTARGET:
|
((Gear*)obj)->setExtension(lval);
|
||||||
((Rotorgear*)obj)->setRotorRelTarget(lval); break;
|
break;
|
||||||
case REVERSE_THRUST: ((Jet*)obj)->setReverse(lval != 0); break;
|
case HEXTEND:
|
||||||
case BOOST:
|
((Hook*)obj)->setExtension(lval);
|
||||||
((PistonEngine*)((Thruster*)obj)->getEngine())->setBoost(lval);
|
break;
|
||||||
break;
|
case LEXTEND:
|
||||||
case WASTEGATE:
|
((Launchbar*)obj)->setExtension(lval);
|
||||||
((PistonEngine*)((Thruster*)obj)->getEngine())->setWastegate(lval);
|
break;
|
||||||
break;
|
case LACCEL:
|
||||||
case WINCHRELSPEED: ((Hitch*)obj)->setWinchRelSpeed(lval); break;
|
((Launchbar*)obj)->setAcceleration(lval);
|
||||||
case HITCHOPEN: ((Hitch*)obj)->setOpen(lval!=0); break;
|
break;
|
||||||
case PLACEWINCH: ((Hitch*)obj)->setWinchPositionAuto(lval!=0); break;
|
case CASTERING:
|
||||||
case FINDAITOW: ((Hitch*)obj)->findBestAIObject(lval!=0); break;
|
((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
|
// The minimum of the range for each type of control
|
||||||
switch(type) {
|
switch(control) {
|
||||||
case FLAP0: return -1; // [-1:1]
|
case FLAP0: return -1; // [-1:1]
|
||||||
case FLAP1: return -1;
|
case FLAP1: return -1;
|
||||||
case STEER: return -1;
|
case STEER: return -1;
|
||||||
case CYCLICELE: return -1;
|
case CYCLICELE: return -1;
|
||||||
case CYCLICAIL: return -1;
|
case CYCLICAIL: return -1;
|
||||||
case COLLECTIVE: return -1;
|
case COLLECTIVE: return -1;
|
||||||
case WINCHRELSPEED: return -1;
|
case WINCHRELSPEED: return -1;
|
||||||
case MAGNETOS: return 0; // [0:3]
|
case MAGNETOS: return 0; // [0:3]
|
||||||
case FLAP0EFFECTIVENESS: return 1; // [0:10]
|
case FLAP0EFFECTIVENESS: return 1; // [0:10]
|
||||||
case FLAP1EFFECTIVENESS: return 1; // [0:10]
|
case FLAP1EFFECTIVENESS: return 1; // [0:10]
|
||||||
default: return 0; // [0:1]
|
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
|
// The maximum of the range for each type of control
|
||||||
switch(type) {
|
switch(control) {
|
||||||
case FLAP0: return 1; // [-1:1]
|
case FLAP0: return 1; // [-1:1]
|
||||||
case FLAP1: return 1;
|
case FLAP1: return 1;
|
||||||
case STEER: return 1;
|
case STEER: return 1;
|
||||||
case MAGNETOS: return 3; // [0:3]
|
case MAGNETOS: return 3; // [0:3]
|
||||||
case FLAP0EFFECTIVENESS: return 10;// [0:10]
|
case FLAP0EFFECTIVENESS: return 10;// [0:10]
|
||||||
case FLAP1EFFECTIVENESS: return 10;// [0:10]
|
case FLAP1EFFECTIVENESS: return 10;// [0:10]
|
||||||
default: return 1; // [0:1]
|
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)
|
/// 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++) {
|
for(int i=0; i < _properties.size(); i++) {
|
||||||
PropHandle* p = (PropHandle*)_properties.get(i);
|
PropHandle* p = (PropHandle*)_properties.get(i);
|
||||||
if(eq(p->name, name))
|
if(!strcmp(p->name, name))
|
||||||
return p->handle;
|
return p->handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
// create new
|
// create new
|
||||||
PropHandle* p = new PropHandle();
|
PropHandle* p = new PropHandle();
|
||||||
p->name = dup(name);
|
p->name = strdup(name);
|
||||||
|
|
||||||
fgGetNode(p->name, true);
|
fgGetNode(p->name, true);
|
||||||
|
|
||||||
Vector* v = new Vector();
|
Vector* v = new Vector();
|
||||||
p->handle = _inputs.add(v);
|
p->handle = _inputs.add(v);
|
||||||
_properties.add(p);
|
_properties.add(p);
|
||||||
return p->handle;
|
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
|
} // namespace yasim
|
||||||
|
|
|
@ -9,42 +9,84 @@ namespace yasim {
|
||||||
class ControlMap {
|
class ControlMap {
|
||||||
public:
|
public:
|
||||||
~ControlMap();
|
~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,
|
enum {
|
||||||
ADVANCE, REHEAT, PROP,
|
OPT_SPLIT = 0x01,
|
||||||
BRAKE, STEER, EXTEND, HEXTEND, LEXTEND, LACCEL,
|
OPT_INVERT = 0x02,
|
||||||
INCIDENCE, FLAP0, FLAP1, SLAT, SPOILER, VECTOR,
|
OPT_SQUARE = 0x04
|
||||||
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,
|
struct PropHandle {
|
||||||
OPT_INVERT = 0x02,
|
char* name {nullptr};
|
||||||
OPT_SQUARE = 0x04 };
|
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; };
|
// same with limits. Input values are clamped to [src0:src1] and then mapped to
|
||||||
|
// [dst0:dst1] before being set on the objects control.
|
||||||
// Adds a mapping to between input handle and a particular setting
|
void addMapping(const char* prop, Control control, ObjectID id, int options, float src0, float src1, float dst0, float dst1);
|
||||||
// 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);
|
|
||||||
|
|
||||||
// Resets our accumulated input values. Call before any
|
// Resets our accumulated input values. Call before any
|
||||||
// setInput() invokations.
|
// setInput() invokations.
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
// Sets the specified input (as returned by propertyHandle) to the
|
// Sets the specified input (as returned by getPropertyHandle()) to the
|
||||||
// specified value.
|
// specified value.
|
||||||
void setInput(int propHandle, float value);
|
void setInput(int propHandle, float value);
|
||||||
|
|
||||||
|
@ -55,12 +97,12 @@ public:
|
||||||
// Returns the input/output range appropriate for the given
|
// Returns the input/output range appropriate for the given
|
||||||
// control. Ailerons go from -1 to 1, while throttles are never
|
// control. Ailerons go from -1 to 1, while throttles are never
|
||||||
// lower than zero, etc...
|
// lower than zero, etc...
|
||||||
static float rangeMin(int type);
|
static float rangeMin(Control control);
|
||||||
static float rangeMax(int type);
|
static float rangeMax(Control control);
|
||||||
|
|
||||||
// Each output record is identified by both an object/type tuple
|
// Each output record is identified by both an object/type tuple
|
||||||
// and a numeric handle.
|
// 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
|
// Sets the transition time for the control output to swing
|
||||||
// through its full range.
|
// through its full range.
|
||||||
|
@ -73,19 +115,29 @@ public:
|
||||||
float getOutputR(int handle);
|
float getOutputR(int handle);
|
||||||
|
|
||||||
// register property name, return handle
|
// register property name, return handle
|
||||||
int propertyHandle(const char* name);
|
int getInputPropertyHandle(const char* name);
|
||||||
int numProperties() { return _properties.size(); }
|
int numProperties() { return _properties.size(); }
|
||||||
PropHandle* getProperty(const int i) { return ((PropHandle*)_properties.get(i)); }
|
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:
|
private:
|
||||||
struct OutRec { int type; void* object; Vector maps;
|
struct OutRec {
|
||||||
float oldL, oldR, time; };
|
Control control;
|
||||||
struct MapRec { OutRec* out; int idx; int opt; float val;
|
ObjectID oid;
|
||||||
float src0; float src1; float dst0; float dst1; };
|
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
|
// A list of (sub)Vectors containing a bunch of MapRec objects for
|
||||||
// each input handle.
|
// each input handle.
|
||||||
|
@ -95,6 +147,8 @@ private:
|
||||||
Vector _outputs;
|
Vector _outputs;
|
||||||
// control properties
|
// control properties
|
||||||
Vector _properties;
|
Vector _properties;
|
||||||
|
|
||||||
|
void* addMapping(const char* prop, Control control, ObjectID id, int options = 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
}; // namespace yasim
|
}; // namespace yasim
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -4,7 +4,6 @@
|
||||||
#include <simgear/xml/easyxml.hxx>
|
#include <simgear/xml/easyxml.hxx>
|
||||||
#include <simgear/props/props.hxx>
|
#include <simgear/props/props.hxx>
|
||||||
|
|
||||||
#include "yasim-common.hpp"
|
|
||||||
#include "Airplane.hpp"
|
#include "Airplane.hpp"
|
||||||
#include "Vector.hpp"
|
#include "Vector.hpp"
|
||||||
|
|
||||||
|
@ -32,30 +31,68 @@ public:
|
||||||
float getVehicleRadius(void) const { return _vehicle_radius; }
|
float getVehicleRadius(void) const { return _vehicle_radius; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct EngRec { char* prefix; Thruster* eng; };
|
struct EngRec {
|
||||||
struct WeightRec { char* prop; float size; int handle; };
|
char* prefix {nullptr};
|
||||||
struct PropOut { SGPropertyNode* prop; int handle, type; bool left;
|
Thruster* eng {nullptr};
|
||||||
float min, max; };
|
};
|
||||||
|
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);
|
void setOutputProperties(float dt);
|
||||||
|
|
||||||
Rotor* parseRotor(XMLAttributes* a, const char* name);
|
void parseRotor(const XMLAttributes* a, const char* name);
|
||||||
Wing* parseWing(XMLAttributes* a, const char* name, Version * version);
|
void parseRotorGear(const XMLAttributes* a);
|
||||||
|
void parseWing(const XMLAttributes* a, const char* name, Airplane* airplane);
|
||||||
int parseOutput(const char* name);
|
int parseOutput(const char* name);
|
||||||
void parseWeight(XMLAttributes* a);
|
void parseWeight(const XMLAttributes* a);
|
||||||
void parseTurbineEngine(XMLAttributes* a);
|
void parseStall(const XMLAttributes* a);
|
||||||
void parsePistonEngine(XMLAttributes* a);
|
void parseFlap(const XMLAttributes* a, const char* name);
|
||||||
void parsePropeller(XMLAttributes* a);
|
|
||||||
bool eq(const char* a, const char* b);
|
void parseTurbineEngine(const XMLAttributes* a);
|
||||||
bool caseeq(const char* a, const char* b);
|
void parsePistonEngine(const XMLAttributes* a);
|
||||||
char* dup(const char* s);
|
void parsePropeller(const XMLAttributes* a);
|
||||||
int attri(XMLAttributes* atts, const char* attr);
|
void parseThruster(const XMLAttributes* a);
|
||||||
int attri(XMLAttributes* atts, const char* attr, int def);
|
void parseJet(const XMLAttributes* a);
|
||||||
float attrf(XMLAttributes* atts, const char* attr);
|
void parseHitch(const XMLAttributes* a);
|
||||||
float attrf(XMLAttributes* atts, const char* attr, float def);
|
void parseTow(const XMLAttributes* a);
|
||||||
double attrd(XMLAttributes* atts, const char* attr);
|
void parseWinch(const XMLAttributes* a);
|
||||||
double attrd(XMLAttributes* atts, const char* attr, double def);
|
void parseGear(const XMLAttributes* a);
|
||||||
bool attrb(XMLAttributes* atts, const char* attr);
|
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.
|
// The core Airplane object we manage.
|
||||||
Airplane _airplane;
|
Airplane _airplane;
|
||||||
|
@ -73,12 +110,13 @@ private:
|
||||||
Vector _controlProps;
|
Vector _controlProps;
|
||||||
|
|
||||||
// Radius of the vehicle, for intersection testing.
|
// Radius of the vehicle, for intersection testing.
|
||||||
float _vehicle_radius;
|
float _vehicle_radius {0};
|
||||||
|
|
||||||
// Parsing temporaries
|
// Parsing temporaries
|
||||||
void* _currObj;
|
void* _currObj {nullptr};
|
||||||
bool _cruiseCurr;
|
Airplane::Configuration _airplaneCfg;
|
||||||
int _nextEngine;
|
int _nextEngine {0};
|
||||||
|
int _wingSection {0};
|
||||||
|
|
||||||
class FuelProps
|
class FuelProps
|
||||||
{
|
{
|
||||||
|
|
|
@ -50,6 +50,8 @@ public:
|
||||||
out[2] = v[2];
|
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) {
|
static inline float dot3(const float* a, const float* b) {
|
||||||
return a[0]*b[0] + a[1]*b[1] + a[2]*b[2];
|
return a[0]*b[0] + a[1]*b[1] + a[2]*b[2];
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,6 +61,12 @@ Model::Model()
|
||||||
_fAeroXN = _modelN->getNode("f-x-drag", true);
|
_fAeroXN = _modelN->getNode("f-x-drag", true);
|
||||||
_fAeroYN = _modelN->getNode("f-y-side", true);
|
_fAeroYN = _modelN->getNode("f-y-side", true);
|
||||||
_fAeroZN = _modelN->getNode("f-z-lift", 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);
|
_gefxN = fgGetNode("/fdm/yasim/debug/ground-effect/ge-f-x", true);
|
||||||
_gefyN = fgGetNode("/fdm/yasim/debug/ground-effect/ge-f-y", 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
|
// Do each surface, remembering that the local velocity at each
|
||||||
// point is different due to rotation.
|
// point is different due to rotation.
|
||||||
float faero[3];
|
float faero[3] {0,0,0};
|
||||||
faero[0] = faero[1] = faero[2] = 0;
|
|
||||||
for(i=0; i<_surfaces.size(); i++) {
|
for(i=0; i<_surfaces.size(); i++) {
|
||||||
Surface* sf = (Surface*)_surfaces.get(i);
|
Surface* sf = (Surface*)_surfaces.get(i);
|
||||||
|
|
||||||
|
@ -374,6 +379,12 @@ void Model::calcForces(State* s)
|
||||||
_fAeroXN->setFloatValue(faero[0]);
|
_fAeroXN->setFloatValue(faero[0]);
|
||||||
_fAeroYN->setFloatValue(faero[1]);
|
_fAeroYN->setFloatValue(faero[1]);
|
||||||
_fAeroZN->setFloatValue(faero[2]);
|
_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
|
// Convert the velocity and rotation vectors to local coordinates
|
||||||
float lrot[3], lv[3];
|
float lrot[3], lv[3];
|
||||||
|
@ -382,18 +393,18 @@ void Model::calcForces(State* s)
|
||||||
|
|
||||||
// The landing gear
|
// The landing gear
|
||||||
for(i=0; i<_gears.size(); i++) {
|
for(i=0; i<_gears.size(); i++) {
|
||||||
float force[3], contact[3];
|
float force[3], contact[3];
|
||||||
Gear* g = (Gear*)_gears.get(i);
|
Gear* g = (Gear*)_gears.get(i);
|
||||||
|
|
||||||
g->calcForce(&_body, s, lv, lrot);
|
g->calcForce(&_body, s, lv, lrot);
|
||||||
g->getForce(force, contact);
|
g->getForce(force, contact);
|
||||||
_body.addForce(contact, force);
|
_body.addForce(contact, force);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The arrester hook
|
// The arrester hook
|
||||||
if(_hook) {
|
if(_hook) {
|
||||||
_hook->calcForce(_ground_cb, &_body, s, lv, lrot);
|
_hook->calcForce(_ground_cb, &_body, s, lv, lrot);
|
||||||
float force[3], contact[3];
|
float force[3], contact[3];
|
||||||
_hook->getForce(force, contact);
|
_hook->getForce(force, contact);
|
||||||
_body.addForce(contact, force);
|
_body.addForce(contact, force);
|
||||||
}
|
}
|
||||||
|
@ -401,13 +412,13 @@ void Model::calcForces(State* s)
|
||||||
// The launchbar/holdback
|
// The launchbar/holdback
|
||||||
if(_launchbar) {
|
if(_launchbar) {
|
||||||
_launchbar->calcForce(_ground_cb, &_body, s, lv, lrot);
|
_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);
|
_launchbar->getForce(forcelb, contactlb, forcehb, contacthb);
|
||||||
_body.addForce(contactlb, forcelb);
|
_body.addForce(contactlb, forcelb);
|
||||||
_body.addForce(contacthb, forcehb);
|
_body.addForce(contacthb, forcehb);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The hitches
|
// The hitches
|
||||||
for(i=0; i<_hitches.size(); i++) {
|
for(i=0; i<_hitches.size(); i++) {
|
||||||
float force[3], contact[3];
|
float force[3], contact[3];
|
||||||
Hitch* h = (Hitch*)_hitches.get(i);
|
Hitch* h = (Hitch*)_hitches.get(i);
|
||||||
|
|
|
@ -118,6 +118,12 @@ private:
|
||||||
SGPropertyNode_ptr _fAeroXN;
|
SGPropertyNode_ptr _fAeroXN;
|
||||||
SGPropertyNode_ptr _fAeroYN;
|
SGPropertyNode_ptr _fAeroYN;
|
||||||
SGPropertyNode_ptr _fAeroZN;
|
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 _gefxN;
|
||||||
SGPropertyNode_ptr _gefyN;
|
SGPropertyNode_ptr _gefyN;
|
||||||
SGPropertyNode_ptr _gefzN;
|
SGPropertyNode_ptr _gefzN;
|
||||||
|
|
|
@ -69,9 +69,7 @@ void RigidBody::setMass(int handle, float mass, const float* pos, bool isStatic)
|
||||||
|
|
||||||
void RigidBody::getMassPosition(int handle, float* out) const
|
void RigidBody::getMassPosition(int handle, float* out) const
|
||||||
{
|
{
|
||||||
out[0] = _masses[handle].p[0];
|
Math::set3(_masses[handle].p, out);
|
||||||
out[1] = _masses[handle].p[1];
|
|
||||||
out[2] = _masses[handle].p[2];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calcualtes the rotational velocity of a particular point. All
|
// 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()
|
void RigidBody::_recalcStatic()
|
||||||
{
|
{
|
||||||
// aggregate all masses that do not change (e.g. fuselage, wings) into one point mass
|
// aggregate all masses that do not change (e.g. fuselage, wings) into one point mass
|
||||||
_staticMass.m = 0;
|
_staticMass.m = 0;
|
||||||
_staticMass.p[0] = 0;
|
Math::zero3(_staticMass.p);
|
||||||
_staticMass.p[1] = 0;
|
int i;
|
||||||
_staticMass.p[2] = 0;
|
int s = 0;
|
||||||
int i;
|
for(i=0; i<_nMasses; i++) {
|
||||||
int s = 0;
|
if (_masses[i].isStatic) {
|
||||||
for(i=0; i<_nMasses; i++) {
|
s++;
|
||||||
if (_masses[i].isStatic) {
|
float mass = _masses[i].m;
|
||||||
s++;
|
_staticMass.m += mass;
|
||||||
float m = _masses[i].m;
|
float momentum[3];
|
||||||
_staticMass.m += m;
|
Math::mul3(mass, _masses[i].p, momentum);
|
||||||
_staticMass.p[0] += m * _masses[i].p[0];
|
Math::add3(momentum, _staticMass.p, _staticMass.p);
|
||||||
_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) {
|
||||||
Math::mul3(1/_staticMass.m, _staticMass.p, _staticMass.p);
|
_bodyN->getNode("aggregated-mass", true)->setFloatValue(_staticMass.m);
|
||||||
if (_bodyN != 0) {
|
_bodyN->getNode("aggregated-count", true)->setIntValue(s);
|
||||||
_bodyN->getNode("aggregated-mass", true)->setFloatValue(_staticMass.m);
|
_bodyN->getNode("aggregated-pos-x", true)->setFloatValue(_staticMass.p[0]);
|
||||||
_bodyN->getNode("aggregated-count", true)->setIntValue(s);
|
_bodyN->getNode("aggregated-pos-y", true)->setFloatValue(_staticMass.p[1]);
|
||||||
_bodyN->getNode("aggregated-pos-x", true)->setFloatValue(_staticMass.p[0]);
|
_bodyN->getNode("aggregated-pos-z", true)->setFloatValue(_staticMass.p[2]);
|
||||||
_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++)
|
||||||
// Now the inertia tensor:
|
_tI_static[i] = 0;
|
||||||
for(i=0; i<9; i++)
|
|
||||||
_tI_static[i] = 0;
|
|
||||||
|
|
||||||
for(i=0; i<_nMasses; i++) {
|
for(i=0; i<_nMasses; i++) {
|
||||||
if (_masses[i].isStatic) {
|
if (_masses[i].isStatic) {
|
||||||
float m = _masses[i].m;
|
float m = _masses[i].m;
|
||||||
|
|
||||||
float x = _masses[i].p[0] - _staticMass.p[0];
|
float x = _masses[i].p[0] - _staticMass.p[0];
|
||||||
float y = _masses[i].p[1] - _staticMass.p[1];
|
float y = _masses[i].p[1] - _staticMass.p[1];
|
||||||
float z = _masses[i].p[2] - _staticMass.p[2];
|
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 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 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
|
// 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[0] += y2+z2; _tI_static[1] -= xy; _tI_static[2] -= zx;
|
||||||
_tI_static[4] += x2+z2; _tI_static[5] -= yz;
|
_tI_static[4] += x2+z2; _tI_static[5] -= yz;
|
||||||
_tI_static[8] += x2+y2;
|
_tI_static[8] += x2+y2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// copy symmetric elements
|
// copy symmetric elements
|
||||||
_tI_static[3] = _tI_static[1];
|
_tI_static[3] = _tI_static[1];
|
||||||
_tI_static[6] = _tI_static[2];
|
_tI_static[6] = _tI_static[2];
|
||||||
_tI_static[7] = _tI_static[5];
|
_tI_static[7] = _tI_static[5];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// calculate the total mass, centre of gravity and inertia tensor
|
/// calculate the total mass, centre of gravity and inertia tensor
|
||||||
|
@ -151,44 +147,42 @@ void RigidBody::recalc()
|
||||||
// Calculate the c.g and total mass
|
// Calculate the c.g and total mass
|
||||||
// init with pre-calculated static mass
|
// init with pre-calculated static mass
|
||||||
_totalMass = _staticMass.m;
|
_totalMass = _staticMass.m;
|
||||||
_cg[0] = _staticMass.m * _staticMass.p[0];
|
Math::mul3(_staticMass.m, _staticMass.p, _cg);
|
||||||
_cg[1] = _staticMass.m * _staticMass.p[1];
|
|
||||||
_cg[2] = _staticMass.m * _staticMass.p[2];
|
|
||||||
int i;
|
int i;
|
||||||
for(i=0; i<_nMasses; i++) {
|
for(i=0; i<_nMasses; i++) {
|
||||||
// only masses we did not aggregate
|
// only masses we did not aggregate
|
||||||
if (!_masses[i].isStatic) {
|
if (!_masses[i].isStatic) {
|
||||||
float m = _masses[i].m;
|
float mass = _masses[i].m;
|
||||||
_totalMass += m;
|
_totalMass += mass;
|
||||||
_cg[0] += m * _masses[i].p[0];
|
float momentum[3];
|
||||||
_cg[1] += m * _masses[i].p[1];
|
Math::mul3(mass, _masses[i].p, momentum);
|
||||||
_cg[2] += m * _masses[i].p[2];
|
Math::add3(momentum, _cg, _cg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Math::mul3(1/_totalMass, _cg, _cg);
|
Math::mul3(1/_totalMass, _cg, _cg);
|
||||||
|
|
||||||
// Now the inertia tensor:
|
// Now the inertia tensor:
|
||||||
for(i=0; i<9; i++)
|
for(i=0; i<9; i++)
|
||||||
_tI[i] = _tI_static[i];
|
_tI[i] = _tI_static[i];
|
||||||
|
|
||||||
for(i=0; i<_nMasses; i++) {
|
for(i=0; i<_nMasses; i++) {
|
||||||
if (!_masses[i].isStatic) {
|
if (!_masses[i].isStatic) {
|
||||||
float m = _masses[i].m;
|
float m = _masses[i].m;
|
||||||
|
|
||||||
float x = _masses[i].p[0] - _cg[0];
|
float x = _masses[i].p[0] - _cg[0];
|
||||||
float y = _masses[i].p[1] - _cg[1];
|
float y = _masses[i].p[1] - _cg[1];
|
||||||
float z = _masses[i].p[2] - _cg[2];
|
float z = _masses[i].p[2] - _cg[2];
|
||||||
float mx = m*x;
|
float mx = m*x;
|
||||||
float my = m*y;
|
float my = m*y;
|
||||||
float mz = m*z;
|
float mz = m*z;
|
||||||
|
|
||||||
float xy = mx*y; float yz = my*z; float zx = mz*x;
|
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 x2 = mx*x; float y2 = my*y; float z2 = mz*z;
|
||||||
|
|
||||||
_tI[0] += y2+z2; _tI[1] -= xy; _tI[2] -= zx;
|
_tI[0] += y2+z2; _tI[1] -= xy; _tI[2] -= zx;
|
||||||
_tI[4] += x2+z2; _tI[5] -= yz;
|
_tI[4] += x2+z2; _tI[5] -= yz;
|
||||||
_tI[8] += x2+y2;
|
_tI[8] += x2+y2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// copy symmetric elements
|
// copy symmetric elements
|
||||||
_tI[3] = _tI[1];
|
_tI[3] = _tI[1];
|
||||||
|
@ -201,8 +195,8 @@ void RigidBody::recalc()
|
||||||
|
|
||||||
void RigidBody::reset()
|
void RigidBody::reset()
|
||||||
{
|
{
|
||||||
_torque[0] = _torque[1] = _torque[2] = 0;
|
Math::zero3(_torque);
|
||||||
_force[0] = _force[1] = _force[2] = 0;
|
Math::zero3(_force);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RigidBody::addForce(const float* pos, const float* force)
|
void RigidBody::addForce(const float* pos, const float* force)
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
#include <Main/fg_props.hxx>
|
#include <Main/fg_props.hxx>
|
||||||
|
#include "yasim-common.hpp"
|
||||||
|
#include "Math.hpp"
|
||||||
#include "Surface.hpp"
|
#include "Surface.hpp"
|
||||||
|
|
||||||
namespace yasim {
|
namespace yasim {
|
||||||
int Surface::s_idGenerator = 0;
|
int Surface::s_idGenerator = 0;
|
||||||
|
|
||||||
Surface::Surface( Version * version ) :
|
Surface::Surface(Version* version, float* pos, float dragCoefficient = 1 ) :
|
||||||
_version(version)
|
_version(version),
|
||||||
|
_c0(dragCoefficient)
|
||||||
{
|
{
|
||||||
_id = s_idGenerator++;
|
_id = s_idGenerator++;
|
||||||
|
|
||||||
|
@ -13,36 +16,59 @@ Surface::Surface( Version * version ) :
|
||||||
_orient[3] = 0; _orient[4] = 1; _orient[5] = 0;
|
_orient[3] = 0; _orient[4] = 1; _orient[5] = 0;
|
||||||
_orient[6] = 0; _orient[7] = 0; _orient[8] = 1;
|
_orient[6] = 0; _orient[7] = 0; _orient[8] = 1;
|
||||||
|
|
||||||
|
Math::set3(pos, _pos);
|
||||||
|
|
||||||
_surfN = fgGetNode("/fdm/yasim/debug/surfaces", true);
|
_surfN = fgGetNode("/fdm/yasim/debug/surfaces", true);
|
||||||
if (_surfN != 0) {
|
if (_surfN != 0) {
|
||||||
_surfN = _surfN->getChild("surface", _id, true);
|
_surfN = _surfN->getChild("surface", _id, true);
|
||||||
_fxN = _surfN->getNode("f-x", true);
|
_fxN = _surfN->getNode("f-x", true);
|
||||||
_fyN = _surfN->getNode("f-y", true);
|
_fyN = _surfN->getNode("f-y", true);
|
||||||
_fzN = _surfN->getNode("f-z", true);
|
_fzN = _surfN->getNode("f-z", true);
|
||||||
_fabsN = _surfN->getNode("f-abs", true);
|
_fabsN = _surfN->getNode("f-abs", true);
|
||||||
_alphaN = _surfN->getNode("alpha", true);
|
_alphaN = _surfN->getNode("alpha", true);
|
||||||
_stallAlphaN = _surfN->getNode("stall-alpha", true);
|
_stallAlphaN = _surfN->getNode("stall-alpha", true);
|
||||||
_flapN = _surfN->getNode("flap-pos", true);
|
_flapN = _surfN->getNode("flap-pos", true);
|
||||||
_slatN = _surfN->getNode("slat-pos", true);
|
_slatN = _surfN->getNode("slat-pos", true);
|
||||||
_spoilerN = _surfN->getNode("spoiler-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;
|
Math::set3(pos, _pos);
|
||||||
for(i=0; i<3; i++) _pos[i] = p[i];
|
|
||||||
if (_surfN != 0) {
|
if (_surfN != 0) {
|
||||||
_surfN->getNode("pos-x", true)->setFloatValue(p[0]);
|
_surfN->getNode("pos-x", true)->setFloatValue(pos[0]);
|
||||||
_surfN->getNode("pos-y", true)->setFloatValue(p[1]);
|
_surfN->getNode("pos-y", true)->setFloatValue(pos[1]);
|
||||||
_surfN->getNode("pos-z", true)->setFloatValue(p[2]);
|
_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)
|
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)
|
void Surface::setFlapPos(float pos)
|
||||||
{
|
{
|
||||||
if (_flapPos != pos) {
|
if (_flapPos != pos) {
|
||||||
_flapPos = pos;
|
_flapPos = pos;
|
||||||
if (_surfN != 0) _flapN->setFloatValue(pos);
|
if (_surfN != 0) {
|
||||||
|
_flapN->setFloatValue(pos);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +104,9 @@ void Surface::setSlatPos(float pos)
|
||||||
{
|
{
|
||||||
if (_slatPos != pos) {
|
if (_slatPos != pos) {
|
||||||
_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
|
// Calculate the aerodynamic force given a wind vector v (in the
|
||||||
// aircraft's "local" coordinates) and an air density rho. Returns a
|
// 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)
|
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:
|
// Split v into magnitude and direction:
|
||||||
float vel = Math::mag3(v);
|
float vel = Math::mag3(v);
|
||||||
|
|
||||||
// Zero velocity means zero force by definition (also prevents div0).
|
// Zero velocity means zero force by definition (also prevents div0).
|
||||||
if(vel == 0) {
|
if(vel == 0) {
|
||||||
int i;
|
return;
|
||||||
for(i=0; i<3; i++) out[i] = torque[i] = 0;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// special case this so the logic below doesn't produce a non-zero
|
// special case this so the logic below doesn't produce a non-zero
|
||||||
// force; should probably have a "no force" flag instead...
|
// force; should probably have a "no force" flag instead...
|
||||||
if(_cx == 0. && _cy == 0. && _cz == 0.) {
|
if(_cx == 0. && _cy == 0. && _cz == 0.) {
|
||||||
for(int i=0; i<3; i++) out[i] = torque[i] = 0.;
|
|
||||||
return;
|
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
|
// "Rotate" by the incidence angle. Assume small angles, so we
|
||||||
// need to diddle only the Z component, X is relatively unchanged
|
// 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;
|
float incidence = _incidence + _twist;
|
||||||
out[2] += incidence * out[0]; // z' = z + incidence * x
|
out[2] += incidence * out[0]; // z' = z + incidence * x
|
||||||
|
|
||||||
// Hold onto the local wind vector so we can multiply the induced
|
// Hold onto the local wind vector so we can multiply the induced
|
||||||
// drag at the end.
|
// drag at the end.
|
||||||
float lwind[3];
|
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
|
// roughly parallel with Z, the small-angle approximation
|
||||||
// must change its X component.
|
// must change its X component.
|
||||||
if( _version->isVersionOrNewer( Version::YASIM_VERSION_32 )) {
|
if( _version->isVersionOrNewer( Version::YASIM_VERSION_32 )) {
|
||||||
out[0] += incidence * out[2];
|
out[0] += incidence * out[2];
|
||||||
} else {
|
} else {
|
||||||
out[2] -= incidence * out[0];
|
out[2] -= incidence * out[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert back to external coordinates
|
// Convert back to external coordinates
|
||||||
|
|
|
@ -16,7 +16,7 @@ class Surface
|
||||||
int _id; //index for property tree
|
int _id; //index for property tree
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Surface( Version * version );
|
Surface(Version * version, float* pos, float dragCoefficient);
|
||||||
|
|
||||||
int getID() const { return _id; };
|
int getID() const { return _id; };
|
||||||
static void resetIDgen() { s_idGenerator = 0; };
|
static void resetIDgen() { s_idGenerator = 0; };
|
||||||
|
@ -25,8 +25,8 @@ public:
|
||||||
void setPosition(const float* p);
|
void setPosition(const float* p);
|
||||||
void getPosition(float* out) const { Math::set3(_pos, out); }
|
void getPosition(float* out) const { Math::set3(_pos, out); }
|
||||||
|
|
||||||
// Distance scale along the X axis
|
// Distance scale along the X axis used for torque (pitch) calculation
|
||||||
void setChord(float chord) { _chord = chord; }
|
void setChord(float chord);
|
||||||
|
|
||||||
// Slats act to move the stall peak by the specified angle, and
|
// Slats act to move the stall peak by the specified angle, and
|
||||||
// increase drag by the multiplier specified.
|
// increase drag by the multiplier specified.
|
||||||
|
@ -59,16 +59,18 @@ public:
|
||||||
// The offset from base incidence for this surface.
|
// The offset from base incidence for this surface.
|
||||||
void setTwist(float angle) { _twist = angle; }
|
void setTwist(float angle) { _twist = angle; }
|
||||||
|
|
||||||
void setTotalDrag(float c0) { _c0 = c0; }
|
void setTotalForceCoefficient(float c0) { _c0 = c0; }
|
||||||
float getTotalDrag() const { return _c0; }
|
void mulTotalForceCoefficient(float factor) { _c0 *= factor; }
|
||||||
|
float getTotalForceCoefficient() const { return _c0; }
|
||||||
|
|
||||||
void setXDrag(float cx) { _cx = cx; }
|
void setDragCoefficient(float cx) { _cx = cx; }
|
||||||
void setYDrag(float cy) { _cy = cy; }
|
float getDragCoefficient() const { return _cx; }
|
||||||
void setZDrag(float cz) { _cz = cz; }
|
void setYDrag(float cy) { _cy = cy; }
|
||||||
float getXDrag() const { return _cx; }
|
void setLiftCoefficient(float cz) { _cz = cz; }
|
||||||
|
float getLiftCoefficient() const { return _cz; }
|
||||||
|
|
||||||
// zero-alpha Z drag ("camber") specified as a fraction of 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
|
// i: 0 == forward, 1 == backwards
|
||||||
void setStallPeak(int i, float peak) { _peaks[i] = peak; }
|
void setStallPeak(int i, float peak) { _peaks[i] = peak; }
|
||||||
|
@ -87,6 +89,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SGPropertyNode_ptr _surfN;
|
SGPropertyNode_ptr _surfN;
|
||||||
|
Version * _version;
|
||||||
|
|
||||||
float stallFunc(float* v);
|
float stallFunc(float* v);
|
||||||
float flapLift(float alpha);
|
float flapLift(float alpha);
|
||||||
|
@ -123,7 +126,6 @@ private:
|
||||||
float _stallAlpha {0};
|
float _stallAlpha {0};
|
||||||
float _alpha {0};
|
float _alpha {0};
|
||||||
|
|
||||||
Version * _version;
|
|
||||||
SGPropertyNode* _fxN;
|
SGPropertyNode* _fxN;
|
||||||
SGPropertyNode* _fyN;
|
SGPropertyNode* _fyN;
|
||||||
SGPropertyNode* _fzN;
|
SGPropertyNode* _fzN;
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
#ifndef _VECTOR_HPP
|
#ifndef _VECTOR_HPP
|
||||||
#define _VECTOR_HPP
|
#define _VECTOR_HPP
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
namespace yasim {
|
||||||
//
|
//
|
||||||
// Excruciatingly simple vector-of-pointers class. Easy & useful.
|
// Excruciatingly simple vector-of-pointers class. Easy & useful.
|
||||||
// No support for addition of elements anywhere but at the end of the
|
// No support for addition of elements anywhere but at the end of the
|
||||||
|
@ -19,9 +23,9 @@ public:
|
||||||
private:
|
private:
|
||||||
void realloc();
|
void realloc();
|
||||||
|
|
||||||
int _nelem;
|
int _nelem {0};
|
||||||
int _sz;
|
int _sz {0};
|
||||||
void** _array;
|
void** _array {nullptr};
|
||||||
};
|
};
|
||||||
|
|
||||||
inline Vector::Vector()
|
inline Vector::Vector()
|
||||||
|
@ -46,11 +50,14 @@ inline int Vector::add(void* p)
|
||||||
|
|
||||||
inline void* Vector::get(int i) const
|
inline void* Vector::get(int i) const
|
||||||
{
|
{
|
||||||
|
assert(i >= 0 and i < _sz);
|
||||||
return _array[i];
|
return _array[i];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void Vector::set(int i, void* p)
|
inline void Vector::set(int i, void* p)
|
||||||
{
|
{
|
||||||
|
assert(i >= 0 and i < _sz);
|
||||||
_array[i] = p;
|
_array[i] = p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,5 +80,5 @@ inline void Vector::realloc()
|
||||||
delete[] _array;
|
delete[] _array;
|
||||||
_array = array;
|
_array = array;
|
||||||
}
|
}
|
||||||
|
}; //namespace yasim
|
||||||
#endif // _VECTOR_HPP
|
#endif // _VECTOR_HPP
|
||||||
|
|
|
@ -18,6 +18,8 @@ void Version::setVersion( const char * version )
|
||||||
_version = YASIM_VERSION_32;
|
_version = YASIM_VERSION_32;
|
||||||
} else if( v == "2017.2" ) {
|
} else if( v == "2017.2" ) {
|
||||||
_version = YASIM_VERSION_2017_2;
|
_version = YASIM_VERSION_2017_2;
|
||||||
|
} else if( v == "2018.1" ) {
|
||||||
|
_version = YASIM_VERSION_2018_1;
|
||||||
} else if( v == "YASIM_VERSION_CURRENT" ) {
|
} else if( v == "YASIM_VERSION_CURRENT" ) {
|
||||||
_version = YASIM_VERSION_CURRENT;
|
_version = YASIM_VERSION_CURRENT;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -12,7 +12,8 @@ public:
|
||||||
YASIM_VERSION_ORIGINAL = 0,
|
YASIM_VERSION_ORIGINAL = 0,
|
||||||
YASIM_VERSION_32,
|
YASIM_VERSION_32,
|
||||||
YASIM_VERSION_2017_2,
|
YASIM_VERSION_2017_2,
|
||||||
YASIM_VERSION_CURRENT = YASIM_VERSION_2017_2
|
YASIM_VERSION_2018_1,
|
||||||
|
YASIM_VERSION_CURRENT = YASIM_VERSION_2018_1
|
||||||
} YASIM_VERSION;
|
} YASIM_VERSION;
|
||||||
|
|
||||||
void setVersion( const char * version );
|
void setVersion( const char * version );
|
||||||
|
|
|
@ -1,135 +1,104 @@
|
||||||
#include "yasim-common.hpp"
|
#include "yasim-common.hpp"
|
||||||
|
#include "Model.hpp"
|
||||||
#include "Surface.hpp"
|
#include "Surface.hpp"
|
||||||
#include "Wing.hpp"
|
#include "Wing.hpp"
|
||||||
|
|
||||||
namespace yasim {
|
namespace yasim {
|
||||||
|
|
||||||
Wing::Wing(Version *ver, bool mirror, float* base, float chord,
|
Wing::Wing(Version *ver, bool mirror) :
|
||||||
float length, float taper, float sweep, float dihedral, float twist) :
|
|
||||||
_version(ver),
|
_version(ver),
|
||||||
_mirror(mirror),
|
_mirror(mirror)
|
||||||
_chord(chord),
|
|
||||||
_length(length),
|
|
||||||
_taper(taper),
|
|
||||||
_sweep(sweep),
|
|
||||||
_dihedral(dihedral),
|
|
||||||
_twist(twist)
|
|
||||||
{
|
{
|
||||||
Math::set3(base, _base);
|
|
||||||
_meanChord = _chord*(_taper+1)*0.5f;
|
|
||||||
calculateWingCoordinateSystem();
|
|
||||||
calculateTip();
|
|
||||||
calculateSpan();
|
|
||||||
calculateMAC();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Wing::~Wing()
|
Wing::~Wing()
|
||||||
{
|
{
|
||||||
for(int i=0; i<_surfs.size(); i++) {
|
WingSection* ws;
|
||||||
SurfRec* s = (SurfRec*)_surfs.get(i);
|
for (int s=0; s < _sections.size(); s++){
|
||||||
delete s->surface;
|
ws = (WingSection*)_sections.get(s);
|
||||||
delete s;
|
for(int i=0; i<ws->_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;
|
WingSection* ws = new WingSection;
|
||||||
for(int i=0; i<_surfs.size(); i++)
|
if (_sections.size() == 0) {
|
||||||
((SurfRec*)_surfs.get(i))->surface->setIncidence(incidence);
|
// first section
|
||||||
}
|
Math::set3(base, _base);
|
||||||
|
ws->_rootChord = _float2chord(base, chord);
|
||||||
void Wing::setFlap0Params(float start, float end, float lift, float drag)
|
ws->_sectionIncidence = incidence;
|
||||||
{
|
} else {
|
||||||
_flap0Start = start;
|
WingSection* prev = (WingSection*)_sections.get(_sections.size()-1);
|
||||||
_flap0End = end;
|
//use old wing tip instead of base argument
|
||||||
_flap0Lift = lift;
|
ws->_rootChord = prev->_tipChord;
|
||||||
_flap0Drag = drag;
|
ws->_sectionIncidence = prev->_sectionIncidence + prev->_twist;
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
ws->_length = wingLength;
|
||||||
|
ws->_taper = taper;
|
||||||
void Wing::setFlap0Effectiveness(float lval)
|
ws->_sweepAngleCenterLine = sweep;
|
||||||
{
|
ws->_dihedral = dihedral;
|
||||||
lval = Math::clamp(lval, 1, 10);
|
ws->_twist = twist;
|
||||||
for(int i=0; i<_flap0Surfs.size(); i++) {
|
ws->_camber = camber;
|
||||||
((Surface*)_flap0Surfs.get(i))->setFlapEffectiveness(lval);
|
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;
|
||||||
}
|
}
|
||||||
}
|
// append section: Calculate wing MAC from MACs of section and prev wing
|
||||||
|
else {
|
||||||
void Wing::setFlap1Pos(float lval, float rval)
|
_mac = Wing::calculateMAC(_mac, ws->getMAC());
|
||||||
{
|
_wingspan += ws->_wingspan;
|
||||||
lval = Math::clamp(lval, -1, 1);
|
_area += ws->getArea();
|
||||||
rval = Math::clamp(rval, -1, 1);
|
_meanChord = _meanChord * ws->_meanChord * 0.5f;
|
||||||
for(int i=0; i<_flap1Surfs.size(); i++) {
|
|
||||||
((Surface*)_flap1Surfs.get(i))->setFlapPos(lval);
|
|
||||||
if(_mirror) ((Surface*)_flap1Surfs.get(++i))->setFlapPos(rval);
|
|
||||||
}
|
}
|
||||||
|
_chord2float(ws->_tipChord, _tip);
|
||||||
|
return idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Wing::setFlap1Effectiveness(float lval)
|
Chord Wing::_float2chord(float* pos, float lenght)
|
||||||
{
|
{
|
||||||
lval = Math::clamp(lval, 1, 10);
|
Chord c;
|
||||||
for(int i=0; i<_flap1Surfs.size(); i++) {
|
c.x = pos[0];
|
||||||
((Surface*)_flap1Surfs.get(i))->setFlapEffectiveness(lval);
|
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);
|
pos[0] = c.x;
|
||||||
rval = Math::clamp(rval, 0, 1);
|
pos[1] = c.y;
|
||||||
for(int i=0; i<_spoilerSurfs.size(); i++) {
|
pos[2] = c.z;
|
||||||
((Surface*)_spoilerSurfs.get(i))->setSpoilerPos(lval);
|
|
||||||
if(_mirror) ((Surface*)_spoilerSurfs.get(++i))->setSpoilerPos(rval);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Wing::setSlatPos(float val)
|
void Wing::WingSection::calculateGeometry()
|
||||||
{
|
{
|
||||||
val = Math::clamp(val, 0, 1);
|
_meanChord = _rootChord.length*(_taper+1)*0.5f;
|
||||||
for(int i=0; i<_slatSurfs.size(); i++)
|
calculateWingCoordinateSystem();
|
||||||
((Surface*)_slatSurfs.get(i))->setSlatPos(val);
|
calculateTipChord();
|
||||||
|
calculateSpan();
|
||||||
|
calculateMAC();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Wing::calculateWingCoordinateSystem() {
|
void Wing::WingSection::calculateWingCoordinateSystem() {
|
||||||
// prepare wing coordinate system, ignoring incidence and twist for now
|
// prepare wing coordinate system, ignoring incidence and twist for now
|
||||||
// (tail incidence is varied by the solver)
|
// (tail incidence is varied by the solver)
|
||||||
// Generating a unit vector pointing out the left wing.
|
// Generating a unit vector pointing out the left wing.
|
||||||
float left[3];
|
float left[3];
|
||||||
left[0] = -Math::tan(_sweep);
|
left[0] = -Math::tan(_sweepAngleCenterLine);
|
||||||
left[1] = Math::cos(_dihedral);
|
left[1] = Math::cos(_dihedral);
|
||||||
left[2] = Math::sin(_dihedral);
|
left[2] = Math::sin(_dihedral);
|
||||||
Math::unit3(left, left);
|
Math::unit3(left, left);
|
||||||
|
@ -155,180 +124,311 @@ void Wing::calculateWingCoordinateSystem() {
|
||||||
for(i=3; i<6; i++) _rightOrient[i] = -_rightOrient[i];
|
for(i=3; i<6; i++) _rightOrient[i] = -_rightOrient[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
void Wing::calculateTip() {
|
void Wing::WingSection::calculateTipChord() {
|
||||||
float *y = _orient+3;
|
float *y = _orient+3;
|
||||||
Math::mul3(_length, y, _tip);
|
_tipChord.x = _rootChord.x + _length * y[0];
|
||||||
Math::add3(_base, _tip, _tip);
|
_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 in y-direction (not for vstab)
|
||||||
_wingspan = Math::abs(2*_tip[1]);
|
_wingspan = Math::abs(2*_tipChord.y);
|
||||||
_netSpan = Math::abs(2*(_tip[1]-_base[1]));
|
|
||||||
_aspectRatio = _wingspan / _meanChord;
|
_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
|
// http://www.nasascale.org/p2/wp-content/uploads/mac-calculator.htm
|
||||||
const float commonFactor = _chord*(0.5+_taper)/(3*_chord*(1+_taper));
|
const float commonFactor = _rootChord.length*(0.5+_taper)/(3*_rootChord.length*(1+_taper));
|
||||||
_mac = _chord-(2*_chord*(1-_taper)*commonFactor);
|
_mac.length = _rootChord.length-(2*_rootChord.length*(1-_taper)*commonFactor);
|
||||||
_macRootDistance = _netSpan*commonFactor;
|
_mac.y = Math::abs(2*(_tipChord.y-_rootChord.y))*commonFactor;
|
||||||
_macX = _base[0]-Math::tan(_sweep) * _macRootDistance + _mac/2;
|
_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 (_length == 0) {
|
||||||
if(! _surfs.empty()) return;
|
return 0;
|
||||||
|
|
||||||
// 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; i<NUM_BOUNDS; i++) {
|
|
||||||
int minIdx = i;
|
|
||||||
float minVal = bounds[i];
|
|
||||||
for(int j=i+1; j<NUM_BOUNDS; j++) {
|
|
||||||
if(bounds[j] < minVal) {
|
|
||||||
minIdx = j;
|
|
||||||
minVal = bounds[j];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
float tmp = bounds[i];
|
|
||||||
bounds[i] = minVal; bounds[minIdx] = tmp;
|
|
||||||
}
|
}
|
||||||
|
return Math::atan(
|
||||||
|
(sin(_sweepAngleCenterLine)+(1-_taper)*_rootChord.length/(2*_length)) /
|
||||||
|
cos(_sweepAngleCenterLine)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Uniqify
|
void Wing::WingSection::setIncidence(float incidence)
|
||||||
float last = bounds[0];
|
{
|
||||||
int nbounds = 1;
|
//update surface
|
||||||
for(i=1; i<NUM_BOUNDS; i++) {
|
for(int i=0; i<_surfs.size(); i++)
|
||||||
if(bounds[i] != last)
|
((SurfRec*)_surfs.get(i))->surface->setIncidence(incidence + _sectionIncidence);
|
||||||
bounds[nbounds++] = bounds[i];
|
}
|
||||||
last = bounds[i];
|
|
||||||
|
// 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;
|
||||||
}
|
}
|
||||||
|
lval = Math::clamp(lval, min, 1);
|
||||||
// Calculate a "nominal" segment length equal to an average chord,
|
rval = Math::clamp(rval, min, 1);
|
||||||
// normalized to lie within 0-1 over the length of the wing.
|
WingSection* ws;
|
||||||
float segLen = _meanChord / _length;
|
for (int section=0; section < _sections.size(); section++)
|
||||||
|
{
|
||||||
// Now go through each boundary and make segments
|
ws = (WingSection*)_sections.get(section);
|
||||||
for(i=0; i<(nbounds-1); i++) {
|
for(int i=0; i < ws->_flapSurfs[type].size(); i++) {
|
||||||
float start = bounds[i];
|
switch (type) {
|
||||||
float end = bounds[i+1];
|
case WING_FLAP0:
|
||||||
float mid = (start+end)/2;
|
case WING_FLAP1:
|
||||||
|
((Surface*)ws->_flapSurfs[type].get(i))->setFlapPos(lval);
|
||||||
bool hasFlap0=0, hasFlap1=0, hasSlat=0, hasSpoiler=0;
|
if(_mirror) ((Surface*)ws->_flapSurfs[type].get(++i))->setFlapPos(rval);
|
||||||
if(_flap0Start < mid && mid < _flap0End) hasFlap0 = 1;
|
break;
|
||||||
if(_flap1Start < mid && mid < _flap1End) hasFlap1 = 1;
|
case WING_SLAT:
|
||||||
if(_slatStart < mid && mid < _slatEnd) hasSlat = 1;
|
((Surface*)ws->_flapSurfs[type].get(i))->setSlatPos(lval);
|
||||||
if(_spoilerStart < mid && mid < _spoilerEnd) hasSpoiler = 1;
|
break;
|
||||||
|
case WING_SPOILER:
|
||||||
// FIXME: Should probably detect an error here if both flap0
|
((Surface*)ws->_flapSurfs[type].get(i))->setSpoilerPos(lval);
|
||||||
// and flap1 are set. Right now flap1 overrides.
|
if(_mirror) ((Surface*)ws->_flapSurfs[type].get(++i))->setSpoilerPos(rval);
|
||||||
|
break;
|
||||||
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<nSegs; j++) {
|
|
||||||
float frac = start + (j+0.5f) * (end-start)/nSegs;
|
|
||||||
float pos[3];
|
|
||||||
interp(_base, _tip, frac, pos);
|
|
||||||
|
|
||||||
float chord = _chord * (1 - (1-_taper)*frac);
|
|
||||||
float weight = chord * segWid;
|
|
||||||
float twist = _twist * frac;
|
|
||||||
|
|
||||||
Surface *s = newSurface(pos, _orient, chord,
|
|
||||||
hasFlap0, hasFlap1, hasSlat, hasSpoiler);
|
|
||||||
|
|
||||||
addSurface(s, weight, twist);
|
|
||||||
|
|
||||||
if(_mirror) {
|
|
||||||
pos[1] = -pos[1];
|
|
||||||
s = newSurface(pos, _rightOrient, chord,
|
|
||||||
hasFlap0, hasFlap1, hasSlat, hasSpoiler);
|
|
||||||
addSurface(s, weight, twist);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 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();
|
lval = Math::clamp(lval, 1, 10);
|
||||||
sr->surface = s;
|
WingSection* ws;
|
||||||
sr->weight = weight;
|
for (int section=0; section < _sections.size(); section++)
|
||||||
s->setTotalDrag(sr->weight);
|
{
|
||||||
s->setTwist(twist);
|
ws = (WingSection*)_sections.get(section);
|
||||||
_surfs.add(sr);
|
for(int i=0; i<ws->_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<NUM_BOUNDS; i++) {
|
||||||
|
int minIdx = i;
|
||||||
|
float minVal = bounds[i];
|
||||||
|
for(int j=i+1; j<NUM_BOUNDS; j++) {
|
||||||
|
if(bounds[j] < minVal) {
|
||||||
|
minIdx = j;
|
||||||
|
minVal = bounds[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
float tmp = bounds[i];
|
||||||
|
bounds[i] = minVal; bounds[minIdx] = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uniqify
|
||||||
|
float last = bounds[0];
|
||||||
|
int nbounds = 1;
|
||||||
|
for(int i=1; i<NUM_BOUNDS; i++) {
|
||||||
|
if(bounds[i] != last)
|
||||||
|
bounds[nbounds++] = bounds[i];
|
||||||
|
last = bounds[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate a "nominal" segment length equal to an average chord,
|
||||||
|
// normalized to lie within 0-1 over the length of the wing.
|
||||||
|
float segLen = ws->_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<nSegs; j++) {
|
||||||
|
float frac = start + (j+0.5f) * (end-start)/nSegs;
|
||||||
|
float pos[3];
|
||||||
|
interp(_base, _tip, frac, pos);
|
||||||
|
|
||||||
|
float chord = ws->_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;
|
_dragScale = scale;
|
||||||
for(int i=0; i<_surfs.size(); i++) {
|
for(int i=0; i<_surfs.size(); i++) {
|
||||||
SurfRec* s = (SurfRec*)_surfs.get(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;
|
_liftRatio = ratio;
|
||||||
for(int i=0; i<_surfs.size(); i++)
|
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,
|
void Wing::WingSection::multiplyLiftRatio(float factor)
|
||||||
bool hasFlap0, bool hasFlap1, bool hasSlat, bool hasSpoiler)
|
|
||||||
{
|
{
|
||||||
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->setOrientation(orient);
|
||||||
s->setChord(chord);
|
s->setChord(chord);
|
||||||
|
|
||||||
// Camber is expressed as a fraction of stall peak, so convert.
|
// 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
|
// The "main" (i.e. normal) stall angle
|
||||||
float stallAoA = _stall - _stallWidth/4;
|
float stallAoA = _stallParams.aoa - _stallParams.width/4;
|
||||||
s->setStall(0, stallAoA);
|
s->setStall(0, stallAoA);
|
||||||
s->setStallWidth(0, _stallWidth);
|
s->setStallWidth(0, _stallParams.width);
|
||||||
s->setStallPeak(0, _stallPeak);
|
s->setStallPeak(0, _stallParams.peak);
|
||||||
|
|
||||||
// The negative AoA stall is the same if we're using an symmetric
|
// The negative AoA stall is the same if we're using an symmetric
|
||||||
// airfoil, otherwise a "little worse".
|
// airfoil, otherwise a "little worse".
|
||||||
if(_camber > 0) {
|
if(_camber > 0) {
|
||||||
s->setStall(1, stallAoA * 0.8f);
|
s->setStall(1, stallAoA * 0.8f);
|
||||||
s->setStallWidth(1, _stallWidth * 0.5f);
|
s->setStallWidth(1, _stallParams.width * 0.5f);
|
||||||
} else {
|
} else {
|
||||||
s->setStall(1, stallAoA);
|
s->setStall(1, stallAoA);
|
||||||
if( _version->isVersionOrNewer( Version::YASIM_VERSION_2017_2 )) {
|
if( _version->isVersionOrNewer( Version::YASIM_VERSION_2017_2 )) {
|
||||||
// what was presumably meant
|
// what was presumably meant
|
||||||
s->setStallWidth(1, _stallWidth);
|
s->setStallWidth(1, _stallParams.width);
|
||||||
} else {
|
} else {
|
||||||
// old code; presumably a copy&paste error
|
// 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);
|
s->setStallWidth(i, 0.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(hasFlap0) s->setFlapParams(_flap0Lift, _flap0Drag);
|
if(hasFlap0) s->setFlapParams(_flapParams[WING_FLAP0].lift, _flapParams[WING_FLAP0].drag);
|
||||||
if(hasFlap1) s->setFlapParams(_flap1Lift, _flap1Drag);
|
if(hasFlap1) s->setFlapParams(_flapParams[WING_FLAP1].lift, _flapParams[WING_FLAP1].drag);
|
||||||
if(hasSlat) s->setSlatParams(_slatAoA, _slatDrag);
|
if(hasSlat) s->setSlatParams(_flapParams[WING_SLAT].aoa, _flapParams[WING_SLAT].drag);
|
||||||
if(hasSpoiler) s->setSpoilerParams(_spoilerLift, _spoilerDrag);
|
if(hasSpoiler) s->setSpoilerParams(_flapParams[WING_SPOILER].lift, _flapParams[WING_SPOILER].drag);
|
||||||
|
|
||||||
if(hasFlap0) _flap0Surfs.add(s);
|
if(hasFlap0) _flapSurfs[WING_FLAP0].add(s);
|
||||||
if(hasFlap1) _flap1Surfs.add(s);
|
if(hasFlap1) _flapSurfs[WING_FLAP1].add(s);
|
||||||
if(hasSlat) _slatSurfs.add(s);
|
if(hasSlat) _flapSurfs[WING_SLAT].add(s);
|
||||||
if(hasSpoiler) _spoilerSurfs.add(s);
|
if(hasSpoiler) _flapSurfs[WING_SPOILER].add(s);
|
||||||
|
|
||||||
s->setInducedDrag(_inducedDrag);
|
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)
|
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]);
|
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
|
}; // namespace yasim
|
||||||
|
|
|
@ -4,159 +4,171 @@
|
||||||
#include "Vector.hpp"
|
#include "Vector.hpp"
|
||||||
#include "Version.hpp"
|
#include "Version.hpp"
|
||||||
#include "Math.hpp"
|
#include "Math.hpp"
|
||||||
|
#include <simgear/props/props.hxx>
|
||||||
|
|
||||||
namespace yasim {
|
namespace yasim {
|
||||||
|
|
||||||
class Surface;
|
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 {
|
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:
|
public:
|
||||||
Wing(Version *ver, bool mirror, float* base, float chord, float length,
|
Wing(Version* ver, bool mirror);
|
||||||
float taper = 1, float sweep = 0, float dihedral = 0, float twist = 0);
|
|
||||||
~Wing();
|
~Wing();
|
||||||
|
|
||||||
// Do we mirror ourselves about the XZ plane?
|
int addWingSection(float* base, float chord, float wingLength,
|
||||||
void setMirror(bool mirror) { _mirror = mirror; }
|
float taper = 1, float sweep = 0, float dihedral = 0, float twist = 0, float camber = 0, float idrag = 1, float incidence = 0);
|
||||||
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);
|
|
||||||
|
|
||||||
// Set the control axes for the sub-surfaces
|
static Chord calculateMAC(Chord root, Chord tip);
|
||||||
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);
|
|
||||||
|
|
||||||
|
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
|
// Compile the thing into a bunch of Surface objects
|
||||||
void compile();
|
void compile();
|
||||||
void getTip(float* tip) const { Math::set3(_tip, tip);};
|
void multiplyLiftRatio(float factor);
|
||||||
|
void multiplyDragCoefficient(float factor);
|
||||||
// valid only after Wing::compile() was called
|
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 getSpan() const { return _wingspan; };
|
||||||
float getArea() const { return _wingspan*_meanChord; };
|
float getArea() const { return _area; };
|
||||||
float getAspectRatio() const { return _aspectRatio; };
|
float getAspectRatio() const { return _aspectRatio; };
|
||||||
float getSMC() const { return _meanChord; };
|
float getSMC() const { return _meanChord; };
|
||||||
float getMAC() const { return _mac; }; // get length of MAC
|
float getMACLength() const { return _mac.length; }; // get length of MAC
|
||||||
float getMACx() const { return _macX; }; // get x-coord of MAC leading edge
|
float getMACx() const { return _mac.x; }; // get x-coord of MAC leading edge
|
||||||
float getMACy() const { return _base[1]+_macRootDistance; }; // get y-coord of MAC leading edge
|
float getMACy() const { return _mac.y; }; // get y-coord of MAC leading edge
|
||||||
|
|
||||||
|
//-----------------------------
|
||||||
int numSurfaces() const { return _surfs.size(); }
|
// propergate the control axes value for the sub-surfaces
|
||||||
Surface* getSurface(int n) { return ((SurfRec*)_surfs.get(n))->surface; }
|
void setFlapPos(WingFlaps type, float lval, float rval = 0);
|
||||||
float getSurfaceWeight(int n) const { return ((SurfRec*)_surfs.get(n))->weight; }
|
void setFlapEffectiveness(WingFlaps f, float lval);
|
||||||
|
|
||||||
// The overall drag coefficient for the wing as a whole. Units are
|
void setPropertyNode(SGPropertyNode_ptr n) { _wingN = n; };
|
||||||
// arbitrary.
|
float updateModel(Model* model);
|
||||||
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};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}; // namespace yasim
|
}; // namespace yasim
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "yasim-common.hpp"
|
||||||
#include "Math.hpp"
|
#include "Math.hpp"
|
||||||
#include "FGFDM.hpp"
|
#include "FGFDM.hpp"
|
||||||
#include "PropEngine.hpp"
|
#include "PropEngine.hpp"
|
||||||
|
|
5
src/FDM/YASim/yasim-common.cpp
Normal file
5
src/FDM/YASim/yasim-common.cpp
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
#include "yasim-common.hpp"
|
||||||
|
|
||||||
|
namespace yasim {
|
||||||
|
|
||||||
|
}; //namespace yasim
|
|
@ -1,34 +1,36 @@
|
||||||
#ifndef _YASIM_COMMON_HPP
|
#ifndef _YASIM_COMMON_HPP
|
||||||
#define _YASIM_COMMON_HPP
|
#define _YASIM_COMMON_HPP
|
||||||
|
|
||||||
|
/*
|
||||||
|
common file for YASim wide constants and static helper functions
|
||||||
|
*/
|
||||||
namespace yasim {
|
namespace yasim {
|
||||||
static const float YASIM_PI = 3.14159265358979323846f;
|
static const float YASIM_PI = 3.14159265358979323846f;
|
||||||
static const float PI2 = YASIM_PI*2;
|
static const float PI2 = YASIM_PI*2;
|
||||||
static const float RAD2DEG = 180/YASIM_PI;
|
static const float RAD2DEG = 180/YASIM_PI;
|
||||||
static const float DEG2RAD = YASIM_PI/180;
|
static const float DEG2RAD = YASIM_PI/180;
|
||||||
static const float RPM2RAD = YASIM_PI/30;
|
static const float RPM2RAD = YASIM_PI/30;
|
||||||
|
|
||||||
static const float KTS2MPS = 1852.0f/3600.0f;
|
static const float KTS2MPS = 1852.0f/3600.0f;
|
||||||
static const float MPS2KTS = 3600.0f/1852.0f;
|
static const float MPS2KTS = 3600.0f/1852.0f;
|
||||||
static const float KMH2MPS = 1/3.6f;
|
static const float KMH2MPS = 1/3.6f;
|
||||||
|
|
||||||
static const float FT2M = 0.3048f;
|
static const float FT2M = 0.3048f;
|
||||||
static const float M2FT = 1/FT2M;
|
static const float M2FT = 1/FT2M;
|
||||||
|
|
||||||
static const float LBS2N = 4.44822f;
|
static const float LBS2N = 4.44822f;
|
||||||
static const float N2LB = 1/LBS2N;
|
static const float N2LB = 1/LBS2N;
|
||||||
static const float LBS2KG = 0.45359237f;
|
static const float LBS2KG = 0.45359237f;
|
||||||
static const float KG2LBS = 1/LBS2KG;
|
static const float KG2LBS = 1/LBS2KG;
|
||||||
static const float CM2GALS = 264.172037284f;
|
static const float CM2GALS = 264.172037284f;
|
||||||
static const float HP2W = 745.700f;
|
static const float HP2W = 745.700f;
|
||||||
static const float INHG2PA = 3386.389f;
|
static const float INHG2PA = 3386.389f;
|
||||||
static const float K2DEGF = 1.8f;
|
static const float K2DEGF = 1.8f;
|
||||||
static const float K2DEGFOFFSET = -459.4f;
|
static const float K2DEGFOFFSET = -459.4f;
|
||||||
static const float CIN2CM = 1.6387064e-5f;
|
static const float CIN2CM = 1.6387064e-5f;
|
||||||
|
|
||||||
static const float NM2FTLB = (1/(LBS2N*FT2M));
|
|
||||||
static const float SLUG2KG = 14.59390f;
|
|
||||||
|
|
||||||
};
|
static const float NM2FTLB = (1/(LBS2N*FT2M));
|
||||||
|
static const float SLUG2KG = 14.59390f;
|
||||||
|
}; //namespace yasim
|
||||||
|
|
||||||
#endif // ifndef _YASIM_COMMON_HPP
|
#endif // ifndef _YASIM_COMMON_HPP
|
||||||
|
|
|
@ -95,7 +95,7 @@ void yasim_graph(Airplane* a, const float alt, const float kts, int cfg = CONFIG
|
||||||
ld_max= ld;
|
ld_max= ld;
|
||||||
ld_max_deg = deg;
|
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("# 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);
|
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];
|
float SI_inertia[9];
|
||||||
a->getModel()->getBody()->getInertiaMatrix(SI_inertia);
|
a->getModel()->getBody()->getInertiaMatrix(SI_inertia);
|
||||||
float MAC = 0, MACx = 0, MACy = 0;
|
float MAC = 0, MACx = 0, MACy = 0;
|
||||||
Wing* wing = a->getWing();
|
Wing* wing {nullptr};
|
||||||
if (wing) {
|
if (a->hasWing()) {
|
||||||
MAC = a->getWing()->getMAC();
|
wing = a->getWing();
|
||||||
MACx = a->getWing()->getMACx();
|
MAC = wing->getMACLength();
|
||||||
MACy = a->getWing()->getMACy();
|
MACx = wing->getMACx();
|
||||||
|
MACy = wing->getMACy();
|
||||||
}
|
}
|
||||||
printf(" Iterations: %d\n", a->getSolutionIterations());
|
printf(" Iterations: %d\n", a->getSolutionIterations());
|
||||||
printf(" Drag Coefficient: %.3f\n", drag);
|
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("Approach Elevator: %.3f\n\n", a->getApproachElevator());
|
||||||
printf(" CG: x:%.3f, y:%.3f, z:%.3f\n", cg[0], cg[1], cg[2]);
|
printf(" CG: x:%.3f, y:%.3f, z:%.3f\n", cg[0], cg[1], cg[2]);
|
||||||
if (wing) {
|
if (wing) {
|
||||||
printf(" Wing MAC (*1): x:%.2f, y:%.2f, length:%.1f \n", MACx, MACy, MAC);
|
printf(" Wing MAC: (x:%.2f, y:%.2f), length:%.1f \n", MACx, MACy, MAC);
|
||||||
printf(" CG-x rel. MAC: %.3f\n", a->getCGMAC());
|
printf(" hard limit CG-x: %.3f \n", a->getCGHardLimitXMax());
|
||||||
printf(" CG-x desired: %.3f < %.3f < %.3f \n", a->getCGSoftLimitXMin(), cg[0], a->getCGSoftLimitXMax());
|
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("\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.0f, %7.0f, %7.0f\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.0f, %7.0f, %7.0f\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(" %7.0f, %7.0f, %7.0f\n", SI_inertia[6], SI_inertia[7], SI_inertia[8]);
|
||||||
printf("\n(*1) MAC calculation works on <wing> only! Numbers will be wrong for segmented wings, e.g. <wing>+<mstab>.\n");
|
|
||||||
}
|
}
|
||||||
delete fdm;
|
delete fdm;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
Loading…
Reference in a new issue