From ab381e5c013292935c598dbe80bdd092bcbdd3ff Mon Sep 17 00:00:00 2001 From: andy Date: Fri, 1 Mar 2002 06:47:28 +0000 Subject: [PATCH] Added a property output feature to ControlMap that allows arbitrary binding and scaling of control values to properties. Also added a time interpolation feature that replaces the hacked-in "retract-time" feature for the gear in a more general way (applicable to flaps, too!). Incompatibly breaks the XML syntax; get new files! --- src/FDM/YASim/Airplane.cpp | 44 +++++---------- src/FDM/YASim/Airplane.hpp | 6 +-- src/FDM/YASim/ControlMap.cpp | 82 ++++++++++++++++++++++++---- src/FDM/YASim/ControlMap.hpp | 25 ++++++++- src/FDM/YASim/FGFDM.cpp | 101 +++++++++++++++++++++++------------ src/FDM/YASim/FGFDM.hpp | 5 ++ src/FDM/YASim/YASim.cxx | 5 +- 7 files changed, 186 insertions(+), 82 deletions(-) diff --git a/src/FDM/YASim/Airplane.cpp b/src/FDM/YASim/Airplane.cpp index 7132772aa..c70db6671 100644 --- a/src/FDM/YASim/Airplane.cpp +++ b/src/FDM/YASim/Airplane.cpp @@ -52,6 +52,9 @@ Airplane::~Airplane() void Airplane::iterate(float dt) { + // The gear might have moved. Change their aerodynamics. + updateGearState(); + _model.iterate(); // FIXME: Consume fuel @@ -108,27 +111,12 @@ Gear* Airplane::getGear(int g) return ((GearRec*)_gears.get(g))->gear; } -void Airplane::setGearState(bool down, float dt) +void Airplane::updateGearState() { - int i; - for(i=0; i<_gears.size(); i++) { + for(int i=0; i<_gears.size(); i++) { GearRec* gr = (GearRec*)_gears.get(i); - if(gr->time == 0) { - // Non-extensible - gr->gear->setExtension(1); - gr->surf->setXDrag(1); - gr->surf->setYDrag(1); - gr->surf->setZDrag(1); - continue; - } - - float diff = dt / gr->time; - if(!down) diff = -diff; - float ext = gr->gear->getExtension() + diff; - if(ext < 0) ext = 0; - if(ext > 1) ext = 1; + float ext = gr->gear->getExtension(); - gr->gear->setExtension(ext); gr->surf->setXDrag(ext); gr->surf->setYDrag(ext); gr->surf->setZDrag(ext); @@ -236,12 +224,11 @@ int Airplane::addTank(float* pos, float cap, float density) return _tanks.add(t); } -void Airplane::addGear(Gear* gear, float transitionTime) +void Airplane::addGear(Gear* gear) { GearRec* g = new GearRec(); g->gear = gear; g->surf = 0; - g->time = transitionTime; _gears.add(g); } @@ -589,9 +576,6 @@ void Airplane::compile() // Do this after solveGear, because it creates "gear" objects that // we don't want to affect. compileContactPoints(); - - // Drop the gear (use a really big dt) - setGearState(true, 1000000); } void Airplane::solveGear() @@ -678,16 +662,13 @@ void Airplane::runCruise() Control* c = (Control*)_cruiseControls.get(i); _controls.setInput(c->control, c->val); } - _controls.applyControls(); + _controls.applyControls(1000000); // Huge dt value // The local wind float wind[3]; Math::mul3(-1, _cruiseState.v, wind); Math::vmul33(_cruiseState.orient, wind, wind); - // Gear are up (if they're non-retractable, this is a noop) - setGearState(false, 100000); - // Cruise is by convention at 50% tank capacity setFuelFraction(0.5); @@ -700,6 +681,8 @@ void Airplane::runCruise() } stabilizeThrust(); + updateGearState(); + // Precompute thrust in the model, and calculate aerodynamic forces _model.getBody()->reset(); _model.initIteration(); @@ -719,7 +702,7 @@ void Airplane::runApproach() Control* c = (Control*)_approachControls.get(i); _controls.setInput(c->control, c->val); } - _controls.applyControls(); + _controls.applyControls(1000000); // The local wind float wind[3]; @@ -729,9 +712,6 @@ void Airplane::runApproach() // Approach is by convention at 20% tank capacity setFuelFraction(0.2); - // Gear are down - setGearState(true, 100000); - // Run the thrusters until they get to a stable setting. FIXME: // this is lots of wasted work. for(i=0; i<_thrusters.size(); i++) { @@ -741,6 +721,8 @@ void Airplane::runApproach() } stabilizeThrust(); + updateGearState(); + // Precompute thrust in the model, and calculate aerodynamic forces _model.getBody()->reset(); _model.initIteration(); diff --git a/src/FDM/YASim/Airplane.hpp b/src/FDM/YASim/Airplane.hpp index cf088f1b9..9a38700a0 100644 --- a/src/FDM/YASim/Airplane.hpp +++ b/src/FDM/YASim/Airplane.hpp @@ -35,7 +35,7 @@ public: void addFuselage(float* front, float* back, float width, float taper=1, float mid=0.5); int addTank(float* pos, float cap, float fuelDensity); - void addGear(Gear* g, float transitionTime); + void addGear(Gear* g); void addThruster(Thruster* t, float mass, float* cg); void addBallast(float* pos, float mass); @@ -51,7 +51,6 @@ public: int numGear(); Gear* getGear(int g); - void setGearState(bool down, float dt); int numTanks(); void setFuelFraction(float frac); // 0-1, total amount of fuel @@ -74,7 +73,7 @@ private: struct Tank { float pos[3]; float cap; float fill; float density; int handle; }; struct Fuselage { float front[3], back[3], width, taper, mid; }; - struct GearRec { Gear* gear; Surface* surf; float wgt; float time; }; + struct GearRec { Gear* gear; Surface* surf; float wgt; }; struct ThrustRec { Thruster* thruster; int handle; float cg[3]; float mass; }; struct Control { int control; float val; }; @@ -94,6 +93,7 @@ private: void addContactPoint(float* pos); void compileContactPoints(); float normFactor(float f); + void updateGearState(); Model _model; ControlMap _controls; diff --git a/src/FDM/YASim/ControlMap.cpp b/src/FDM/YASim/ControlMap.cpp index a023d91f3..9bc74b5ec 100644 --- a/src/FDM/YASim/ControlMap.cpp +++ b/src/FDM/YASim/ControlMap.cpp @@ -63,6 +63,7 @@ void ControlMap::addMapping(int input, int type, void* object, int options) out = new OutRec(); out->type = type; out->object = object; + out->oldL = out->oldR = out->time = 0; _outputs.add(out); } @@ -73,12 +74,8 @@ void ControlMap::addMapping(int input, int type, void* object, int options) map->idx = out->maps.add(map); // The default ranges differ depending on type! - map->src1 = map->dst1 = 1; - map->src0 = map->dst0 = 0; - if(type==FLAP0 || type==FLAP1 || type==STEER) - map->src0 = map->dst0 = -1; - if(type==MAGNETOS) - map->src1 = map->dst1 = 3; + map->src1 = map->dst1 = rangeMax(type); + map->src0 = map->dst0 = rangeMin(type); // And add it to the approproate vectors. Vector* maps = (Vector*)_inputs.get(input); @@ -91,7 +88,7 @@ void ControlMap::reset() for(int i=0; i<_outputs.size(); i++) { OutRec* o = (OutRec*)_outputs.get(i); for(int j=0; jmaps.size(); j++) - ((MapRec*)o->maps.get(j))->val = 0; + ((MapRec*)(o->maps.get(j)))->val = 0; } } @@ -114,7 +111,31 @@ void ControlMap::setInput(int input, float val) } } -void ControlMap::applyControls() +int ControlMap::getOutputHandle(void* obj, int type) +{ + for(int i=0; i<_outputs.size(); i++) { + OutRec* o = (OutRec*)_outputs.get(i); + if(o->object == obj && o->type == type) + return i; + } +} + +void ControlMap::setTransitionTime(int handle, float time) +{ + ((OutRec*)_outputs.get(handle))->time = time; +} + +float ControlMap::getOutput(int handle) +{ + return ((OutRec*)_outputs.get(handle))->oldL; +} + +float ControlMap::getOutputR(int handle) +{ + return ((OutRec*)_outputs.get(handle))->oldR; +} + +void ControlMap::applyControls(float dt) { int outrec; for(outrec=0; outrec<_outputs.size(); outrec++) { @@ -139,6 +160,25 @@ void ControlMap::applyControls() rval += val; } + // If there is a finite transition time, clamp the values to + // the maximum travel allowed in this dt. + if(o->time > 0) { + float dl = lval - o->oldL; + float dr = rval - o->oldR; + float adl = Math::abs(dl); + float adr = Math::abs(dr); + + float max = (dt/o->time) * (rangeMax(o->type) - rangeMin(o->type)); + if(adl > max) dl = dl*max/adl; + if(adr > max) dr = dr*max/adr; + + lval = o->oldL + dl; + rval = o->oldR + dr; + } + + o->oldL = lval; + o->oldR = rval; + void* obj = o->object; switch(o->type) { case THROTTLE: ((Thruster*)obj)->setThrottle(lval); break; @@ -162,4 +202,28 @@ void ControlMap::applyControls() } } -}; // namespace yasim +float ControlMap::rangeMin(int type) +{ + // The minimum of the range for each type of control + switch(type) { + case FLAP0: return -1; // [-1:1] + case FLAP1: return -1; + case STEER: return -1; + case MAGNETOS: return 0; // [0:3] + default: return 0; // [0:1] + } +} + +float ControlMap::rangeMax(int type) +{ + // The maximum of the range for each type of control + switch(type) { + case FLAP0: return 1; // [-1:1] + case FLAP1: return 1; + case STEER: return 1; + case MAGNETOS: return 3; // [0:3] + default: return 1; // [0:1] + } +} + +} // namespace yasim diff --git a/src/FDM/YASim/ControlMap.hpp b/src/FDM/YASim/ControlMap.hpp index b9d6f0f17..cbbd46ab6 100644 --- a/src/FDM/YASim/ControlMap.hpp +++ b/src/FDM/YASim/ControlMap.hpp @@ -43,10 +43,31 @@ public: void setInput(int input, float value); // Calculates and applies the settings received since the last reset(). - void applyControls(); + void applyControls(float dt); + + // Returns the input/output range appropriate for the given + // control. Ailerons go from -1 to 1, while throttles are never + // lower than zero, etc... + static float rangeMin(int type); + static float rangeMax(int type); + + // Each output record is identified by both an object/type tuple + // and a numeric handle. + int getOutputHandle(void* obj, int type); + + // Sets the transition time for the control output to swing + // through its full range. + void setTransitionTime(int handle, float time); + + // Retrieves the current value of the control output. Controls + // with OPT_SPLIT settable on inputs will have a separately + // computed "right side" value. + float getOutput(int handle); + float getOutputR(int handle); private: - struct OutRec { int type; void* object; Vector maps; }; + struct OutRec { int type; void* object; Vector maps; + float oldL, oldR, time; }; struct MapRec { OutRec* out; int idx; int opt; float val; float src0; float src1; float dst0; float dst1; }; diff --git a/src/FDM/YASim/FGFDM.cpp b/src/FDM/YASim/FGFDM.cpp index 73bc4b429..4fa0cee11 100644 --- a/src/FDM/YASim/FGFDM.cpp +++ b/src/FDM/YASim/FGFDM.cpp @@ -57,7 +57,8 @@ FGFDM::~FGFDM() delete[] wr->prop; delete wr; } - + for(i=0; i<_controlProps.size(); i++) + delete (PropOut*)_controlProps.get(i); } void FGFDM::iterate(float dt) @@ -170,8 +171,7 @@ void FGFDM::startElement(const char* name, const XMLAttributes &atts) g->setDynamicFriction(attrf(a, "dfric", 0.7)); if(a->hasAttribute("castering")) g->setCastering(true); - float transitionTime = attrf(a, "retract-time", 0); - _airplane.addGear(g, transitionTime); + _airplane.addGear(g); } else if(eq(name, "fuselage")) { float b[3]; v[0] = attrf(a, "ax"); @@ -225,34 +225,55 @@ void FGFDM::startElement(const char* name, const XMLAttributes &atts) v[1] = attrf(a, "y"); v[2] = attrf(a, "z"); ((Thruster*)_currObj)->setDirection(v); - } else if(eq(name, "control")) { + } else if(eq(name, "control-setting")) { + // A cruise or approach control setting const char* axis = a->getValue("axis"); - if(a->hasAttribute("output")) { - // assert: output type must match _currObj type! - const char* output = a->getValue("output"); - int opt = 0; - opt |= a->hasAttribute("split") ? ControlMap::OPT_SPLIT : 0; - opt |= a->hasAttribute("invert") ? ControlMap::OPT_INVERT : 0; - opt |= a->hasAttribute("square") ? ControlMap::OPT_SQUARE : 0; - - ControlMap* cm = _airplane.getControlMap(); - if(a->hasAttribute("src0")) { - cm->addMapping(parseAxis(axis), parseOutput(output), - _currObj, opt, - attrf(a, "src0"), attrf(a, "src1"), - attrf(a, "dst0"), attrf(a, "dst1")); - } else { - cm->addMapping(parseAxis(axis), parseOutput(output), - _currObj, opt); - } + float value = attrf(a, "value", 0); + if(_cruiseCurr) + _airplane.addCruiseControl(parseAxis(axis), value); + else + _airplane.addApproachControl(parseAxis(axis), value); + } else if(eq(name, "control-input")) { + + // A mapping of input property to a control + int axis = parseAxis(a->getValue("axis")); + int control = parseOutput(a->getValue("control")); + int opt = 0; + opt |= a->hasAttribute("split") ? ControlMap::OPT_SPLIT : 0; + opt |= a->hasAttribute("invert") ? ControlMap::OPT_INVERT : 0; + opt |= a->hasAttribute("square") ? ControlMap::OPT_SQUARE : 0; + + ControlMap* cm = _airplane.getControlMap(); + if(a->hasAttribute("src0")) { + cm->addMapping(axis, control, _currObj, opt, + attrf(a, "src0"), attrf(a, "src1"), + attrf(a, "dst0"), attrf(a, "dst1")); } else { - // assert: must be under a "cruise" or "approach" tag - float value = attrf(a, "value", 0); - if(_cruiseCurr) - _airplane.addCruiseControl(parseAxis(axis), value); - else - _airplane.addApproachControl(parseAxis(axis), value); + cm->addMapping(axis, control, _currObj, opt); } + } else if(eq(name, "control-output")) { + // A property output for a control on the current object + ControlMap* cm = _airplane.getControlMap(); + int type = parseOutput(a->getValue("control")); + int handle = cm->getOutputHandle(_currObj, type); + + PropOut* p = new PropOut(); + p->prop = fgGetNode(a->getValue("prop"), true); + p->handle = handle; + p->type = type; + p->left = !(a->hasAttribute("side") && + eq("right", a->getValue("side"))); + p->min = attrf(a, "min", cm->rangeMin(type)); + p->max = attrf(a, "max", cm->rangeMax(type)); + _controlProps.add(p); + + } else if(eq(name, "control-speed")) { + ControlMap* cm = _airplane.getControlMap(); + int type = parseOutput(a->getValue("control")); + int handle = cm->getOutputHandle(_currObj, type); + float time = attrf(a, "transition-time", 0); + + cm->setTransitionTime(handle, time); } else { *(int*)0=0; // unexpected tag, boom } @@ -269,22 +290,33 @@ void FGFDM::getExternalInput(float dt) float val = fgGetFloat(a->name, 0); cm->setInput(a->handle, val); } - cm->applyControls(); + cm->applyControls(dt); // Weights for(i=0; i<_weights.size(); i++) { WeightRec* wr = (WeightRec*)_weights.get(i); _airplane.setWeight(wr->handle, fgGetFloat(wr->prop)); } - - // Gear state - _airplane.setGearState(fgGetBool("/controls/gear-down"), dt); } void FGFDM::setOutputProperties() { char buf[256]; int i; + + ControlMap* cm = _airplane.getControlMap(); + for(i=0; i<_controlProps.size(); i++) { + PropOut* p = (PropOut*)_controlProps.get(i); + float val = (p->left + ? cm->getOutput(p->handle) + : cm->getOutputR(p->handle)); + float rmin = cm->rangeMin(p->type); + float rmax = cm->rangeMax(p->type); + float frac = (val - rmin) / (rmax - rmin); + val = frac*(p->max - p->min) + p->min; + p->prop->setFloatValue(val); + } + float fuelDensity = 718.95; // default to gasoline: ~6 lb/gal for(i=0; i<_airplane.numTanks(); i++) { fuelDensity = _airplane.getFuelDensity(i); @@ -461,8 +493,7 @@ int FGFDM::parseOutput(const char* name) if(eq(name, "FLAP1")) return ControlMap::FLAP1; if(eq(name, "SLAT")) return ControlMap::SLAT; if(eq(name, "SPOILER")) return ControlMap::SPOILER; - // error here... - return *(int*)0; + *(int*)0=0; } void FGFDM::parseWeight(XMLAttributes* a) @@ -484,7 +515,7 @@ void FGFDM::parseWeight(XMLAttributes* a) bool FGFDM::eq(const char* a, const char* b) { // Figure it out for yourself. :) - while(*a && *b && *a++ == *b++); + while(*a && *b && *a == *b) { a++; b++; } return !(*a || *b); } diff --git a/src/FDM/YASim/FGFDM.hpp b/src/FDM/YASim/FGFDM.hpp index f9506dbd2..0a374b7a3 100644 --- a/src/FDM/YASim/FGFDM.hpp +++ b/src/FDM/YASim/FGFDM.hpp @@ -30,6 +30,8 @@ private: struct AxisRec { char* name; int handle; }; struct EngRec { char* prefix; Thruster* eng; }; struct WeightRec { char* prop; float size; int handle; }; + struct PropOut { SGPropertyNode* prop; int handle, type; bool left; + float min, max; }; void setOutputProperties(); @@ -58,6 +60,9 @@ private: // Engine types. Contains an EngRec structure. Vector _thrusters; + // Output properties for the ControlMap + Vector _controlProps; + // Parsing temporaries void* _currObj; bool _cruiseCurr; diff --git a/src/FDM/YASim/YASim.cxx b/src/FDM/YASim/YASim.cxx index 118d14aab..e37432933 100644 --- a/src/FDM/YASim/YASim.cxx +++ b/src/FDM/YASim/YASim.cxx @@ -159,9 +159,10 @@ void YASim::init() fgSetFloat("/controls/spoilers", 0); // Are we at ground level? If so, lift the plane up so the gear - // clear the ground + // clear the ground. double runway_altitude = fgGetDouble("/environment/ground-elevation-m") * SG_METER_TO_FEET; + fgSetBool("/controls/gear-down", false); if(get_Altitude() - runway_altitude < 50) { float minGearZ = 1e18; for(i=0; inumGear(); i++) { @@ -172,6 +173,7 @@ void YASim::init() minGearZ = pos[2]; } _set_Altitude(runway_altitude - minGearZ*M2FT); + fgSetBool("/controls/gear-down", true); } // The pilot's eyepoint @@ -418,7 +420,6 @@ void YASim::copyFromYASim() SGPropertyNode * node = fgGetNode("gear/gear", i, true); node->setBoolValue("has-brake", g->getBrake() != 0); node->setBoolValue("wow", g->getCompressFraction() != 0); - node->setBoolValue("position", g->getExtension()); } for(i=0; inumThrusters(); i++) { -- 2.39.5