]> git.mxchange.org Git - flightgear.git/commitdiff
Route-path bug fixes.
authorJames Turner <zakalawe@mac.com>
Thu, 8 Jan 2015 19:46:04 +0000 (19:46 +0000)
committerJames Turner <zakalawe@mac.com>
Thu, 8 Jan 2015 19:46:04 +0000 (19:46 +0000)
- explicit aircraft performance categories for turn radius
- allow overflight leg course behaviour to be selected

src/Instrumentation/gps.cxx
src/Instrumentation/gps.hxx
src/Navaids/FlightPlan.cxx
src/Navaids/FlightPlan.hxx
src/Navaids/routePath.cxx
src/Navaids/routePath.hxx
src/Scripting/NasalPositioned.cxx

index 2a356035518b06e467fafe19d9f8dd1dbdc034b8..1d0acf23bbc6e3951038bb0781e24bb2cff4ec0b 100644 (file)
@@ -70,7 +70,8 @@ GPS::Config::Config() :
   _requireHardSurface(true),
   _cdiMaxDeflectionNm(3.0), // linear mode, 3nm at the peg
   _driveAutopilot(true),
-  _courseSelectable(false)
+  _courseSelectable(false),
+  _followLegTrackToFix(true)
 {
   _enableTurnAnticipation = false;
 }
@@ -84,6 +85,7 @@ void GPS::Config::bind(GPS* aOwner, SGPropertyNode* aCfg)
   aOwner->tie(aCfg, "cdi-max-deflection-nm", SGRawValuePointer<double>(&_cdiMaxDeflectionNm));
   aOwner->tie(aCfg, "drive-autopilot", SGRawValuePointer<bool>(&_driveAutopilot));
   aOwner->tie(aCfg, "course-selectable", SGRawValuePointer<bool>(&_courseSelectable));
+  aOwner->tie(aCfg, "follow-leg-track-to-fix", SGRawValuePointer<bool>(&_followLegTrackToFix));
   aOwner->tie(aCfg, "over-flight-distance-nm", SGRawValuePointer<double>(&_overflightDistance));
   aOwner->tie(aCfg, "over-flight-arm-distance-nm", SGRawValuePointer<double>(&_overflightArmDistance));
   aOwner->tie(aCfg, "over-flight-arm-angle-deg", SGRawValuePointer<double>(&_overflightArmAngle));
index 100d236b6ce4424d1d9f17a7104f691da6639e46..ada60638e282d728ea04ae7dffa0e7448247b49d 100644 (file)
@@ -139,6 +139,15 @@ private:
 
       bool courseSelectable() const        { return _courseSelectable; }
 
+        /**
+         * Select whether we fly the leg track between waypoints, or
+         * use a direct course from the turn end. Since this is likely confusing,
+         * look at: http://fgfs.goneabitbursar.com//screenshots/FlyByType-LegType.svg
+         * For fly-by waypoints, there is no difference. For fly-over waypoints,
+         * this selects if we fly TF or DF mode.
+         */
+        bool followLegTrackToFix() const      { return _followLegTrackToFix; }
+
     private:
       bool _enableTurnAnticipation;
 
@@ -168,6 +177,9 @@ private:
 
       // is selected-course-deg read to set desired-course or not?
       bool _courseSelectable;
+
+        // do we fly direct to fixes, or follow the leg track closely?
+        bool _followLegTrackToFix;
     };
 
     class SearchFilter : public FGPositioned::Filter
index 83edf5c78aded2b7b566f55fbaf73da7d0692ff0..ee1e908157baf31c2a4a3a9327a6d8214a24600f 100644 (file)
@@ -64,6 +64,8 @@ static FPDelegateFactoryVec static_delegateFactories;
 FlightPlan::FlightPlan() :
   _delegateLock(0),
   _currentIndex(-1),
+    _followLegTrackToFix(true),
+    _aircraftCategory(ICAO_AIRCRAFT_CATEGORY_C),
   _departureRunway(NULL),
   _destinationRunway(NULL),
   _sid(NULL),
@@ -1359,5 +1361,36 @@ void FlightPlan::Delegate::runFinished()
     if (_inner) _inner->runFinished();
     endOfFlightPlan();
 }
