]> git.mxchange.org Git - flightgear.git/commitdiff
Added a property output feature to ControlMap that allows arbitrary binding
authorandy <andy>
Fri, 1 Mar 2002 06:47:28 +0000 (06:47 +0000)
committerandy <andy>
Fri, 1 Mar 2002 06:47:28 +0000 (06:47 +0000)
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
src/FDM/YASim/Airplane.hpp
src/FDM/YASim/ControlMap.cpp
src/FDM/YASim/ControlMap.hpp
src/FDM/YASim/FGFDM.cpp
src/FDM/YASim/FGFDM.hpp
src/FDM/YASim/YASim.cxx

index 7132772aaad2c0b3a974fd18538396831c312107..c70db66719a4b8a934b4211ee577779ede666f38 100644 (file)
@@ -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();
index cf088f1b9014e876ecb22d8df3304b6faa238746..9a38700a029387fc1db9dfbf66ee41d21ad18e38 100644 (file)
@@ -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;
index a023d91f320e9df0806e473863d18da3aaf41fa5..9bc74b5ecf48ed9bc73b202255ddae9557380280 100644 (file)
@@ -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; j<o->maps.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
index b9d6f0f1746904982c5357a6886a1a4bee75aa97..cbbd46ab6a662fc8026806026c964c9e9fa9d6c5 100644 (file)
@@ -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; };
 
index 73bc4b42922bffc16e66b0fdb81976c92ff8b481..4fa0cee11c3c1ea3bf604ac6b96a2933b0fd8c46 100644 (file)
@@ -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);
 }
 
index f9506dbd289c51843ad31cea70b322052a79aa5a..0a374b7a3d7f16a17016765f01942545d996fda5 100644 (file)
@@ -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;
index 118d14aab50e4b8ef61bf5e1d68ccba6dc6d5c71..e374329338819c6959bcd7421363de6f49f5bf8d 100644 (file)
@@ -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; i<a->numGear(); 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; i<model->numThrusters(); i++) {