]> git.mxchange.org Git - flightgear.git/commitdiff
Thomas Foerster: Prepare for the inclusion of aircraft specific performance
authordurk <durk>
Thu, 28 Jun 2007 07:47:20 +0000 (07:47 +0000)
committerdurk <durk>
Thu, 28 Jun 2007 07:47:20 +0000 (07:47 +0000)
data for AI traffic. Default performance classes are still available as a
backup. This database will allow the calculation of aircraft-specific
take-off speed and estimate runway lenght requirements. Further added
rudimentary support for take-off and landing rotation of AIAircraft.

src/AIModel/AIAircraft.cxx
src/AIModel/AIAircraft.hxx
src/AIModel/Makefile.am
src/AIModel/performancedata.cxx [new file with mode: 0644]
src/AIModel/performancedata.hxx [new file with mode: 0644]
src/AIModel/performancedb.cxx [new file with mode: 0644]
src/AIModel/performancedb.hxx [new file with mode: 0644]

index 19f57f5c28fd18f109b4ad289a7f4738afd0beee..8cc5089ad4e3c0de814af03691e43b51a29d0417 100644 (file)
 SG_USING_STD(string);
 
 #include "AIAircraft.hxx"
+#include "performancedata.hxx"
+#include "performancedb.hxx"
+
 //#include <Airports/trafficcontroller.hxx>
 
 static string tempReg;
-//
-// accel, decel, climb_rate, descent_rate, takeoff_speed, climb_speed,
-// cruise_speed, descent_speed, land_speed
-//
 
-const FGAIAircraft::PERF_STRUCT FGAIAircraft::settings[] = {
-            // light aircraft
-            {2.0, 2.0,  450.0, 1000.0,  70.0,  80.0, 100.0,  80.0,  60.0},
-            // ww2_fighter
-            {4.0, 2.0, 3000.0, 1500.0, 110.0, 180.0, 250.0, 200.0, 100.0},
-            // jet_transport
-            {5.0, 2.0, 3000.0, 1500.0, 140.0, 300.0, 430.0, 300.0, 130.0},
-            // jet_fighter
-            {7.0, 3.0, 4000.0, 2000.0, 150.0, 350.0, 500.0, 350.0, 150.0},
-            // tanker
-            {5.0, 2.0, 3000.0, 1500.0, 140.0, 300.0, 430.0, 300.0, 130.0},
-            // ufo (extreme accel/decel)
-            {30.0, 30.0, 6000.0, 6000.0, 150.0, 300.0, 430.0, 300.0, 130.0}
-        };
+class AI_OutOfSight{};
+class FP_Inactive{};
 
 FGAIAircraft::FGAIAircraft(FGAISchedule *ref) : FGAIBase(otAircraft) {
     trafficRef = ref;
@@ -92,6 +79,8 @@ FGAIAircraft::FGAIAircraft(FGAISchedule *ref) : FGAIBase(otAircraft) {
     headingChangeRate = 0.0;
 
     holdPos = false;
+
+    _performance = 0; //TODO initialize to JET_TRANSPORT from PerformanceDB
 }
 
 
@@ -138,62 +127,36 @@ void FGAIAircraft::update(double dt) {
     Transform();
 }
 
-
 void FGAIAircraft::setPerformance(const std::string& acclass) {
-    if (acclass == "light") {
-        SetPerformance(&FGAIAircraft::settings[FGAIAircraft::LIGHT]);
-    } else if (acclass == "ww2_fighter") {
-        SetPerformance(&FGAIAircraft::settings[FGAIAircraft::WW2_FIGHTER]);
-    } else if (acclass == "jet_transport") {
-        SetPerformance(&FGAIAircraft::settings[FGAIAircraft::JET_TRANSPORT]);
-    } else if (acclass == "jet_fighter") {
-        SetPerformance(&FGAIAircraft::settings[FGAIAircraft::JET_FIGHTER]);
-    } else if (acclass == "tanker") {
-        SetPerformance(&FGAIAircraft::settings[FGAIAircraft::JET_TRANSPORT]);
-    } else if (acclass == "ufo") {
-        SetPerformance(&FGAIAircraft::settings[FGAIAircraft::UFO]);
-    } else {
-        SetPerformance(&FGAIAircraft::settings[FGAIAircraft::JET_TRANSPORT]);
-    }
-}
+     static PerformanceDB perfdb; //TODO make it a global service
+     setPerformance(perfdb.getDataFor(acclass));
+  }
 
 
-void FGAIAircraft::SetPerformance(const PERF_STRUCT *ps) {
+ void FGAIAircraft::setPerformance(PerformanceData *ps) {
+     _performance = ps;
+  }
 
-    performance = ps;
-}
 
