_requireHardSurface(true),
_cdiMaxDeflectionNm(3.0), // linear mode, 3nm at the peg
_driveAutopilot(true),
- _courseSelectable(false)
+ _courseSelectable(false),
+ _followLegTrackToFix(true)
{
_enableTurnAnticipation = false;
}
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));
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:
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;
/**
std::string _ident;
int _currentIndex;
-
+ bool _followLegTrackToFix;
+ char _aircraftCategory;
+
FGAirportRef _departure, _destination;
FGRunway* _departureRunway, *_destinationRunway;
SGSharedPtr<SID> _sid;
}
}
- 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);
// 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
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
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) {
#endif
}
#endif
-
- return 250.0;
}
double distanceBetweenIndices(int from, int to) const
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
}; // 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;
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;
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.
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()
}
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);
}
}
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;
}
}
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);
}
}