+
+void FlightPlan::setFollowLegTrackToFixes(bool tf)
+{
+    _followLegTrackToFix = tf;
+}
+
+bool FlightPlan::followLegTrackToFixes() const
+{
+    return _followLegTrackToFix;
+}
+
+std::string FlightPlan::icaoAircraftCategory() const
+{
+    std::string r;
+    r.push_back(_aircraftCategory);
+    return r;
+}
+
+void FlightPlan::setIcaoAircraftCategory(const std::string& cat)
+{
+    if (cat.empty()) {
+        throw sg_range_exception("Invalid ICAO aircraft category:", cat);
+    }
+
+    if ((cat[0] < ICAO_AIRCRAFT_CATEGORY_A) || (cat[0] > ICAO_AIRCRAFT_CATEGORY_E)) {
+        throw sg_range_exception("Invalid ICAO aircraft category:", cat);
+    }
+
+    _aircraftCategory = cat[0];
+}
+
     
 } // of namespace flightgear
index 93e421b3b4c59a70f8dd70df7127830982b789d3..50747b6df192db91c2b0348d13def81bcb9297b3 100644 (file)
@@ -34,7 +34,13 @@ class Transition;
 class FlightPlan;
     
 typedef SGSharedPtr<FlightPlan> FlightPlanRef;