+ void FGAIAircraft::Run(double dt) {
+      FGAIAircraft::dt = dt;
 
-void FGAIAircraft::Run(double dt) {
+     try {
+         updatePrimaryTargetValues(); // target hdg, alt, speed
+     }
+     catch (AI_OutOfSight) {
+         return;
+     }
+     catch (FP_Inactive) {
+         return;
+     }
 
-    FGAIAircraft::dt = dt;
-    if (!updateTargetValues())
-        return;
+     handleATCRequests(); // ATC also has a word to say
+     updateSecondaryTargetValues(); // target roll, vertical speed, pitch
+     updateActualState(); 
+     UpdateRadar(manager);
+  }
 
-    if (controller) {
-        controller->update(getID(),
-                           pos.getLatitudeDeg(),
-                           pos.getLongitudeDeg(),
-                           hdg,
-                           speed,
-                           altitude_ft, dt);
-        processATC(controller->getInstruction(getID()));
-    }
-
-    if (no_roll) {
-        adjustSpeed(groundTargetSpeed);
-    } else {
-        adjustSpeed(tgt_speed);
-    }
-
-    updatePosition();
-    updateHeading();
-    updateBankAngles();
-    updateAltitudes();
-    updateVerticalSpeed();
-    matchPitchAngle();
-    UpdateRadar(manager);
-}
 
 
 void FGAIAircraft::AccelTo(double speed) {
@@ -352,9 +315,7 @@ void FGAIAircraft::initializeFlightPlan() {
 
 
 bool FGAIAircraft::_getGearDown() const {
-    return ((props->getFloatValue("position/altitude-agl-ft") < 900.0)
-            && (props->getFloatValue("velocities/airspeed-kt")
-                < performance->land_speed*1.25));
+    return _performance->gearExtensible(this);
 }
 
 
@@ -403,8 +364,8 @@ void FGAIAircraft::getGroundElev(double dt) {
     // to prevent all IA objects doing this in synchrony
     if (dt_elev_count < (3.0) + (rand() % 10))
         return;
-    else
-        dt_elev_count = 0;
+
+    dt_elev_count = 0;
 
     // Only do the proper hitlist stuff if we are within visible range of the viewer.
     if (!invisible) {
@@ -511,6 +472,17 @@ void FGAIAircraft::announcePositionToController() {
 
 
 void FGAIAircraft::processATC(FGATCInstruction instruction) {
+    if (instruction.getCheckForCircularWait()) {
+        // This is not exactly an elegant solution, 
+        // but at least it gives me a chance to check
+        // if circular waits are resolved.
+        // For now, just take the offending aircraft 
+        // out of the scene
+       setDie(true);
+        // a more proper way should be - of course - to
+        // let an offending aircraft take an evasive action
+        // for instance taxi back a little bit.
+    }
     //cerr << "Processing ATC instruction (not Implimented yet)" << endl;
     if (instruction.getHoldPattern   ()) {}
 
@@ -755,7 +727,7 @@ void FGAIAircraft::controlSpeed(FGAIFlightPlan::waypoint* curr, FGAIFlightPlan::
 /**
  * Update target values (heading, alt, speed) depending on flight plan or control properties
  */
-bool FGAIAircraft::updateTargetValues() {
+void FGAIAircraft::updatePrimaryTargetValues() {
     if (fp)                      // AI object has a flightplan
     {
         //TODO make this a function of AIBase
@@ -767,13 +739,13 @@ bool FGAIAircraft::updateTargetValues() {
             // Are repositioned to the correct ground altitude when the user flies within visibility range.
             // In addition, check whether we are out of user range, so this aircraft
             // can be deleted.
-            if (no_roll) {
+            if (onGround()) {
                 Transform();     // make sure aip is initialized.
                 if (trafficRef) {
                     //cerr << trafficRef->getRegistration() << " Setting altitude to " << altitude_ft;
                     if (! aiTrafficVisible()) {
                         setDie(true);
-                        return false;
+                        throw AI_OutOfSight();
                     }
                     getGroundElev(dt);
                     doGroundAltitude();
@@ -781,7 +753,7 @@ bool FGAIAircraft::updateTargetValues() {
                     pos.setElevationFt(altitude_ft);
                 }
             }
-            return false;
+            throw FP_Inactive();
         }
     }
     else {
@@ -813,39 +785,8 @@ bool FGAIAircraft::updateTargetValues() {
 
         AccelTo( props->getDoubleValue("controls/flight/target-spd" ) );
     }
-    return true;
-}
-
-
-/**
- * Adjust the speed (accelerate/decelerate) to tgt_speed.
- */
-void FGAIAircraft::adjustSpeed(double tgt_speed) {
-    double speed_diff = tgt_speed - speed;
-    speed_diff = groundTargetSpeed - speed;
-
-    if (speed_diff > 0.0)        // need to accelerate
-    {
-        speed += performance->accel * dt;
-        if ( speed > tgt_speed )
-            speed = tgt_speed;
-
-    } else if (speed_diff < 0.0) {
-        if (no_roll) {
-            // on ground (aircraft can't roll)
-            // deceleration performance is better due to wheel brakes.
-            speed -= performance->decel * dt * 3;
-        } else {
-            speed -= performance->decel * dt;
-        }
-
-        if ( speed < tgt_speed )
-            speed = tgt_speed;
-
-    }
 }
 
-
 void FGAIAircraft::updatePosition() {
     // convert speed to degrees per second
     double speed_north_deg_sec = cos( hdg * SGD_DEGREES_TO_RADIANS )
@@ -871,7 +812,7 @@ void FGAIAircraft::updateHeading() {
         //else
         //  turnConstant = 0.088362;
         // If on ground, calculate heading change directly
-        if (no_roll) {
+        if (onGround()) {
             double headingDiff = fabs(hdg-tgt_heading);
 
             if (headingDiff > 180)
@@ -922,7 +863,7 @@ void FGAIAircraft::updateHeading() {
 }
 
 
-void FGAIAircraft::updateBankAngles() {
+void FGAIAircraft::updateBankAngleTarget() {
     // adjust target bank angle if heading lock engaged
     if (hdg_lock) {
         double bank_sense = 0.0;
@@ -938,55 +879,37 @@ void FGAIAircraft::updateBankAngles() {
         } else {
             bank_sense = -1.0;   // left turn
         }
-        if (diff < 30) {
+        if (diff < _performance->maximumBankAngle()) {
             tgt_roll = diff * bank_sense;
         } else {
-            tgt_roll = 30.0 * bank_sense;
+            tgt_roll = _performance->maximumBankAngle() * bank_sense;
         }
-        if ((fabs((double) spinCounter) > 1) && (diff > 30)) {
+        if ((fabs((double) spinCounter) > 1) && (diff > _performance->maximumBankAngle())) {
             tgt_speed *= 0.999;  // Ugly hack: If aircraft get stuck, they will continually spin around.
             // The only way to resolve this is to make them slow down.
         }
     }
-
-    // adjust bank angle, use 9 degrees per second
-    double bank_diff = tgt_roll - roll;
-    if (fabs(bank_diff) > 0.2) {
-        if (bank_diff > 0.0)
-            roll += 9.0 * dt;
-
-        if (bank_diff < 0.0)
-            roll -= 9.0 * dt;
-        //while (roll > 180) roll -= 360;
-        //while (roll < 180) roll += 360;
-    }
 }
 
 
-void FGAIAircraft::updateAltitudes() {
-    // adjust altitude (meters) based on current vertical speed (fpm)
-    altitude_ft += vs / 60.0 * dt;
-    pos.setElevationFt(altitude_ft);
-
+void FGAIAircraft::updateVerticalSpeedTarget() {
     // adjust target Altitude, based on ground elevation when on ground
-    if (no_roll) {
+    if (onGround()) {
         getGroundElev(dt);
         doGroundAltitude();
-    } else {
-        // find target vertical speed if altitude lock engaged
-        if (alt_lock && use_perf_vs) {
+    } else if (alt_lock) {
+        // find target vertical speed
+        if (use_perf_vs) {
             if (altitude_ft < tgt_altitude_ft) {
                 tgt_vs = tgt_altitude_ft - altitude_ft;
-                if (tgt_vs > performance->climb_rate)
-                    tgt_vs = performance->climb_rate;
+                if (tgt_vs > _performance->climbRate())
+                    tgt_vs = _performance->climbRate();
             } else {
                 tgt_vs = tgt_altitude_ft - altitude_ft;
-                if (tgt_vs  < (-performance->descent_rate))
-                    tgt_vs = -performance->descent_rate;
+                if (tgt_vs  < (-_performance->descentRate()))
+                    tgt_vs = -_performance->descentRate();
             }
-        }
-
-        if (alt_lock && !use_perf_vs) {
+        } else {
             double max_vs = 4*(tgt_altitude_ft - altitude_ft);
             double min_vs = 100;
             if (tgt_altitude_ft < altitude_ft)
@@ -998,34 +921,64 @@ void FGAIAircraft::updateAltitudes() {
             if (fabs(tgt_vs) < fabs(min_vs))
                 tgt_vs = min_vs;
         }
+    } //else 
+    //    tgt_vs = 0.0;
+}
+
+void FGAIAircraft::updatePitchAngleTarget() {
+    // if on ground and above vRotate -> initial rotation
+    if (onGround() && (speed > _performance->vRotate()))
+        tgt_pitch = 8.0; // some rough B737 value 
+
+    //TODO pitch angle on approach and landing
+    
+    // match pitch angle to vertical speed
+    else if (tgt_vs > 0) {
+        tgt_pitch = tgt_vs * 0.005;
+    } else {
+        tgt_pitch = tgt_vs * 0.002;
     }
 }
 
+void FGAIAircraft::handleATCRequests() {
+    //TODO implement NullController for having no ATC to save the conditionals
+    if (controller) {
+        controller->update(getID(),
+                           pos.getLatitudeDeg(),
+                           pos.getLongitudeDeg(),
+                           hdg,
+                           speed,
+                           altitude_ft, dt);
+        processATC(controller->getInstruction(getID()));
+    }
+}
 
-void FGAIAircraft::updateVerticalSpeed() {
-    // adjust vertical speed
-    double vs_diff = tgt_vs - vs;
-    if (fabs(vs_diff) > 10.0) {
-        if (vs_diff > 0.0) {
-            vs += (performance->climb_rate / 3.0) * dt;
+void FGAIAircraft::updateActualState() {
+    //update current state
+    //TODO have a single tgt_speed and check speed limit on ground on setting tgt_speed
+    updatePosition();
 
-            if (vs > tgt_vs)
-                vs = tgt_vs;
-        } else {
-            vs -= (performance->descent_rate / 3.0) * dt;
+    if (onGround())
+        speed = _performance->actualSpeed(this, groundTargetSpeed, dt);
+    else
+        speed = _performance->actualSpeed(this, tgt_speed, dt);
 
-            if (vs < tgt_vs)
-                vs = tgt_vs;
-        }
-    }
+    updateHeading();
+    roll = _performance->actualBankAngle(this, tgt_roll, dt);
+
+    // adjust altitude (meters) based on current vertical speed (fpm)
+    altitude_ft += vs / 60.0 * dt;
+    pos.setElevationFt(altitude_ft);
+
+    vs = _performance->actualVerticalSpeed(this, tgt_vs, dt);
+    pitch = _performance->actualPitch(this, tgt_pitch, dt);
 }
 
+void FGAIAircraft::updateSecondaryTargetValues() {
+    // derived target state values
+    updateBankAngleTarget();
+    updateVerticalSpeedTarget();
+    updatePitchAngleTarget();
 
-void FGAIAircraft::matchPitchAngle() {
-    // match pitch angle to vertical speed
-    if (vs > 0) {
-        pitch = vs * 0.005;
-    } else {
-        pitch = vs * 0.002;
-    }
+    //TODO calculate wind correction angle (tgt_yaw)
 }
index 035b44bc724667e150436149b7a953daf56aaa34..b70c1ea6244a0a2bb58a450d8096695b7ba297fb 100644 (file)
 #include <string>
 SG_USING_STD(string);
 
+class PerformanceData;
 
 class FGAIAircraft : public FGAIBase {
 
-private:
-    typedef struct {
-        double accel;
-        double decel;
-        double climb_rate;
-        double descent_rate;
-        double takeoff_speed;
-        double climb_speed;
-        double cruise_speed;
-        double descent_speed;
-        double land_speed;
-    } PERF_STRUCT;
-
 public:
-    enum aircraft_e {
-        LIGHT = 0,
-        WW2_FIGHTER,
-        JET_TRANSPORT,
-        JET_FIGHTER,
-        TANKER,
-        UFO
-    };
-    static const PERF_STRUCT settings[];
-
     FGAIAircraft(FGAISchedule *ref=0);
     ~FGAIAircraft();
 
@@ -68,32 +46,45 @@ public:
     virtual void update(double dt);
 
     void setPerformance(const std::string& perfString);
-    void SetPerformance(const PERF_STRUCT *ps);
+    void setPerformance(PerformanceData *ps);
+
     void setFlightPlan(const std::string& fp, bool repat = false);
     void SetFlightPlan(FGAIFlightPlan *f);
     void initializeFlightPlan();
     FGAIFlightPlan* GetFlightPlan() const { return fp; };
+    void ProcessFlightPlan( double dt, time_t now );
+    
     void AccelTo(double speed);
     void PitchTo(double angle);
     void RollTo(double angle);
     void YawTo(double angle);
     void ClimbTo(double altitude);
     void TurnTo(double heading);
-    void ProcessFlightPlan( double dt, time_t now );
+    
     void setCallSign(const string& );
 
-    void getGroundElev(double dt);
+    void getGroundElev(double dt); //TODO these 3 really need to be public?
     void doGroundAltitude();
     void loadNextLeg  ();
 
     void setAcType(const string& ac) { acType = ac; };
     void setCompany(const string& comp) { company = comp;};
 
-    void announcePositionToController();
+    void announcePositionToController(); //TODO have to be public?
     void processATC(FGATCInstruction instruction);
 
     virtual const char* getTypeString(void) const { return "aircraft"; }
 
+    // included as performance data needs them, who else?
+    inline bool onGround() const { return no_roll; };
+    inline double getSpeed() const { return speed; };
+    inline double getRoll() const { return roll; };
+    inline double getPitch() const { return pitch; };
+    inline double getAltitude() const { return altitude_ft; };
+    inline double getVerticalSpeed() const { return vs; };
+    inline double altitudeAGL() const { return props->getFloatValue("position/altitude-agl-ft");};
+    inline double airspeed() const { return props->getFloatValue("velocities/airspeed-kt");};
+    
 protected:
     void Run(double dt);
 
@@ -110,11 +101,12 @@ private:
     double groundOffset;
     double dt;
 
-    const PERF_STRUCT *performance;
     bool use_perf_vs;
     SGPropertyNode_ptr refuel_node;
 
     // helpers for Run
+    //TODO sort out which ones are better protected virtuals to allow
+    //subclasses to override specific behaviour
     bool fpExecutable(time_t now);
     void handleFirstWaypoint(void);
     bool leadPointReached(FGAIFlightPlan::waypoint* curr);
@@ -122,16 +114,17 @@ private:
     bool aiTrafficVisible(void);
     void controlHeading(FGAIFlightPlan::waypoint* curr);
     void controlSpeed(FGAIFlightPlan::waypoint* curr,
-                        FGAIFlightPlan::waypoint* next);
-    bool updateTargetValues();
-    void adjustSpeed(double tgt_speed);
+                      FGAIFlightPlan::waypoint* next);
+    void updatePrimaryTargetValues();
+    void updateSecondaryTargetValues();
     void updatePosition();
     void updateHeading();
-    void updateBankAngles();
-    void updateAltitudes();
-    void updateVerticalSpeed();
-    void matchPitchAngle();
-            
+    void updateBankAngleTarget();
+    void updateVerticalSpeedTarget();
+    void updatePitchAngleTarget();
+    void updateActualState();
+    void handleATCRequests();
+
     double sign(double x);
 
     string acType;
@@ -146,6 +139,8 @@ private:
     bool _getGearDown() const;
     bool reachedWaypoint;
     string callsign;             // The callsign of this tanker.
+
+    PerformanceData* _performance; // the performance data for this aircraft
 };
 
 
index 4c768c681dd2e4bedac091643c7ea0461092f92d..30ba83f4b99e175ef0963abc8505c670dd42ddf4 100644 (file)
@@ -1,19 +1,20 @@
 noinst_LIBRARIES = libAIModel.a
 
-libAIModel_a_SOURCES = submodel.cxx submodel.hxx \
-       AIManager.hxx AIManager.cxx \
-       AIBase.hxx AIBase.cxx \
-       AIAircraft.hxx AIAircraft.cxx AIMultiplayer.hxx \
-       AIMultiplayer.cxx \
-       AIShip.hxx AIShip.cxx \
-       AIBallistic.hxx AIBallistic.cxx \
-       AIStorm.hxx AIStorm.cxx \
-       AIThermal.hxx AIThermal.cxx \
-       AIFlightPlan.hxx AIFlightPlan.cxx \
-       AIFlightPlanCreate.cxx \
-       AIFlightPlanCreateCruise.cxx \
-       AICarrier.hxx AICarrier.cxx \
-       AIStatic.hxx AIStatic.cxx \
-       AITanker.cxx AITanker.hxx
+libAIModel_a_SOURCES = submodel.cxx submodel.hxx       \
+                       AIManager.hxx AIManager.cxx \
+                       AIBase.hxx AIBase.cxx   \
+                       AIAircraft.hxx AIAircraft.cxx \
+                       AIMultiplayer.hxx AIMultiplayer.cxx \
+                       AIShip.hxx AIShip.cxx \
+                       AIBallistic.hxx AIBallistic.cxx \
+                       AIStorm.hxx AIStorm.cxx \
+                       AIThermal.hxx AIThermal.cxx \
+                       AIFlightPlan.hxx AIFlightPlan.cxx \
+                       AIFlightPlanCreate.cxx  AIFlightPlanCreateCruise.cxx \
+                       AICarrier.hxx AICarrier.cxx \
+                       AIStatic.hxx AIStatic.cxx \
+                       AITanker.cxx AITanker.hxx \
+                       performancedata.cxx performancedata.hxx \
+                       performancedb.cxx performancedb.hxx
 
 INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/src
diff --git a/src/AIModel/performancedata.cxx b/src/AIModel/performancedata.cxx
new file mode 100644 (file)
index 0000000..5c0ebb5
--- /dev/null
@@ -0,0 +1,148 @@
+#include "performancedata.hxx"
+#include "AIAircraft.hxx"
+
+PerformanceData::PerformanceData(double acceleration,
+                                 double deceleration,
+                                 double climbRate,
+                                 double descentRate,
+                                 double vRotate,
+                                 double vTakeOff,
+                                 double vClimb,
+                                 double vCruise,
+                                 double vDescent,
+                                 double vApproach,
+                                 double vTouchdown,
+                                 double vTaxi) :
+    _acceleration(acceleration),
+    _deceleration(deceleration),
+    _climbRate(climbRate),
+    _descentRate(descentRate),
+    _vRotate(vRotate),
+    _vTakeOff(vTakeOff),
+    _vClimb(vClimb),
+    _vCruise(vCruise),
+    _vDescent(vDescent),
+    _vApproach(vApproach),
+    _vTouchdown(vTouchdown),
+    _vTaxi(vTaxi)
+{
+    _rollrate = 9.0; // degrees per second
+    _maxbank = 30.0; // passenger friendly bank angle
+}
+
+// read perf data from file
+PerformanceData::PerformanceData( const std::string& filename)
+{}
+
+PerformanceData::~PerformanceData()
+{}
+
+double PerformanceData::actualSpeed(FGAIAircraft* ac, double tgt_speed, double dt) {
+    // if (tgt_speed > _vTaxi & ac->onGround()) // maximum taxi speed on ground
+    //    tgt_speed = _vTaxi;
+    // bad idea for a take off roll :-)
+    
+    double speed = ac->getSpeed();
+    double speed_diff = tgt_speed - speed;
+
+    if (speed_diff > 0.0)        // need to accelerate
+    {
+        speed += _acceleration * dt;
+        if ( speed > tgt_speed )
+            speed = tgt_speed;
+
+    } else if (speed_diff < 0.0) { // decelerate
+        if (ac->onGround()) {
+            // deceleration performance is better due to wheel brakes.
+            speed -= 3 * _deceleration * dt;
+        } else {
+            speed -= _deceleration * dt;
+        }
+
+        if ( speed < tgt_speed )
+            speed = tgt_speed;
+
+    }
+
+    return speed;
+}
+
+double PerformanceData::actualBankAngle(FGAIAircraft* ac, double tgt_roll, double dt) {
+    // check maximum bank angle
+    if (fabs(tgt_roll) > _maxbank)
+        tgt_roll = _maxbank * tgt_roll/fabs(tgt_roll);
+    
+    double roll = ac->getRoll();
+    double bank_diff = tgt_roll - roll;
+    
+    if (fabs(bank_diff) > 0.2) {
+        if (bank_diff > 0.0) {
+            roll += _rollrate * dt;
+            if (roll > tgt_roll)
+                roll = tgt_roll;
+        }
+        else if (bank_diff < 0.0) {
+            roll -= _rollrate * dt;
+
+            if (roll < tgt_roll)
+                roll = tgt_roll;
+        }
+        //while (roll > 180) roll -= 360;
+        //while (roll < 180) roll += 360;
+    }
+
+    return roll;
+}
+
+double PerformanceData::actualPitch(FGAIAircraft* ac, double tgt_pitch, double dt) {
+    double pitch = ac->getPitch();
+    double pdiff = tgt_pitch - pitch;
+
+    if (pdiff > 0.0) { // nose up
+        pitch += 0.005*_climbRate * dt / 3.0; //TODO avoid hardcoded 3 secs
+
+        if (pitch > tgt_pitch)
+            pitch = tgt_pitch;
+            
+    } else if (pdiff < 0.0) { // nose down
+        pitch -= 0.002*_descentRate * dt / 3.0;
+
+        if (pitch < tgt_pitch)
+            pitch = tgt_pitch;
+    }
+
+    return pitch;
+}
+
+double PerformanceData::actualAltitude(FGAIAircraft* ac, double tgt_altitude, double dt) {
+    if (ac->onGround()) {
+    } else
+        return ac->getAltitude() + ac->getVerticalSpeed()*dt/60.0;
+}
+
+double PerformanceData::actualVerticalSpeed(FGAIAircraft* ac, double tgt_vs, double dt) {
+    double vs = ac->getVerticalSpeed();
+    double vs_diff = tgt_vs - vs;
+    
+    if (fabs(vs_diff) > 10.0) {
+        if (vs_diff > 0.0) {
+            vs += _climbRate * dt / 3.0; //TODO avoid hardcoded 3 secs to attain climb rate from level flight
+
+            if (vs > tgt_vs)
+                vs = tgt_vs;
+            
+        } else if (vs_diff < 0.0) {
+            vs -= _descentRate * dt / 3.0;
+
+            if (vs < tgt_vs)
+                vs = tgt_vs;
+        }
+    }
+
+    return vs;
+}
+
+bool PerformanceData::gearExtensible(const FGAIAircraft* ac) {
+    return (ac->altitudeAGL() < 900.0)
+            && (ac->airspeed() < _vTouchdown * 1.25);
+}
diff --git a/src/AIModel/performancedata.hxx b/src/AIModel/performancedata.hxx
new file mode 100644 (file)
index 0000000..5df158e
--- /dev/null
@@ -0,0 +1,64 @@
+#ifndef PERFORMANCEDATA_HXX
+#define PERFORMANCEDATA_HXX
+
+#include <string>
+#include <map>
+
+class FGAIAircraft;
+
+/**
+Data storage for aircraft performance data. This is used to properly simulate the flight of AIAircrafts.
+       @author Thomas Förster <t.foerster@biologie.hu-berlin.de>
+*/
+class PerformanceData
+{
+public:
+    PerformanceData(double acceleration,
+                    double deceleration,
+                    double climbRate,
+                    double descentRate,
+                    double vRotate,
+                    double vTakeOff,
+                    double vClimb,
+                    double vCruise,
+                    double vDescent,
+                    double vApproach,
+                    double vTouchdown,
+                    double vTaxi);
+    PerformanceData(const std::string& filename);
+    ~PerformanceData();
+
+    double actualSpeed(FGAIAircraft* ac, double tgt_speed, double dt);
+    double actualBankAngle(FGAIAircraft* ac, double tgt_roll, double dt);
+    double actualPitch(FGAIAircraft* ac, double tgt_pitch, double dt);
+    double actualHeading(FGAIAircraft* ac, double tgt_heading, double dt);
+    double actualAltitude(FGAIAircraft* ac, double tgt_altitude, double dt);
+    double actualVerticalSpeed(FGAIAircraft* ac, double tgt_vs, double dt);
+
+    bool gearExtensible(const FGAIAircraft* ac);
+
+    inline double climbRate() { return _climbRate; };
+    inline double descentRate() { return _descentRate; };
+    inline double vRotate() { return _vRotate; };
+    inline double maximumBankAngle() { return _maxbank; };
+    
+private:
+    double _acceleration;
+    double _deceleration;
+    double _climbRate;
+    double _descentRate;
+    double _vRotate;
+    double _vTakeOff;
+    double _vClimb;
+    double _vCruise;
+    double _vDescent;
+    double _vApproach;
+    double _vTouchdown;
+    double _vTaxi;
+
+    double _rollrate;
+    double _maxbank;
+};
+
+#endif
diff --git a/src/AIModel/performancedb.cxx b/src/AIModel/performancedb.cxx
new file mode 100644 (file)
index 0000000..aec66f3
--- /dev/null
@@ -0,0 +1,40 @@
+#include "performancedb.hxx"
+
+PerformanceDB::PerformanceDB()
+{
+    // these are the 6 classes originally defined in the PERFSTRUCT
+    registerPerformanceData("light", new PerformanceData(
+        2.0, 2.0,  450.0, 1000.0,  70.0, 70.0,  80.0, 100.0,  80.0,  70.0, 60.0, 15.0));
+    registerPerformanceData("ww2_fighter", new PerformanceData(
+        4.0, 2.0,  3000.0, 1500.0,  110.0, 110.0,  180.0, 250.0,  200.0,  130.0, 100.0, 15.0));
+    registerPerformanceData("jet_fighter", new PerformanceData(
+        7.0, 3.0,  4000.0, 2000.0,  120.0, 150.0,  350.0, 500.0,  350.0,  170.0, 150.0, 15.0));
+    registerPerformanceData("jet_transport", new PerformanceData(
+        5.0, 2.0,  3000.0, 1500.0,  100.0, 140.0,  300.0, 430.0,  300.0,  170.0, 130.0, 15.0));
+    registerPerformanceData("tanker", new PerformanceData(
+        5.0, 2.0,  3000.0, 1500.0,  100.0, 140.0,  300.0, 430.0,  300.0,  170.0, 130.0, 15.0));
+    registerPerformanceData("ufo", new PerformanceData(
+        30.0, 30.0, 6000.0, 6000.0, 150.0, 150.0, 300.0, 430.0, 300.0, 170.0, 130.0, 15.0));
+
+}
+
+
+PerformanceDB::~PerformanceDB()
+{}
+
+void PerformanceDB::registerPerformanceData(const std::string& id, PerformanceData* data) {
+    //TODO if key exists already replace data "inplace", i.e. copy to existing PerfData instance
+    // this updates all aircraft currently using the PerfData instance.
+    _db[id] = data;
+}
+
+void PerformanceDB::registerPerformanceData(const std::string& id, const std::string& filename) {
+    registerPerformanceData(id, new PerformanceData(filename));
+}
+
+PerformanceData* PerformanceDB::getDataFor(const std::string& id) {
+    if (_db.find(id) == _db.end()) // id not found -> return jet_transport data
+        return _db["jet_transport"];
+
+    return _db[id];
+}
diff --git a/src/AIModel/performancedb.hxx b/src/AIModel/performancedb.hxx
new file mode 100644 (file)
index 0000000..4e397ac
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef PERFORMANCEDB_HXX
+#define PERFORMANCEDB_HXX
+
+#include <string>
+#include <map>
+
+#include "performancedata.hxx"
+
+/**
+ * Registry for performance data.
+ *
+ * Allows to store performance data for later reuse/retrieval. Just
+ * a simple map for now.
+ * 
+ * @author Thomas Förster <t.foerster@biologie.hu-berlin.de>
+*/
+//TODO provide std::map interface?
+class PerformanceDB
+{
+public:
+    PerformanceDB();
+    ~PerformanceDB();
+
+    void registerPerformanceData(const std::string& id, PerformanceData* data);
+    void registerPerformanceData(const std::string& id, const std::string& filename);
+
+    PerformanceData* getDataFor(const std::string& id);
+
+private:
+    std::map<std::string, PerformanceData*> _db;
+};
+
+#endif