]> git.mxchange.org Git - flightgear.git/commitdiff
Make traffic take-off roll look a little better.
authorJames Turner <zakalawe@mac.com>
Fri, 5 Oct 2012 17:12:46 +0000 (18:12 +0100)
committerJames Turner <zakalawe@mac.com>
Fri, 5 Oct 2012 17:12:46 +0000 (18:12 +0100)
Expand the performance DB logic to support aliases, and select based on aircraft type as well as class. This allows to introduce some variation into AI traffic performance. Change the initial climb-out waypoints to use pitch-hold until passing 3000', which looks much more convincing

src/AIModel/AIAircraft.cxx
src/AIModel/AIAircraft.hxx
src/AIModel/AIFlightPlanCreate.cxx
src/AIModel/performancedb.cxx
src/AIModel/performancedb.hxx
src/ATC/atc_mgr.cxx
src/Traffic/Schedule.cxx
src/Traffic/TrafficMgr.cxx

index 82f21a4e4dfddc12eb45149591adf2b3777b7745..fe93d938b29385ca9437ea62f96117ad2c0b1582 100644 (file)
@@ -107,7 +107,7 @@ void FGAIAircraft::readFromScenario(SGPropertyNode* scFileNode) {
 
     FGAIBase::readFromScenario(scFileNode);
 
-    setPerformance(scFileNode->getStringValue("class", "jet_transport"));
+    setPerformance("", scFileNode->getStringValue("class", "jet_transport"));
     setFlightPlan(scFileNode->getStringValue("flightplan"),
                   scFileNode->getBoolValue("repeat", false));
     setCallSign(scFileNode->getStringValue("callsign"));
@@ -131,16 +131,17 @@ void FGAIAircraft::update(double dt) {
     Transform();
 }
 
-void FGAIAircraft::setPerformance(const std::string& acclass) {
-     static PerformanceDB perfdb; //TODO make it a global service
-     setPerformance(perfdb.getDataFor(acclass));
-  }
-
+void FGAIAircraft::setPerformance(const std::string& acType, const std::string& acclass)
+{
+  static PerformanceDB perfdb; //TODO make it a global service
+  _performance = perfdb.getDataFor(acType, acclass);
+}
 
+#if 0
  void FGAIAircraft::setPerformance(PerformanceData *ps) {
      _performance = ps;
   }
-
+#endif
 
  void FGAIAircraft::Run(double dt) {
       FGAIAircraft::dt = dt;
index ea2d08e7cba1882f194590f49a74818c810a6680..73e98aeb3c9db2d9a2fd4fe968d510ad4e649798 100644 (file)
@@ -44,8 +44,8 @@ public:
     virtual void bind();
     virtual void update(double dt);
 
-    void setPerformance(const std::string& perfString);
-    void setPerformance(PerformanceData *ps);
+    void setPerformance(const std::string& acType, const std::string& perfString);
+  //  void setPerformance(PerformanceData *ps);
 
     void setFlightPlan(const std::string& fp, bool repat = false);
     void SetFlightPlan(FGAIFlightPlan *f);
@@ -68,6 +68,8 @@ public:
     double getBearing(double crse);
 
     void setAcType(const std::string& ac) { acType = ac; };
+    std::string getAcType() const { return acType; }
+  
     void setCompany(const std::string& comp) { company = comp;};
 
     void announcePositionToController(); //TODO have to be public?
index c49b4abe8a6c7504e764ec779c22d4c63c5beb7b..47026b60ed71a826d8325e4f789ddad228b89313 100644 (file)
@@ -145,6 +145,7 @@ FGAIWaypoint *    FGAIFlightPlan::createInAir(FGAIAircraft * ac,
     wpt->setGear_down  (false                   );
     wpt->setFlaps_down (false                   );
     wpt->setOn_ground  (false                   );
+    wpt->setCrossat    (aElev                   );
     return wpt;
 }
 
@@ -427,6 +428,22 @@ bool FGAIFlightPlan::createLandingTaxi(FGAIAircraft * ac, FGAirport * apt,
     return true;
 }
 
+static double accelDistance(double v0, double v1, double accel)
+{
+  double t = fabs(v1 - v0) / accel; // time in seconds to change velocity
+  // area under the v/t graph: (t * v0) + (dV / 2t) where (dV = v1 - v0)
+  return t * 0.5 * (v1 + v0); 
+}
+
+// find the horizontal distance to gain the specific altiude, holding
+// a constant pitch angle. Used to compute distance based on standard FD/AP
+// PITCH mode prior to VS or CLIMB engaging. Visually, we want to avoid
+// a dip in the nose angle after rotation, during initial climb-out.
+static double pitchDistance(double pitchAngleDeg, double altGainM)
+{
+  return altGainM / tan(pitchAngleDeg * SG_DEGREES_TO_RADIANS);
+}
+
 /*******************************************************************
  * CreateTakeOff 
  * A note on units: 
@@ -442,27 +459,22 @@ bool FGAIFlightPlan::createTakeOff(FGAIAircraft * ac, bool firstFlight,
                                    FGAirport * apt, double speed,
                                    const string & fltType)
 {
+    const double ACCEL_POINT = 105.0;
+    const double KNOTS_HOUR_TO_MSEC = SG_NM_TO_METER / 3600.0;
+  // climb-out angle in degrees. could move this to the perf-db but this
+  // value is pretty sane
+    const double INITIAL_PITCH_ANGLE = 12.5;
+  
     double accel = ac->getPerformance()->acceleration();
     double vTaxi = ac->getPerformance()->vTaxi();
     double vRotate = ac->getPerformance()->vRotate();
     double vTakeoff = ac->getPerformance()->vTakeoff();
-    //double vClimb = ac->getPerformance()->vClimb();
-
-    double accelMetric = (accel * SG_NM_TO_METER) / 3600;
-    double vTaxiMetric = (vTaxi * SG_NM_TO_METER) / 3600;
-    double vRotateMetric = (vRotate * SG_NM_TO_METER) / 3600;
-    double vTakeoffMetric = (vTakeoff * SG_NM_TO_METER) / 3600;
-    //double vClimbMetric = (vClimb * SG_NM_TO_METER) / 3600;
-    // Acceleration = dV / dT
-    // Acceleration X dT = dV
-    // dT = dT / Acceleration
-    //d = (Vf^2 - Vo^2) / (2*a)
-    //double accelTime = (vRotate - vTaxi) / accel;
-    //cerr << "Using " << accelTime << " as total acceleration time" << endl;
-    double accelDistance =
-        (vRotateMetric * vRotateMetric -
-         vTaxiMetric * vTaxiMetric) / (2 * accelMetric);
-    //cerr << "Using " << accelDistance << " " << accelMetric << " " << vRotateMetric << endl;
+
+    double accelMetric = accel * KNOTS_HOUR_TO_MSEC;
+    double vTaxiMetric = vTaxi * KNOTS_HOUR_TO_MSEC;
+    double vRotateMetric = vRotate * KNOTS_HOUR_TO_MSEC;
+    double vTakeoffMetric = vTakeoff * KNOTS_HOUR_TO_MSEC;
+   
     FGAIWaypoint *wpt;
     // Get the current active runway, based on code from David Luff
     // This should actually be unified and extended to include 
@@ -475,41 +487,36 @@ bool FGAIFlightPlan::createTakeOff(FGAIAircraft * ac, bool firstFlight,
         apt->getDynamics()->getActiveRunway(rwyClass, 1, activeRunway,
                                             heading);
     }
+  
     FGRunway * rwy = apt->getRunwayByIdent(activeRunway);
     assert( rwy != NULL );
-
     double airportElev = apt->getElevation();
     
-
-    accelDistance =
-        (vTakeoffMetric * vTakeoffMetric -
-         vTaxiMetric * vTaxiMetric) / (2 * accelMetric);
-    //cerr << "Using " << accelDistance << " " << accelMetric << " " << vTakeoffMetric << endl;
-    SGGeod accelPoint = rwy->pointOnCenterline(105.0 + accelDistance);
+    double d = accelDistance(vTaxiMetric, vRotateMetric, accelMetric) + ACCEL_POINT;
+  
+    SGGeod accelPoint = rwy->pointOnCenterline(d);
     wpt = createOnGround(ac, "rotate", accelPoint, airportElev, vTakeoff);
     pushBackWaypoint(wpt);
 
-    accelDistance =
-        ((vTakeoffMetric * 1.1) * (vTakeoffMetric * 1.1) -
-         vTaxiMetric * vTaxiMetric) / (2 * accelMetric);
-    //cerr << "Using " << accelDistance << " " << accelMetric << " " << vTakeoffMetric << endl;
-    accelPoint = rwy->pointOnCenterline(105.0 + accelDistance);
-    wpt =
-        createOnGround(ac, "rotate", accelPoint, airportElev + 1000,
-                       vTakeoff * 1.1);
+    double vRef = vTakeoffMetric + 20; // climb-out at v2 + 20kts
+    double gearUpDist = d + pitchDistance(INITIAL_PITCH_ANGLE, 400 * SG_FEET_TO_METER);
+    accelPoint = rwy->pointOnCenterline(gearUpDist);
+    
+    wpt = cloneWithPos(ac, wpt, "gear-up", accelPoint);
+    wpt->setSpeed(vRef);
+    wpt->setCrossat(airportElev + 400);
     wpt->setOn_ground(false);
+    wpt->setGear_down(false);
     pushBackWaypoint(wpt);
-
-    wpt = cloneWithPos(ac, wpt, "3000 ft", rwy->end());
-    wpt->setAltitude(airportElev + 3000);
+  
+    double climbOut = d + pitchDistance(INITIAL_PITCH_ANGLE, 2000 * SG_FEET_TO_METER);
+    accelPoint = rwy->pointOnCenterline(climbOut);
+    wpt = createInAir(ac, "2000'", accelPoint, airportElev + 2000, vRef);
     pushBackWaypoint(wpt);
 
-    // Finally, add two more waypoints, so that aircraft will remain under
-    // Tower control until they have reached the 3000 ft climb point
-    SGGeod pt = rwy->pointOnCenterline(5000 + rwy->lengthM() * 0.5);
-    wpt = cloneWithPos(ac, wpt, "5000 ft", pt);
-    wpt->setAltitude(airportElev + 5000);
-    pushBackWaypoint(wpt);
+  // as soon as we pass 2000', hand off to departure so the next acft can line up
+  // ideally the next aircraft would be able to line-up + hold but that's tricky
+  // with the current design.
     return true;
 }
 
index 37660e6a0549191c347affb7134213d6098833cf..f218e851dab340f809f3b7d4f382009c47d7f01d 100644 (file)
@@ -1,3 +1,11 @@
+#ifdef HAVE_CONFIG_H
+  #include "config.h"
+#endif
+
+#include "performancedb.hxx"
+
+#include <boost/foreach.hpp>
+
 #include <simgear/misc/sg_path.hxx>
 #include <simgear/props/props.hxx>
 #include <simgear/props/props_io.hxx>
@@ -7,7 +15,7 @@
 #include <iostream>
 #include <fstream>
 
-#include "performancedb.hxx"
+#include "performancedata.hxx"
 
 using std::string;
 using std::cerr;
@@ -36,14 +44,28 @@ void PerformanceDB::registerPerformanceData(const std::string& id, const std::st
     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
+PerformanceData* PerformanceDB::getDataFor(const string& acType, const string& acClass)
+{
+  // first, try with the specific aircraft type, such as 738 or A322
+    if (_db.find(acType) != _db.end()) {
+        return _db[acType];
+    }
+    
+    string alias = findAlias(acType);
+    if (_db.find(alias) != _db.end()) {
+      return _db[alias];
+    }
+  
+    SG_LOG(SG_AI, SG_INFO, "no performance data for " << acType);
+  
+    if (_db.find(acClass) == _db.end()) {
         return _db["jet_transport"];
-
-    return _db[id];
+    }
+  
+    return _db[acClass];
 }
 
-void PerformanceDB::load(SGPath filename) {
+void PerformanceDB::load(const SGPath& filename) {
     string name;
     double acceleration;
     double deceleration;
@@ -67,8 +89,9 @@ void PerformanceDB::load(SGPath filename) {
     }
 
     SGPropertyNode * node = root.getNode("performancedb");
-    for (int i = 0; i < node->nChildren(); i++) { 
+    for (int i = 0; i < node->nChildren(); i++) {
         SGPropertyNode * db_node = node->getChild(i);
+        if (!strcmp(db_node->getName(), "aircraft")) {
             name         = db_node->getStringValue("type", "heavy_jet");
             acceleration = db_node->getDoubleValue("acceleration-kts-hour", 4.0);
             deceleration = db_node->getDoubleValue("deceleration-kts-hour", 2.0);
@@ -85,6 +108,32 @@ void PerformanceDB::load(SGPath filename) {
 
             registerPerformanceData(name, new PerformanceData(
                 acceleration, deceleration, climbRate, descentRate, vRotate, vTakeOff, vClimb, vCruise, vDescent, vApproach, vTouchdown, vTaxi));
-    }
+        } else if (!strcmp(db_node->getName(), "alias")) {
+            string alias(db_node->getStringValue("alias"));
+            if (alias.empty()) {
+                SG_LOG(SG_AI, SG_ALERT, "performance DB alias entry with no <alias> definition");
+                continue;
+            }
+          
+            BOOST_FOREACH(SGPropertyNode* matchNode, db_node->getChildren("match")) {
+                string match(matchNode->getStringValue());
+                _aliases.push_back(StringPair(match, alias));
+            }
+        } else {
+            SG_LOG(SG_AI, SG_ALERT, "unrecognized performance DB entry:" << db_node->getName());
+        }
+    } // of nodes iteration
 }
 
+string PerformanceDB::findAlias(const string& acType) const
+{
+    BOOST_FOREACH(const StringPair& alias, _aliases) {
+        if (acType.find(alias.first) == 0) { // matched!
+            return alias.second;
+        }
+    } // of alias iteration
+  
+    return string();
+}
+
+
index 61d040da0ec2d969599e84d7912c6528aa0ca888..ad3066cb8dc6f4df7a65bc276e0af4a17ef779b1 100644 (file)
@@ -2,10 +2,11 @@
 #define PERFORMANCEDB_HXX
 
 #include <string>
-#include <vector>
 #include <map>
+#include <vector>
 
-#include "performancedata.hxx"
+class PerformanceData;
+class SGPath;
 
 /**
  * Registry for performance data.
@@ -25,11 +26,23 @@ public:
     void registerPerformanceData(const std::string& id, PerformanceData* data);
     void registerPerformanceData(const std::string& id, const std::string& filename);
 
-    PerformanceData* getDataFor(const std::string& id);
-    void load(SGPath path);
+    /**
+     * get performance data for an aircraft type / class. Type is specific, eg
+     * '738' or 'A319'. Class is more generic, such as 'jet_transport'.
+     */
+    PerformanceData* getDataFor(const std::string& acType, const std::string& acClass);
+    void load(const SGPath& path);
 
 private:
     std::map<std::string, PerformanceData*> _db;
+    
+    std::string findAlias(const std::string& acType) const;
+  
+    typedef std::pair<std::string, std::string> StringPair;
+  /// alias list, to allow type/class names to share data. This is used to merge
+  /// related types together. Note it's ordered, and not a map since we permit
+  /// partial matches when merging - the first matching alias is used.
+    std::vector<StringPair> _aliases;
 };
 
 #endif
index c4372173934b045cf55d3bd36d82dfc292997f8e..bbfc73311b52a41c9dd1b783372b5ffe03256fb7 100644 (file)
@@ -74,7 +74,7 @@ void FGATCManager::init() {
     ai_ac.setLongitude( longitude );
     ai_ac.setLatitude ( latitude  );
     ai_ac.setAltitude ( altitude  );
-    ai_ac.setPerformance("jet_transport");
+    ai_ac.setPerformance("", "jet_transport");
 
     // NEXT UP: Create a traffic Schedule and fill that with appropriate information. This we can use to flight planning.
     // Note that these are currently only defaults. 
index 9af6a0c2725e0d0a0cc44325332c244aae41822a..3d4db5cb7683517f85e257e5f7599b5148730e2a 100644 (file)
@@ -346,7 +346,7 @@ bool FGAISchedule::createAIAircraft(FGScheduledFlight* flight, double speedKnots
   }
 
   FGAIAircraft *aircraft = new FGAIAircraft(this);
-  aircraft->setPerformance(m_class); //"jet_transport";
+  aircraft->setPerformance(acType, m_class); //"jet_transport";
   aircraft->setCompany(airline); //i->getAirline();
   aircraft->setAcType(acType); //i->getAcType();
   aircraft->setPath(modelPath.c_str());
index 2510b7a62c0ca2297b8ea699f5eee28982fd8cd7..464dca5e04286b7d589b3d58d2bb12e8428ec10e 100644 (file)
@@ -782,8 +782,6 @@ void FGTrafficManager::endAircraft()
     acCounter++;
     requiredAircraft = "";
     homePort = "";
-    SG_LOG(SG_GENERAL, SG_BULK, "Reading aircraft : "
-           << registration << " with prioritization score " << score);
     score = 0;
 }