-    
+
+const char ICAO_AIRCRAFT_CATEGORY_A = 'A';
+const char ICAO_AIRCRAFT_CATEGORY_B = 'B';
+const char ICAO_AIRCRAFT_CATEGORY_C = 'C';
+const char ICAO_AIRCRAFT_CATEGORY_D = 'D';
+const char ICAO_AIRCRAFT_CATEGORY_E = 'E';
+
 class FlightPlan : public RouteBase
 {
 public:
@@ -43,7 +49,16 @@ public:
   
   virtual std::string ident() const;
   void setIdent(const std::string& s);
-  
+
+    // propogate the GPS/FMS setting for this through to the RoutePath
+    void setFollowLegTrackToFixes(bool tf);
+    bool followLegTrackToFixes() const;
+
+    // aircraft approach category as per CFR 97.3, etc
+    // http://www.flightsimaviation.com/data/FARS/part_97-3.html
+    std::string icaoAircraftCategory() const;
+    void setIcaoAircraftCategory(const std::string& cat);
+
   FlightPlan* clone(const std::string& newIdent = std::string()) const;
   
   /**
@@ -259,7 +274,9 @@ private:
   
   std::string _ident;
   int _currentIndex;
-  
+    bool _followLegTrackToFix;
+    char _aircraftCategory;
+
   FGAirportRef _departure, _destination;
   FGRunway* _departureRunway, *_destinationRunway;
   SGSharedPtr<SID> _sid;
index e5d86e63ee09bc2054b7c7def70c9a4a4a3e4ff3..8aa51815c5c2f8665a8a62c6c8d149b33ca94d8d 100644 (file)
@@ -173,7 +173,7 @@ public:
       }
   }
 
-  void computeTurn(double radiusM, const WayptData& previous, WayptData& next)
+  void computeTurn(double radiusM, bool constrainLegCourse, const WayptData& previous, WayptData& next)
   {
     assert(!skipped);
     assert(legCourseValid && next.legCourseValid);
@@ -201,7 +201,7 @@ public:
         // through turnAngle
         double xtk = turnRadius * (1 - cos(turnAngle * SG_DEGREES_TO_RADIANS));
 
-        if (next.isCourseConstrained()) {
+        if (constrainLegCourse || next.isCourseConstrained()) {
           // next leg course is constrained. We need to swing back onto the
           // desired course, using a compensation turn
           
@@ -409,9 +409,12 @@ bool isDescentWaypoint(const WayptRef& wpt)
 class RoutePath::RoutePathPrivate
 {
 public:
-  WayptDataVec waypoints;
-  PerformanceBracketVec perf;
-  
+    WayptDataVec waypoints;
+
+    char aircraftCategory;
+    PerformanceBracketVec perf;
+    double pathTurnRate;
+    bool constrainLegCourses;
   
   PerformanceBracketVec::const_iterator
   findPerformanceBracket(double altFt) const
@@ -621,10 +624,16 @@ public:
   
   double groundSpeedForAltitude(double altitude) const
   {
-    // FIXME
+      PerformanceBracketVec::const_iterator it = findPerformanceBracket(altitude);
+      if (it->speedIsMach) {
+          return 300.0;
+      } else {
+          // FIXME - convert IAS to ground-speed, using standard atmosphere / temperature model
+          return it->speedIASOrMach;
+      }
+
 #if 0
     if (0) {
-      PerformanceBracketVec::const_iterator it = findPerformanceBracket(altitude);
       double mach;
       
       if (it->speedIsMach) {
@@ -652,8 +661,6 @@ public:
 #endif
     }
 #endif
-    
-    return 250.0;
   }
 
   double distanceBetweenIndices(int from, int to) const
@@ -669,11 +676,36 @@ public:
   
   void initPerfData()
   {
-    // assume category C/D aircraft for now
-    perf.push_back(PerformanceBracket(4000, 1800, 1800, 180));
-    perf.push_back(PerformanceBracket(10000, 1800, 1800, 230));
-    perf.push_back(PerformanceBracket(18000, 1200, 1800, 270));
-    perf.push_back(PerformanceBracket(60000, 800, 1200, 0.85, true /* is Mach */));
+      pathTurnRate = 3.0; // 3 deg/sec = 180deg/min = standard rate turn
+      switch (aircraftCategory) {
+      case ICAO_AIRCRAFT_CATEGORY_A:
+          perf.push_back(PerformanceBracket(4000, 600, 1200, 75));
+          perf.push_back(PerformanceBracket(10000, 600, 1200, 140));
+          break;
+
+      case ICAO_AIRCRAFT_CATEGORY_B:
+          perf.push_back(PerformanceBracket(4000, 100, 1200, 100));
+          perf.push_back(PerformanceBracket(10000, 800, 1200, 160));
+          perf.push_back(PerformanceBracket(18000, 600, 1800, 200));
+          break;
+
+      case ICAO_AIRCRAFT_CATEGORY_C:
+          perf.push_back(PerformanceBracket(4000, 1800, 1800, 150));
+          perf.push_back(PerformanceBracket(10000, 1800, 1800, 200));
+          perf.push_back(PerformanceBracket(18000, 1200, 1800, 270));
+          perf.push_back(PerformanceBracket(60000, 800, 1200, 0.80, true /* is Mach */));
+          break;
+
+      case ICAO_AIRCRAFT_CATEGORY_D:
+      case ICAO_AIRCRAFT_CATEGORY_E:
+      default:
+
+          perf.push_back(PerformanceBracket(4000, 1800, 1800, 180));
+          perf.push_back(PerformanceBracket(10000, 1800, 1800, 230));
+          perf.push_back(PerformanceBracket(18000, 1200, 1800, 270));
+          perf.push_back(PerformanceBracket(60000, 800, 1200, 0.87, true /* is Mach */));
+          break;
+      }
   }
 
     const WayptData& previousValidWaypoint(int index) const
@@ -688,29 +720,20 @@ public:
 
 }; // of RoutePathPrivate class
 
