1
0
Fork 0

YASIM add wing section support

refactoring of FGFDM parser
replace old helpers with lib functions from <cstring>
remove typecast that kills 'const'
add some comments and clarify variable names
This commit is contained in:
Henning Stahlke 2017-06-13 21:15:46 +02:00
parent e913e44aa0
commit 408e645bb2
13 changed files with 1389 additions and 1052 deletions

View file

@ -111,6 +111,22 @@ 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++) {
@ -143,28 +159,28 @@ 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()->getPropertyHandle(prop);
_approachElevator.val = 0; _approachElevator.val = 0;
_approachConfig.controls.add(&_approachElevator); _approachConfig.controls.add(&_approachElevator);
} }
void Airplane::addApproachControl(int control, float val) void Airplane::addApproachControl(const char* prop, float val)
{ {
ControlSetting* c = new ControlSetting(); ControlSetting* c = new ControlSetting();
c->control = control; c->propHandle = getControlMap()->getPropertyHandle(prop);
c->val = val; c->val = val;
_approachConfig.controls.add(c); _approachConfig.controls.add(c);
} }
void Airplane::addCruiseControl(int control, float val) void Airplane::addCruiseControl(const char* prop, float val)
{ {
ControlSetting* c = new ControlSetting(); ControlSetting* c = new ControlSetting();
c->control = control; c->propHandle = getControlMap()->getPropertyHandle(prop);
c->val = val; c->val = val;
_cruiseConfig.controls.add(c); _cruiseConfig.controls.add(c);
} }
void Airplane::addSolutionWeight(bool approach, int idx, float wgt) void Airplane::addSolutionWeight(bool approach, int idx, float wgt)
{ {
@ -258,11 +274,11 @@ void Airplane::setWeight(int handle, float mass)
// how we simulate droppable stores. // how we simulate droppable stores.
if(mass == 0) { if(mass == 0) {
wr->surf->setXDrag(0); wr->surf->setXDrag(0);
wr->surf->setYDrag(0); wr->surf->setYDrag(0);
wr->surf->setZDrag(0); wr->surf->setZDrag(0);
} else { } else {
wr->surf->setXDrag(1); wr->surf->setXDrag(1);
wr->surf->setYDrag(1); wr->surf->setYDrag(1);
wr->surf->setZDrag(1); wr->surf->setZDrag(1);
} }
} }
@ -331,7 +347,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);
@ -344,14 +360,14 @@ float Airplane::compileFuselage(Fuselage* f)
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];
@ -367,40 +383,40 @@ float Airplane::compileFuselage(Fuselage* f)
Surface* s = new Surface(this); Surface* s = new Surface(this);
s->setPosition(pos); s->setPosition(pos);
// The following is the original YASim value for sideDrag. // The following is the original YASim value for sideDrag.
// Originally YASim calculated the fuselage's lateral drag // Originally YASim calculated the fuselage's lateral drag
// coefficient as (solver drag factor) * (len/wid). // coefficient as (solver drag factor) * (len/wid).
// However, this greatly underestimates a fuselage's lateral drag. // However, this greatly underestimates a fuselage's lateral drag.
float sideDrag = len/wid; float sideDrag = len/wid;
if ( isVersionOrNewer( YASIM_VERSION_32 ) ) { if ( isVersionOrNewer( YASIM_VERSION_32 ) ) {
// New YASim assumes a fixed lateral drag coefficient of 0.5. // New YASim assumes a fixed lateral drag coefficient of 0.5.
// This will not be multiplied by the solver drag factor, because // This will not be multiplied by the solver drag factor, because
// that factor is tuned to match the drag in the direction of // that factor is tuned to match the drag in the direction of
// flight, which is completely independent of lateral drag. // flight, which is completely independent of lateral drag.
// The value of 0.5 is only a ballpark estimate, roughly matching // The value of 0.5 is only a ballpark estimate, roughly matching
// the side-on drag for a long cylinder at the higher Reynolds // the side-on drag for a long cylinder at the higher Reynolds
// numbers typical for an aircraft's lateral drag. // numbers typical for an aircraft's lateral drag.
// This fits if the fuselage is long and has a round cross section. // This fits if the fuselage is long and has a round cross section.
// For flat-sided fuselages, the value should be increased, up to // For flat-sided fuselages, the value should be increased, up to
// a limit of around 2 for a long rectangular prism. // a limit of around 2 for a long rectangular prism.
// For very short fuselages, in which the end effects are strong, // For very short fuselages, in which the end effects are strong,
// the value should be reduced. // the value should be reduced.
// Such adjustments can be made using the fuselage's "cy" and "cz" // Such adjustments can be made using the fuselage's "cy" and "cz"
// XML parameters: "cy" for the sides, "cz" for top and bottom. // XML parameters: "cy" for the sides, "cz" for top and bottom.
sideDrag = 0.5; sideDrag = 0.5;
} }
if( isVersionOrNewer( YASIM_VERSION_32 ) ) { if( isVersionOrNewer( YASIM_VERSION_32 ) ) {
s->setXDrag(f->_cx); s->setXDrag(f->_cx);
} }
s->setYDrag(sideDrag*f->_cy); s->setYDrag(sideDrag*f->_cy);
s->setZDrag(sideDrag*f->_cz); s->setZDrag(sideDrag*f->_cz);
if( isVersionOrNewer( YASIM_VERSION_32 ) ) { if( isVersionOrNewer( YASIM_VERSION_32 ) ) {
s->setDragCoefficient(scale*segWgt); s->setDragCoefficient(scale*segWgt);
} else { } else {
s->setDragCoefficient(scale*segWgt*f->_cx); s->setDragCoefficient(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
@ -409,8 +425,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);
@ -503,38 +519,38 @@ void Airplane::compile()
// The Wing objects // The Wing objects
if (_wing) if (_wing)
{ {
if (baseN != 0) { if (baseN != 0) {
_wingsN = baseN->getChild("wing", 0, true); _wingsN = baseN->getChild("wing", 0, true);
_wing->setPropertyNode(_wingsN); _wing->setPropertyNode(_wingsN);
} }
aeroWgt += compileWing(_wing); aeroWgt += compileWing(_wing);
// convert % to absolute x coordinates // convert % to absolute x coordinates
_cgDesiredFront = _wing->getMACx() - _wing->getMACLength()*_cgDesiredMin; _cgDesiredFront = _wing->getMACx() - _wing->getMACLength()*_cgDesiredMin;
_cgDesiredAft = _wing->getMACx() - _wing->getMACLength()*_cgDesiredMax; _cgDesiredAft = _wing->getMACx() - _wing->getMACLength()*_cgDesiredMax;
if (baseN != 0) { if (baseN != 0) {
SGPropertyNode_ptr n = fgGetNode("/fdm/yasim/model", true); SGPropertyNode_ptr n = fgGetNode("/fdm/yasim/model", true);
n->getNode("cg-x-range-front", true)->setFloatValue(_cgDesiredFront); n->getNode("cg-x-range-front", true)->setFloatValue(_cgDesiredFront);
n->getNode("cg-x-range-aft", true)->setFloatValue(_cgDesiredAft); n->getNode("cg-x-range-aft", true)->setFloatValue(_cgDesiredAft);
} }
} }
if (_tail) if (_tail)
{ {
if (baseN != 0) { if (baseN != 0) {
_wingsN = baseN->getChild("tail", 0, true); _wingsN = baseN->getChild("tail", 0, true);
_tail->setPropertyNode(_wingsN); _tail->setPropertyNode(_wingsN);
} }
aeroWgt += compileWing(_tail); aeroWgt += compileWing(_tail);
} }
int i; int i;
for(i=0; i<_vstabs.size(); i++) for(i=0; i<_vstabs.size(); i++)
{ {
Wing* vs = (Wing*)_vstabs.get(i); Wing* vs = (Wing*)_vstabs.get(i);
if (baseN != 0) { if (baseN != 0) {
_wingsN = baseN->getChild("stab", i, true); _wingsN = baseN->getChild("stab", i, true);
vs->setPropertyNode(_wingsN); vs->setPropertyNode(_wingsN);
} }
aeroWgt += compileWing(vs); aeroWgt += compileWing(vs);
} }
// The fuselage(s) // The fuselage(s)
@ -715,15 +731,15 @@ 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++) {
ControlSetting* c = (ControlSetting*)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();
} }
/// Helper for solve() /// Helper for solve()
@ -801,11 +817,11 @@ void Airplane::applyDragFactor(float factor)
} }
} }
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->setDragCoefficient(wr->surf->getDragCoefficient() * applied); wr->surf->setDragCoefficient(wr->surf->getDragCoefficient() * 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->setDragCoefficient(gr->surf->getDragCoefficient() * applied); gr->surf->setDragCoefficient(gr->surf->getDragCoefficient() * applied);
} }
} }