-RoutePath::RoutePath(const flightgear::WayptVec& wpts) :
-  d(new RoutePathPrivate)
-{
-  WayptVec::const_iterator it;
-  for (it = wpts.begin(); it != wpts.end(); ++it) {
-    d->waypoints.push_back(WayptData(*it));
-  }
-  commonInit();
-}
-
 RoutePath::RoutePath(const flightgear::FlightPlan* fp) :
   d(new RoutePathPrivate)
 {
   for (int l=0; l<fp->numLegs(); ++l) {
      d->waypoints.push_back(WayptData(fp->legAtIndex(l)->waypoint()));
   }
+
+    d->aircraftCategory = fp->icaoAircraftCategory()[0];
+    d->constrainLegCourses = fp->followLegTrackToFixes();
   commonInit();
 }
 
 void RoutePath::commonInit()
 {
-  _pathTurnRate = 3.0; // 3 deg/sec = 180deg/min = standard rate turn
   d->initPerfData();
   
   WayptDataVec::iterator it;
@@ -734,7 +757,7 @@ void RoutePath::commonInit()
 
     double alt = 0.0; // FIXME
     double gs = d->groundSpeedForAltitude(alt);
-    double radiusM = ((360.0 / _pathTurnRate) * gs * SG_KT_TO_MPS) / SGMiscd::twopi();
+    double radiusM = ((360.0 / d->pathTurnRate) * gs * SG_KT_TO_MPS) / SGMiscd::twopi();
     
     if (i < (d->waypoints.size() - 1)) {
         int nextIndex = i + 1;
@@ -745,7 +768,7 @@ void RoutePath::commonInit()
         next.computeLegCourse(d->waypoints[i]);
 
       if (next.legCourseValid) {
-        d->waypoints[i].computeTurn(radiusM, prev, next);
+        d->waypoints[i].computeTurn(radiusM, d->constrainLegCourses, prev, next);
       } else {
         // next waypoint has indeterminate course. Let's create a sharp turn
         // this can happen when the following point is ATC vectors, for example.
@@ -861,7 +884,7 @@ SGGeodVec RoutePath::pathForHold(Hold* hold) const
   
   SGGeodVec r;
   double az2;
-  double stepTime = turnDelta / _pathTurnRate; // in seconds
+  double stepTime = turnDelta / d->pathTurnRate; // in seconds
   double stepDist = gsKts * (stepTime / 3600.0) * SG_NM_TO_METER;
   double legDist = hold->isDistance() ? 
     hold->timeOrDistance() 
index e94060607a29a5e5874bdbe4dc97a0b415c337b7..ac9e87130b5abd0e92624aa493e435348999edf8 100644 (file)
@@ -37,7 +37,6 @@ typedef std::vector<SGGeod> SGGeodVec;
 class RoutePath
 {
 public:
-  RoutePath(const flightgear::WayptVec& wpts);
   RoutePath(const flightgear::FlightPlan* fp);
   
   SGGeodVec pathForIndex(int index) const;
@@ -66,9 +65,6 @@ private:
   
   
   RoutePathPrivate* d;
-  
-  
-  double _pathTurnRate; ///< degrees-per-second, defaults to 3, i.e 180 in a minute
 };
 
 #endif
index b8e4e62dfca145bac0dd37b36db4790e11bbe05a..4b2af7f77be8c048f85a9435e87fc499768899af 100644 (file)
@@ -463,6 +463,10 @@ static void waypointCommonSetMember(naContext c, Waypt* wpt, const char* fieldNa
     }
     
     wpt->setFlag(f, true);
+  } else if (!strcmp(fieldName, "fly_type")) {
+      if (!naIsString(value)) naRuntimeError(c, "fly_type must be a string");
+      bool flyOver = (strcmp(naStr_data(value), "flyOver") == 0);
+      wpt->setFlag(WPT_OVERFLIGHT, flyOver);
   }
 }
 
@@ -569,6 +573,8 @@ static const char* flightplanGhostGetMember(naContext c, void* g, naRef field, n
   else if (!strcmp(fieldName, "star_trans")) *out = ghostForProcedure(c, fp->starTransition());
   else if (!strcmp(fieldName, "approach")) *out = ghostForProcedure(c, fp->approach());
   else if (!strcmp(fieldName, "current")) *out = naNum(fp->currentIndex());
+    else if (!strcmp(fieldName, "aircraftCategory")) *out = stringToNasal(c, fp->icaoAircraftCategory());
+    else if (!strcmp(fieldName, "followLegTrackToFix")) *out = naNum(fp->followLegTrackToFixes());
   else {
     return 0;
   }
@@ -691,6 +697,12 @@ static void flightplanGhostSetMember(naContext c, void* g, naRef field, naRef va
     }
     
     naRuntimeError(c, "bad argument type setting approach");
+  } else if (!strcmp(fieldName, "aircraftCategory")) {
+      if (!naIsString(value)) naRuntimeError(c, "aircraftCategory must be a string");
+      fp->setIcaoAircraftCategory(naStr_data(value));
+  } else if (!strcmp(fieldName, "followLegTrackToFix")) {
+      int b = (int) value.num;
+      fp->setFollowLegTrackToFixes(b);
   }
 }