View file

@ -37,9 +37,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,9 +59,9 @@ 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 addApproachControl(const char* prop, float val);
void addCruiseControl(int control, float val); void addCruiseControl(const char* prop, float val);
void addSolutionWeight(bool approach, int idx, float wgt); void addSolutionWeight(bool approach, int idx, float wgt);
@ -135,7 +135,7 @@ private:
float mass; float mass;
}; };
struct ControlSetting { struct ControlSetting {
int control; int propHandle;
float val; float val;
}; };
struct WeightRec { struct WeightRec {

View file

@ -2,7 +2,8 @@
# include "config.h" # include "config.h"
#endif #endif
#include "yasim-common.hpp" #include <cstring>
#include "Jet.hpp" #include "Jet.hpp"
#include "Thruster.hpp" #include "Thruster.hpp"
#include "PropEngine.hpp" #include "PropEngine.hpp"
@ -42,16 +43,18 @@ 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, Control control, 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, control, object, options); addMapping(prop, control, id, options);
int inputPropHandle = getPropertyHandle(prop);
// The one we just added is last in the list (ugly, awful hack!) // The one we just added is last in the list (ugly, awful hack!)
Vector* maps = (Vector*)_inputs.get(input); Vector* maps = (Vector*)_inputs.get(inputPropHandle);
MapRec* m = (MapRec*)maps->get(maps->size() - 1); MapRec* m = (MapRec*)maps->get(maps->size() - 1);
m->src0 = src0; m->src0 = src0;
@ -61,16 +64,21 @@ void ControlMap::addMapping(int input, Control control, void* object, int option
} }
/** /**
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, Control control, void* object, int options) void ControlMap::addMapping(const char* prop, Control control, ObjectID id, int options)
{ {
int inputPropHandle = getPropertyHandle(prop);
// See if the output object already exists // See if the output object already exists
OutRec* out {nullptr}; OutRec* out {nullptr};
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 == object && o->control == control) { if(o->oid.object == id.object && o->oid.subObj == id.subObj
&& o->control == control)
{
out = o; out = o;
break; break;
} }
@ -80,7 +88,7 @@ void ControlMap::addMapping(int input, Control control, void* object, int option
if(out == nullptr) { if(out == nullptr) {
out = new OutRec(); out = new OutRec();
out->control = control; out->control = control;
out->object = object; out->oid = id;
_outputs.add(out); _outputs.add(out);
} }
@ -95,7 +103,7 @@ void ControlMap::addMapping(int input, Control control, void* object, int option
map->src0 = map->dst0 = rangeMin(control); 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);
} }
@ -124,14 +132,15 @@ void ControlMap::setInput(int input, float val)
} }
} }
int ControlMap::getOutputHandle(void* obj, Control control) 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->control == control) if(o->oid.object == id.object && o->oid.subObj == id.subObj
&& o->control == control)
return i; return i;
} }
fprintf(stderr, "ControlMap::getOutputHandle cannot find *%d, control %d \n", obj, control); fprintf(stderr, "ControlMap::getOutputHandle cannot find *%ld, control %d \n", (long)id.object, control);
return -1; return -1;
} }
@ -153,27 +162,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.
@ -194,59 +204,135 @@ 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->control) { 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)->setFlapPos(WING_SLAT,lval); break; case PROPPITCH:
case FLAP0: ((Wing*)obj)->setFlapPos(WING_FLAP0, lval, rval); break; ((PropEngine*)obj)->setPropPitch(lval);
case FLAP0EFFECTIVENESS: ((Wing*)obj)->setFlapEffectiveness(WING_FLAP0,lval); break; break;
case FLAP1: ((Wing*)obj)->setFlapPos(WING_FLAP1,lval, rval); break; case PROPFEATHER:
case FLAP1EFFECTIVENESS: ((Wing*)obj)->setFlapEffectiveness(WING_FLAP1,lval); break; ((PropEngine*)obj)->setPropFeather((int)lval);
case SPOILER: ((Wing*)obj)->setFlapPos(WING_SPOILER, 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:
case INCIDENCE:
break;
}
} }
} }
@ -283,17 +369,17 @@ float ControlMap::rangeMax(Control control)
} }
/// register property name, return ID (int) /// register property name, return ID (int)
int ControlMap::propertyHandle(const char* name) int ControlMap::getPropertyHandle(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);
@ -306,52 +392,60 @@ int ControlMap::propertyHandle(const char* name)
ControlMap::Control ControlMap::parseControl(const char* name) ControlMap::Control ControlMap::parseControl(const char* name)
{ {
if(eq(name, "THROTTLE")) return THROTTLE; if(!strcmp(name, "THROTTLE")) return THROTTLE;
if(eq(name, "MIXTURE")) return MIXTURE; if(!strcmp(name, "MIXTURE")) return MIXTURE;
if(eq(name, "CONDLEVER")) return CONDLEVER; if(!strcmp(name, "CONDLEVER")) return CONDLEVER;
if(eq(name, "STARTER")) return STARTER; if(!strcmp(name, "STARTER")) return STARTER;
if(eq(name, "MAGNETOS")) return MAGNETOS; if(!strcmp(name, "MAGNETOS")) return MAGNETOS;
if(eq(name, "ADVANCE")) return ADVANCE; if(!strcmp(name, "ADVANCE")) return ADVANCE;
if(eq(name, "REHEAT")) return REHEAT; if(!strcmp(name, "REHEAT")) return REHEAT;
if(eq(name, "BOOST")) return BOOST; if(!strcmp(name, "BOOST")) return BOOST;
if(eq(name, "VECTOR")) return VECTOR; if(!strcmp(name, "VECTOR")) return VECTOR;
if(eq(name, "PROP")) return PROP; if(!strcmp(name, "PROP")) return PROP;
if(eq(name, "BRAKE")) return BRAKE; if(!strcmp(name, "BRAKE")) return BRAKE;
if(eq(name, "STEER")) return STEER; if(!strcmp(name, "STEER")) return STEER;
if(eq(name, "EXTEND")) return EXTEND; if(!strcmp(name, "EXTEND")) return EXTEND;
if(eq(name, "HEXTEND")) return HEXTEND; if(!strcmp(name, "HEXTEND")) return HEXTEND;
if(eq(name, "LEXTEND")) return LEXTEND; if(!strcmp(name, "LEXTEND")) return LEXTEND;
if(eq(name, "LACCEL")) return LACCEL; if(!strcmp(name, "LACCEL")) return LACCEL;
if(eq(name, "INCIDENCE")) return INCIDENCE; if(!strcmp(name, "INCIDENCE")) return INCIDENCE;
if(eq(name, "FLAP0")) return FLAP0; if(!strcmp(name, "FLAP0")) return FLAP0;
if(eq(name, "FLAP0EFFECTIVENESS")) return FLAP0EFFECTIVENESS; if(!strcmp(name, "FLAP0EFFECTIVENESS")) return FLAP0EFFECTIVENESS;
if(eq(name, "FLAP1")) return FLAP1; if(!strcmp(name, "FLAP1")) return FLAP1;
if(eq(name, "FLAP1EFFECTIVENESS")) return FLAP1EFFECTIVENESS; if(!strcmp(name, "FLAP1EFFECTIVENESS")) return FLAP1EFFECTIVENESS;
if(eq(name, "SLAT")) return SLAT; if(!strcmp(name, "SLAT")) return SLAT;
if(eq(name, "SPOILER")) return SPOILER; if(!strcmp(name, "SPOILER")) return SPOILER;
if(eq(name, "CASTERING")) return CASTERING; if(!strcmp(name, "CASTERING")) return CASTERING;
if(eq(name, "PROPPITCH")) return PROPPITCH; if(!strcmp(name, "PROPPITCH")) return PROPPITCH;
if(eq(name, "PROPFEATHER")) return PROPFEATHER; if(!strcmp(name, "PROPFEATHER")) return PROPFEATHER;
if(eq(name, "COLLECTIVE")) return COLLECTIVE; if(!strcmp(name, "COLLECTIVE")) return COLLECTIVE;
if(eq(name, "CYCLICAIL")) return CYCLICAIL; if(!strcmp(name, "CYCLICAIL")) return CYCLICAIL;
if(eq(name, "CYCLICELE")) return CYCLICELE; if(!strcmp(name, "CYCLICELE")) return CYCLICELE;
if(eq(name, "TILTROLL")) return TILTROLL; if(!strcmp(name, "TILTROLL")) return TILTROLL;
if(eq(name, "TILTPITCH")) return TILTPITCH; if(!strcmp(name, "TILTPITCH")) return TILTPITCH;
if(eq(name, "TILTYAW")) return TILTYAW; if(!strcmp(name, "TILTYAW")) return TILTYAW;
if(eq(name, "ROTORGEARENGINEON")) return ROTORENGINEON; if(!strcmp(name, "ROTORGEARENGINEON")) return ROTORENGINEON;
if(eq(name, "ROTORBRAKE")) return ROTORBRAKE; if(!strcmp(name, "ROTORBRAKE")) return ROTORBRAKE;
if(eq(name, "ROTORENGINEMAXRELTORQUE")) return ROTORENGINEMAXRELTORQUE; if(!strcmp(name, "ROTORENGINEMAXRELTORQUE")) return ROTORENGINEMAXRELTORQUE;
if(eq(name, "ROTORRELTARGET")) return ROTORRELTARGET; if(!strcmp(name, "ROTORRELTARGET")) return ROTORRELTARGET;
if(eq(name, "ROTORBALANCE")) return ROTORBALANCE; if(!strcmp(name, "ROTORBALANCE")) return ROTORBALANCE;
if(eq(name, "REVERSE_THRUST")) return REVERSE_THRUST; if(!strcmp(name, "REVERSE_THRUST")) return REVERSE_THRUST;
if(eq(name, "WASTEGATE")) return WASTEGATE; if(!strcmp(name, "WASTEGATE")) return WASTEGATE;
if(eq(name, "WINCHRELSPEED")) return WINCHRELSPEED; if(!strcmp(name, "WINCHRELSPEED")) return WINCHRELSPEED;
if(eq(name, "HITCHOPEN")) return HITCHOPEN; if(!strcmp(name, "HITCHOPEN")) return HITCHOPEN;
if(eq(name, "PLACEWINCH")) return PLACEWINCH; if(!strcmp(name, "PLACEWINCH")) return PLACEWINCH;
if(eq(name, "FINDAITOW")) return FINDAITOW; if(!strcmp(name, "FINDAITOW")) return FINDAITOW;
SG_LOG(SG_FLIGHT,SG_ALERT,"Unrecognized control type '" << name SG_LOG(SG_FLIGHT,SG_ALERT,"Unrecognized control type '" << name
<< "' in YASim aircraft description."); << "' in YASim aircraft description.");
exit(1); exit(1);
} }
ControlMap::ObjectID ControlMap::getObjectID(void* object, int subObj)
{
ObjectID o;
o.object = object;
o.subObj = subObj;
return o;
}
} // namespace yasim } // namespace yasim

View file

@ -62,26 +62,32 @@ public:
OPT_SQUARE = 0x04 OPT_SQUARE = 0x04
}; };
struct PropHandle { char* name; int handle; }; struct PropHandle {
char* name {nullptr};
int handle {0};
};
struct ObjectID {
void* object {nullptr};
int subObj {0};
};
// map control name to int (enum) // map control name to int (enum)
Control parseControl(const char* name); Control parseControl(const char* name);
// Adds a mapping to between input handle and a particular setting // create ID from object and optional sub index (e.g. for wing section)
// on an output object. The value of output MUST match the type ObjectID getObjectID(void* object, int subObj = 0);
// of object!
void addMapping(int input, Control control, void* object, int options=0); // add input property for a control to an object
void addMapping(const char* prop, Control control, ObjectID id, int options = 0);
// An additional form to specify a mapping range. Input values // same with limits. Input values are clamped to [src0:src1] and then mapped to
// outside of [src0:src1] are clamped, and are then mapped to // [dst0:dst1] before being set on the objects control.
// [dst0:dst1] before being set on the object. void addMapping(const char* prop, Control control, ObjectID id, int options, float src0, float src1, float dst0, float dst1);
void addMapping(int input, Control control, 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);
@ -97,7 +103,7 @@ public:
// 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, Control control); 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.
@ -110,14 +116,14 @@ 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 getPropertyHandle(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)); }
private: private:
struct OutRec { struct OutRec {
Control control; Control control;
void* object {nullptr}; ObjectID oid;
Vector maps; Vector maps;
float oldL {0}; float oldL {0};
float oldR {0}; float oldR {0};

File diff suppressed because it is too large Load diff

View file

@ -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"
@ -50,22 +49,49 @@ private:
float max {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 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);
int attri(XMLAttributes* atts, const char* attr); void parseTurbineEngine(const XMLAttributes* a);
int attri(XMLAttributes* atts, const char* attr, int def); void parsePistonEngine(const XMLAttributes* a);
float attrf(XMLAttributes* atts, const char* attr); void parsePropeller(const XMLAttributes* a);
float attrf(XMLAttributes* atts, const char* attr, float def); void parseThruster(const XMLAttributes* a);
double attrd(XMLAttributes* atts, const char* attr); void parseJet(const XMLAttributes* a);
double attrd(XMLAttributes* atts, const char* attr, double def); void parseHitch(const XMLAttributes* a);
bool attrb(XMLAttributes* atts, const char* attr); void parseTow(const XMLAttributes* a);
void parseWinch(const XMLAttributes* a);
void parseGear(const XMLAttributes* a);
void parseHook(const XMLAttributes* a);
void parseLaunchbar(const XMLAttributes* a);
void parseFuselage(const XMLAttributes* a);
void parseTank(const XMLAttributes* a);
void parseBallast(const XMLAttributes* a);
void parseControlSetting(const XMLAttributes* a);
void parseControlIn(const XMLAttributes* a);
void parseControlOut(const XMLAttributes* a);
void parseControlSpeed(const XMLAttributes* a);
int attri(const XMLAttributes* atts, const char* attr);
int attri(const XMLAttributes* atts, const char* attr, int def);
float attrf(const XMLAttributes* atts, const char* attr);
float attrf(const XMLAttributes* atts, const char* attr, float def);
void attrf_xyz(const XMLAttributes* atts, float* out);
double attrd(const XMLAttributes* atts, const char* attr);
double attrd(const XMLAttributes* atts, const char* attr, double def);
bool attrb(const XMLAttributes* atts, const char* attr);
// The core Airplane object we manage. // The core Airplane object we manage.
Airplane _airplane; Airplane _airplane;
@ -89,6 +115,7 @@ private:
void* _currObj {nullptr}; void* _currObj {nullptr};
bool _cruiseCurr {false}; bool _cruiseCurr {false};
int _nextEngine {0}; int _nextEngine {0};
int _wingSection {0};
class FuelProps class FuelProps
{ {

View file

@ -67,8 +67,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 +78,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);
}
} }
} }

View file

@ -5,81 +5,95 @@
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)
_rootChordLength(chord),
_length(length),
_taper(taper),
_sweepAngleCenterLine(sweep),
_dihedral(dihedral),
_twist(twist)
{ {
Math::set3(base, _base);
_meanChord = _rootChordLength*(_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)
{
_incidence = incidence;
for(int i=0; i<_surfs.size(); i++)
((SurfRec*)_surfs.get(i))->surface->setIncidence(incidence);
}
void Wing::setFlapParams(WingFlaps i, FlapParams fp)
{
_flapParams[i] = fp;
}
void Wing::setFlapPos(WingFlaps f,float lval, float rval)
{
float min {-1};
if (f == WING_SPOILER || f == WING_SLAT) {
min = 0;
}
lval = Math::clamp(lval, min, 1);
rval = Math::clamp(rval, min, 1);
for(int i=0; i<_flapSurfs[f].size(); i++) {
switch (f) {
case WING_FLAP0:
case WING_FLAP1:
((Surface*)_flapSurfs[f].get(i))->setFlapPos(lval);
if(_mirror) ((Surface*)_flapSurfs[f].get(++i))->setFlapPos(rval);
break;
case WING_SLAT:
((Surface*)_flapSurfs[f].get(i))->setSlatPos(lval);
break;
case WING_SPOILER:
((Surface*)_flapSurfs[f].get(i))->setSpoilerPos(lval);
if(_mirror) ((Surface*)_flapSurfs[f].get(++i))->setSpoilerPos(rval);
break;
} }
} }
} }
void Wing::setFlapEffectiveness(WingFlaps f, float lval) int Wing::addWingSection(float* base, float chord, float wingLength, float taper,
float sweep, float dihedral, float twist, float camber,
float idrag, float incidence)
{ {
lval = Math::clamp(lval, 1, 10); WingSection* ws = new WingSection;
for(int i=0; i<_flapSurfs[f].size(); i++) { if (_sections.size() == 0) {
((Surface*)_flapSurfs[f].get(i))->setFlapEffectiveness(lval); // first section
Math::set3(base, _base);
ws->_rootChord = _float2chord(base, chord);
ws->_sectionIncidence = incidence;
} else {
WingSection* prev = (WingSection*)_sections.get(_sections.size()-1);
//use old wing tip instead of base argument
ws->_rootChord = prev->_tipChord;
ws->_sectionIncidence = prev->_sectionIncidence + prev->_twist;
} }
ws->_length = wingLength;
ws->_taper = taper;
ws->_sweepAngleCenterLine = sweep;
ws->_dihedral = dihedral;
ws->_twist = twist;
ws->_camber = camber;
ws->_inducedDrag = idrag;
ws->calculateGeometry();
int idx = _sections.add(ws);
// first / only section
if (idx == 0) {
_mac = ws->getMAC();
_wingspan = ws->_wingspan;
_area = ws->getArea();
_meanChord = ws->_meanChord;
}
// append section: Calculate wing MAC from MACs of section and prev wing
else {
_mac = Wing::calculateMAC(_mac, ws->getMAC());
_wingspan += ws->_wingspan;
_area += ws->getArea();
_meanChord = _meanChord * ws->_meanChord * 0.5f;
}
_chord2float(ws->_tipChord, _tip);
return idx;
} }
void Wing::calculateWingCoordinateSystem() { Chord Wing::_float2chord(float* pos, float lenght)
{
Chord c;
c.x = pos[0];
c.y = pos[1];
c.z = pos[2];
c.length = lenght;
return c;
}
void Wing::_chord2float(Chord c, float* pos)
{
pos[0] = c.x;
pos[1] = c.y;
pos[2] = c.z;
}
void Wing::WingSection::calculateGeometry()
{
_meanChord = _rootChord.length*(_taper+1)*0.5f;
calculateWingCoordinateSystem();
calculateTipChord();
calculateSpan();
calculateMAC();
}
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.
@ -110,168 +124,257 @@ 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 = _rootChordLength*(0.5+_taper)/(3*_rootChordLength*(1+_taper)); const float commonFactor = _rootChord.length*(0.5+_taper)/(3*_rootChord.length*(1+_taper));
_mac.length = _rootChordLength-(2*_rootChordLength*(1-_taper)*commonFactor); _mac.length = _rootChord.length-(2*_rootChord.length*(1-_taper)*commonFactor);
_mac.y = _netSpan*commonFactor; _mac.y = Math::abs(2*(_tipChord.y-_rootChord.y))*commonFactor;
_mac.x = _base[0]-Math::tan(_sweepAngleCenterLine) * _mac.y + _mac.length/2; _mac.x = _rootChord.x-Math::tan(_sweepAngleCenterLine)*_mac.y + _mac.length/2;
_mac.y += _rootChord.y;
} }
Chord Wing::calculateMAC(Chord root, Chord tip) float Wing::WingSection::calculateSweepAngleLeadingEdge()
{
assert(root.length > 0);
//const float 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;
m.x = root.x - (root.x - tip.x)*(root.y - m.y)/(root.y - tip.y);
return m;
}
float Wing::calculateSweepAngleLeadingEdge()
{ {
if (_length == 0) { if (_length == 0) {
return 0; return 0;
} }
return Math::atan( return Math::atan(
(sin(_sweepAngleCenterLine)+(1-_taper)*_rootChordLength/(2*_length)) / (sin(_sweepAngleCenterLine)+(1-_taper)*_rootChord.length/(2*_length)) /
cos(_sweepAngleCenterLine) cos(_sweepAngleCenterLine)
); );
} }
void Wing::WingSection::setIncidence(float incidence)
{
//update surface
for(int i=0; i<_surfs.size(); i++)
((SurfRec*)_surfs.get(i))->surface->setIncidence(incidence + _sectionIncidence);
}
// root and tip (x,y) coordinates are on leading edge
Chord Wing::calculateMAC(Chord root, Chord tip)
{
assert(root.length > 0);
//taper = tip.length / root.length;
const float commonFactor = (root.length*0.5+tip.length)/(3*(root.length+tip.length));
Chord m;
m.length = root.length-(2*(root.length - tip.length)*commonFactor);
m.y = Math::abs(2*(tip.y - root.y))*commonFactor + root.y;
m.x = root.x - (root.x - tip.x)*(root.y - m.y)/(root.y - tip.y);
return m;
}
void Wing::setFlapParams(int section, WingFlaps type, FlapParams fp)
{
((WingSection*)_sections.get(section))->_flapParams[type] = fp;
}
void Wing::setSectionDrag(int section, float pdrag)
{
((WingSection*)_sections.get(section))->_dragScale = pdrag;
}
void Wing::setSectionStallParams(int section, StallParams sp)
{
((WingSection*)_sections.get(section))->_stallParams = sp;
}
void Wing::setFlapPos(WingFlaps type,float lval, float rval)
{
float min {-1};
if (type == WING_SPOILER || type == WING_SLAT) {
min = 0;
}
lval = Math::clamp(lval, min, 1);
rval = Math::clamp(rval, min, 1);
WingSection* ws;
for (int section=0; section < _sections.size(); section++)
{
ws = (WingSection*)_sections.get(section);
for(int i=0; i < ws->_flapSurfs[type].size(); i++) {
switch (type) {
case WING_FLAP0:
case WING_FLAP1:
((Surface*)ws->_flapSurfs[type].get(i))->setFlapPos(lval);
if(_mirror) ((Surface*)ws->_flapSurfs[type].get(++i))->setFlapPos(rval);
break;
case WING_SLAT:
((Surface*)ws->_flapSurfs[type].get(i))->setSlatPos(lval);
break;
case WING_SPOILER:
((Surface*)ws->_flapSurfs[type].get(i))->setSpoilerPos(lval);
if(_mirror) ((Surface*)ws->_flapSurfs[type].get(++i))->setSpoilerPos(rval);
break;
}
}
}
}
void Wing::setFlapEffectiveness(WingFlaps f, float lval)
{
lval = Math::clamp(lval, 1, 10);
WingSection* ws;
for (int section=0; section < _sections.size(); section++)
{
ws = (WingSection*)_sections.get(section);
for(int i=0; i<ws->_flapSurfs[f].size(); i++) {
((Surface*)ws->_flapSurfs[f].get(i))->setFlapEffectiveness(lval);
}
}
}
void Wing::compile() void Wing::compile()
{ {
// Have we already been compiled? WingSection* ws;
if(! _surfs.empty()) return; 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;
// Assemble the start/end coordinates of all control surfaces // Sort in increasing order
// and the wing itself into an array, sort them, for(int i=0; i<NUM_BOUNDS; i++) {
// and remove duplicates. This gives us the boundaries of our int minIdx = i;
// segments. float minVal = bounds[i];
const int NUM_BOUNDS {10}; for(int j=i+1; j<NUM_BOUNDS; j++) {
float bounds[NUM_BOUNDS]; if(bounds[j] < minVal) {
bounds[0] = _flapParams[WING_FLAP0].start; minIdx = j;
bounds[1] = _flapParams[WING_FLAP0].end; minVal = bounds[j];
bounds[2] = _flapParams[WING_FLAP1].start; }
bounds[3] = _flapParams[WING_FLAP1].end; }
bounds[4] = _flapParams[WING_SPOILER].start; float tmp = bounds[i];
bounds[5] = _flapParams[WING_SPOILER].end; bounds[i] = minVal; bounds[minIdx] = tmp;
bounds[6] = _flapParams[WING_SLAT].start;
bounds[7] = _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
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;
}
// Uniqify // Uniqify
float last = bounds[0]; float last = bounds[0];
int nbounds = 1; int nbounds = 1;
for(i=1; i<NUM_BOUNDS; i++) { for(int i=1; i<NUM_BOUNDS; i++) {
if(bounds[i] != last) if(bounds[i] != last)
bounds[nbounds++] = bounds[i]; bounds[nbounds++] = bounds[i];
last = bounds[i]; last = bounds[i];
} }
// Calculate a "nominal" segment length equal to an average chord, // Calculate a "nominal" segment length equal to an average chord,
// normalized to lie within 0-1 over the length of the wing. // normalized to lie within 0-1 over the length of the wing.
float segLen = _meanChord / _length; float segLen = ws->_meanChord / ws->_length;
// Now go through each boundary and make segments // Now go through each boundary and make segments
for(i=0; i<(nbounds-1); i++) { for(int i=0; i<(nbounds-1); i++) {
float start = bounds[i]; float start = bounds[i];
float end = bounds[i+1]; float end = bounds[i+1];
float mid = (start+end)/2; float mid = (start+end)/2;
bool hasFlap0=0, hasFlap1=0, hasSlat=0, hasSpoiler=0; bool hasFlap0=0, hasFlap1=0, hasSlat=0, hasSpoiler=0;
if(_flapParams[WING_FLAP0].start < mid && mid < _flapParams[WING_FLAP0].end) hasFlap0 = 1; if(ws->_flapParams[WING_FLAP0].start < mid && mid < ws->_flapParams[WING_FLAP0].end)
if(_flapParams[WING_FLAP1].start < mid && mid < _flapParams[WING_FLAP1].end) hasFlap1 = 1; hasFlap0 = 1;
if(_flapParams[WING_SLAT].start < mid && mid < _flapParams[WING_SLAT].end) hasSlat = 1; if(ws->_flapParams[WING_FLAP1].start < mid && mid < ws->_flapParams[WING_FLAP1].end)
if(_flapParams[WING_SPOILER].start < mid && mid < _flapParams[WING_SPOILER].end) hasSpoiler = 1; 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 // FIXME: Should probably detect an error here if both flap0
// and flap1 are set. Right now flap1 overrides. // and flap1 are set. Right now flap1 overrides.
int nSegs = (int)Math::ceil((end-start)/segLen); int nSegs = (int)Math::ceil((end-start)/segLen);
if (_twist != 0 && nSegs < 8) // more segments if twisted if (ws->_twist != 0 && nSegs < 8) // more segments if twisted
nSegs = 8; nSegs = 8;
float segWid = _length * (end - start)/nSegs; float segWid = ws->_length * (end - start)/nSegs;
int j; for(int j=0; j<nSegs; j++) {
for(j=0; j<nSegs; j++) { float frac = start + (j+0.5f) * (end-start)/nSegs;
float frac = start + (j+0.5f) * (end-start)/nSegs; float pos[3];
float pos[3]; interp(_base, _tip, frac, pos);
interp(_base, _tip, frac, pos);
float chord = _rootChordLength * (1 - (1-_taper)*frac); float chord = ws->_rootChord.length * (1 - (1-ws->_taper)*frac);
float weight = chord * segWid; float weight = chord * segWid;
float twist = _twist * frac; float twist = ws->_twist * frac;
Surface *s = newSurface(pos, _orient, chord, ws->newSurface(_version, pos, ws->_orient, chord, hasFlap0, hasFlap1, hasSlat, hasSpoiler, weight, twist);
hasFlap0, hasFlap1, hasSlat, hasSpoiler);
addSurface(s, weight, twist); if(_mirror) {
pos[1] = -pos[1];
if(_mirror) { ws->newSurface(_version, pos, ws->_rightOrient, chord,
pos[1] = -pos[1]; hasFlap0, hasFlap1, hasSlat, hasSpoiler, weight, twist);
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);
} }
// Last of all, re-set the incidence in case setIncidence() was
// called before we were compiled.
setIncidence(_incidence);
writeInfoToProptree(); writeInfoToProptree();
} }
void Wing::multiplyLiftRatio(float factor) void Wing::multiplyLiftRatio(float factor)
{ {
setLiftRatio(_liftRatio * factor); WingSection* ws;
for (int section=0; section < _sections.size(); section++)
{
ws = (WingSection*)_sections.get(section);
ws->multiplyLiftRatio(factor);
}
} }
void Wing::addSurface(Surface* s, float weight, float twist) void Wing::multiplyDragCoefficient(float factor)
{ {
SurfRec *sr = new SurfRec(); WingSection* ws;
sr->surface = s; for (int section=0; section < _sections.size(); section++)
sr->weight = weight; {
s->setDragCoefficient(sr->weight); ws = (WingSection*)_sections.get(section);
s->setTwist(twist); ws->multiplyDragCoefficient(factor);
_surfs.add(sr); }
} }
void Wing::setDragCoefficient(float scale) 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++) {
@ -280,20 +383,24 @@ void Wing::setDragCoefficient(float scale)
} }
} }
void Wing::multiplyDragCoefficient(float factor) void Wing::WingSection::multiplyDragCoefficient(float factor)
{ {
setDragCoefficient(_dragScale * factor); setDragCoefficient(_dragScale * factor);
} }
void Wing::setLiftRatio(float ratio) 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->setZDrag(ratio);
} }
Surface* Wing::newSurface(float* pos, float* orient, float chord, void Wing::WingSection::multiplyLiftRatio(float factor)
bool hasFlap0, bool hasFlap1, bool hasSlat, bool hasSpoiler) {
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); Surface* s = new Surface(_version);
@ -347,7 +454,12 @@ Surface* Wing::newSurface(float* pos, float* orient, float chord,
s->setInducedDrag(_inducedDrag); s->setInducedDrag(_inducedDrag);
return s; SurfRec *sr = new SurfRec();
sr->surface = s;
sr->weight = weight;
s->setDragCoefficient(sr->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)
@ -361,30 +473,40 @@ void Wing::writeInfoToProptree()
{ {
if (_wingN == nullptr) if (_wingN == nullptr)
return; 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-x", true)->setFloatValue(_tip[0]);
_wingN->getNode("tip-y", true)->setFloatValue(_tip[1]); _wingN->getNode("tip-y", true)->setFloatValue(_tip[1]);
_wingN->getNode("tip-z", true)->setFloatValue(_tip[2]); _wingN->getNode("tip-z", true)->setFloatValue(_tip[2]);
_wingN->getNode("base-x", true)->setFloatValue(_base[0]); _wingN->getNode("base-x", true)->setFloatValue(_base[0]);
_wingN->getNode("base-y", true)->setFloatValue(_base[1]); _wingN->getNode("base-y", true)->setFloatValue(_base[1]);
_wingN->getNode("base-z", true)->setFloatValue(_base[2]); _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-span", true)->setFloatValue(_wingspan);
_wingN->getNode("wing-area", true)->setFloatValue(_wingspan*_meanChord); _wingN->getNode("wing-area", true)->setFloatValue(_wingspan*_meanChord);
_wingN->getNode("aspect-ratio", true)->setFloatValue(_aspectRatio); _wingN->getNode("aspect-ratio", true)->setFloatValue(_aspectRatio);
_wingN->getNode("standard-mean-chord", true)->setFloatValue(_meanChord); _wingN->getNode("standard-mean-chord", true)->setFloatValue(_meanChord);
_wingN->getNode("mac", true)->setFloatValue(_mac.length); _wingN->getNode("mac", true)->setFloatValue(_mac.length);
_wingN->getNode("mac-x", true)->setFloatValue(_mac.x); _wingN->getNode("mac-x", true)->setFloatValue(_mac.x);
_wingN->getNode("mac-y", true)->setFloatValue(_base[1]+_mac.y); _wingN->getNode("mac-y", true)->setFloatValue(_mac.y);
float wgt = 0; float wgt = 0;
float dragSum = 0; float dragSum = 0;
for(int surf=0; surf < numSurfaces(); surf++) {
Surface* s = (Surface*)getSurface(surf);
float drag = s->getDragCoefficient();
dragSum += drag;
float mass = getSurfaceWeight(surf); for (int section=0; section < _sections.size(); section++) {
mass = mass * Math::sqrt(mass); ws = (WingSection*)_sections.get(section);
wgt += mass; for (int surf=0; surf < ws->numSurfaces(); surf++) {
Surface* s = ws->getSurface(surf);
float drag = s->getDragCoefficient();
dragSum += drag;
float mass = ws->getSurfaceWeight(surf);
mass = mass * Math::sqrt(mass);
wgt += mass;
}
} }
_wingN->getNode("weight", true)->setFloatValue(wgt); _wingN->getNode("weight", true)->setFloatValue(wgt);
_wingN->getNode("drag", true)->setFloatValue(dragSum); _wingN->getNode("drag", true)->setFloatValue(dragSum);
@ -393,21 +515,25 @@ void Wing::writeInfoToProptree()
float Wing::updateModel(Model* model) float Wing::updateModel(Model* model)
{ {
float wgt = 0; float wgt = 0;
for(int surf=0; surf < numSurfaces(); surf++) { WingSection* ws;
Surface* s = (Surface*)getSurface(surf); for (int section=0; section < _sections.size(); section++) {
model->addSurface(s); ws = (WingSection*)_sections.get(section);
for(int surf=0; surf < ws->numSurfaces(); surf++) {
Surface* s = ws->getSurface(surf);
model->addSurface(s);
float mass = getSurfaceWeight(surf); float mass = ws->getSurfaceWeight(surf);
mass = mass * Math::sqrt(mass); mass = mass * Math::sqrt(mass);
wgt += mass; wgt += mass;
float pos[3]; float pos[3];
s->getPosition(pos); s->getPosition(pos);
int mid = model->getBody()->addMass(mass, pos, true); int mid = model->getBody()->addMass(mass, pos, true);
if (_wingN != nullptr) { if (_wingN != nullptr) {
SGPropertyNode_ptr n = _wingN->getNode("surfaces", true)->getChild("surface", s->getID(), true); SGPropertyNode_ptr n = _wingN->getNode("surfaces", true)->getChild("surface", s->getID(), true);
n->getNode("drag", true)->setFloatValue(s->getDragCoefficient()); n->getNode("drag", true)->setFloatValue(s->getDragCoefficient());
n->getNode("mass-id", true)->setIntValue(mid); n->getNode("mass-id", true)->setIntValue(mid);
}
} }
} }
return wgt; return wgt;

View file

@ -29,6 +29,7 @@ struct StallParams {
struct Chord { struct Chord {
float x {0}; float x {0};
float y {0}; float y {0};
float z {0};
float length {0}; float length {0};
}; };
@ -39,133 +40,132 @@ enum WingFlaps {
WING_SLAT, WING_SLAT,
}; };
// FIXME: need to handle "inverted" controls for mirrored wings.
class Wing { class Wing {
SGPropertyNode_ptr _wingN {nullptr}; SGPropertyNode_ptr _wingN {nullptr};
struct SurfRec {
Surface* surface;
float weight;
};
struct WingSection {
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};
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 _rootChordLength; };
// fraction of chord at wing tip, 0..1
float getTaper() const { return _taper; };
// radians
float getSweep() const { return _sweepAngleCenterLine; };
// radians, positive is "up"
void setDihedral(float dihedral) { _dihedral = dihedral; }
float getDihedral() const { return _dihedral; };
void setIncidence(float incidence);
// parameters for stall curve
void setStallParams(StallParams sp) { _stallParams = sp; }
void setCamber(float camber) { _camber = camber; }
void setInducedDrag(float drag) { _inducedDrag = drag; }
void setFlapParams(WingFlaps i, FlapParams fp);
// propergate the control axes value for the sub-surfaces static Chord calculateMAC(Chord root, Chord tip);
void setFlapPos(WingFlaps i, float lval, float rval = 0);
void setFlapEffectiveness(WingFlaps f, 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; };
Chord getMAC() const { return _mac; };
float getMACLength() const { return _mac.length; }; // get length of MAC float getMACLength() const { return _mac.length; }; // get length of MAC
float getMACx() const { return _mac.x; }; // get x-coord of MAC float getMACx() const { return _mac.x; }; // get x-coord of MAC leading edge
float getMACy() const { return _base[1]+_mac.y; }; // get y-coord of MAC 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
// arbitrary.
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 setPropertyNode(SGPropertyNode_ptr n) { _wingN = n; }; void setPropertyNode(SGPropertyNode_ptr n) { _wingN = n; };
float updateModel(Model* model); float updateModel(Model* model);
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();
static Chord calculateMAC(Chord root, Chord tip);
float calculateSweepAngleLeadingEdge();
void addSurface(Surface* s, float weight, float twist);
void writeInfoToProptree();
struct SurfRec { Surface * surface; float weight; };
// all surfaces of this wing
Vector _surfs;
// surfaces having a certain type of flap (flap, slat, spoiler)
Vector _flapSurfs[sizeof(WingFlaps)];
Version * _version;
bool _mirror {false};
float _base[3] {0,0,0};
float _rootChordLength {0};
// 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};
// calculated from above
float _orient[9];
float _rightOrient[9];
float _tip[3] {0,0,0};
float _meanChord {0}; // std. mean chord
Chord _mac; // mean aerodynamic chord (x,y) leading edge
float _netSpan {0};
float _wingspan {0};
float _aspectRatio {1};
StallParams _stallParams;
float _twist {0};
float _camber {0};
float _incidence {0};
float _inducedDrag {1};
float _dragScale {1};
float _liftRatio {1};
FlapParams _flapParams[sizeof(WingFlaps)];
}; };

View file

@ -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"

View file

@ -1,23 +1,5 @@
#include "yasim-common.hpp" #include "yasim-common.hpp"
namespace yasim { namespace yasim {
/// duplicate null-terminated string
char* 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 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);
}
}; //namespace yasim }; //namespace yasim

View file

@ -31,10 +31,6 @@ namespace yasim {
static const float NM2FTLB = (1/(LBS2N*FT2M)); static const float NM2FTLB = (1/(LBS2N*FT2M));
static const float SLUG2KG = 14.59390f; static const float SLUG2KG = 14.59390f;
char* dup(const char* s);
bool eq(const char* a, const char* b);
}; //namespace yasim }; //namespace yasim
#endif // ifndef _YASIM_COMMON_HPP #endif // ifndef _YASIM_COMMON_HPP

View file

@ -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()->getMACLength(); 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);