Route-path bug fixes.
- explicit aircraft performance categories for turn radius - allow overflight leg course behaviour to be selected
This commit is contained in:
parent
55b092771f
commit
e94371ebfc
7 changed files with 132 additions and 37 deletions
|
@ -70,7 +70,8 @@ GPS::Config::Config() :
|
||||||
_requireHardSurface(true),
|
_requireHardSurface(true),
|
||||||
_cdiMaxDeflectionNm(3.0), // linear mode, 3nm at the peg
|
_cdiMaxDeflectionNm(3.0), // linear mode, 3nm at the peg
|
||||||
_driveAutopilot(true),
|
_driveAutopilot(true),
|
||||||
_courseSelectable(false)
|
_courseSelectable(false),
|
||||||
|
_followLegTrackToFix(true)
|
||||||
{
|
{
|
||||||
_enableTurnAnticipation = false;
|
_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, "cdi-max-deflection-nm", SGRawValuePointer<double>(&_cdiMaxDeflectionNm));
|
||||||
aOwner->tie(aCfg, "drive-autopilot", SGRawValuePointer<bool>(&_driveAutopilot));
|
aOwner->tie(aCfg, "drive-autopilot", SGRawValuePointer<bool>(&_driveAutopilot));
|
||||||
aOwner->tie(aCfg, "course-selectable", SGRawValuePointer<bool>(&_courseSelectable));
|
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-distance-nm", SGRawValuePointer<double>(&_overflightDistance));
|
||||||
aOwner->tie(aCfg, "over-flight-arm-distance-nm", SGRawValuePointer<double>(&_overflightArmDistance));
|
aOwner->tie(aCfg, "over-flight-arm-distance-nm", SGRawValuePointer<double>(&_overflightArmDistance));
|
||||||
aOwner->tie(aCfg, "over-flight-arm-angle-deg", SGRawValuePointer<double>(&_overflightArmAngle));
|
aOwner->tie(aCfg, "over-flight-arm-angle-deg", SGRawValuePointer<double>(&_overflightArmAngle));
|
||||||
|
|
|
@ -139,6 +139,15 @@ private:
|
||||||
|
|
||||||
bool courseSelectable() const { return _courseSelectable; }
|
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:
|
private:
|
||||||
bool _enableTurnAnticipation;
|
bool _enableTurnAnticipation;
|
||||||
|
|
||||||
|
@ -168,6 +177,9 @@ private:
|
||||||
|
|
||||||
// is selected-course-deg read to set desired-course or not?
|
// is selected-course-deg read to set desired-course or not?
|
||||||
bool _courseSelectable;
|
bool _courseSelectable;
|
||||||
|
|
||||||
|
// do we fly direct to fixes, or follow the leg track closely?
|
||||||
|
bool _followLegTrackToFix;
|
||||||
};
|
};
|
||||||
|
|
||||||
class SearchFilter : public FGPositioned::Filter
|
class SearchFilter : public FGPositioned::Filter
|
||||||
|
|
|
@ -64,6 +64,8 @@ static FPDelegateFactoryVec static_delegateFactories;
|
||||||
FlightPlan::FlightPlan() :
|
FlightPlan::FlightPlan() :
|
||||||
_delegateLock(0),
|
_delegateLock(0),
|
||||||
_currentIndex(-1),
|
_currentIndex(-1),
|
||||||
|
_followLegTrackToFix(true),
|
||||||
|
_aircraftCategory(ICAO_AIRCRAFT_CATEGORY_C),
|
||||||
_departureRunway(NULL),
|
_departureRunway(NULL),
|
||||||
_destinationRunway(NULL),
|
_destinationRunway(NULL),
|
||||||
_sid(NULL),
|
_sid(NULL),
|
||||||
|
@ -1360,4 +1362,35 @@ void FlightPlan::Delegate::runFinished()
|
||||||
endOfFlightPlan();
|
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
|
} // of namespace flightgear
|
||||||
|
|
|
@ -35,6 +35,12 @@ class FlightPlan;
|
||||||
|
|
||||||
typedef SGSharedPtr<FlightPlan> FlightPlanRef;
|
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
|
class FlightPlan : public RouteBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -44,6 +50,15 @@ public:
|
||||||
virtual std::string ident() const;
|
virtual std::string ident() const;
|
||||||
void setIdent(const std::string& s);
|
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;
|
FlightPlan* clone(const std::string& newIdent = std::string()) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -259,6 +274,8 @@ private:
|
||||||
|
|
||||||
std::string _ident;
|
std::string _ident;
|
||||||
int _currentIndex;
|
int _currentIndex;
|
||||||
|
bool _followLegTrackToFix;
|
||||||
|
char _aircraftCategory;
|
||||||
|
|
||||||
FGAirportRef _departure, _destination;
|
FGAirportRef _departure, _destination;
|
||||||
FGRunway* _departureRunway, *_destinationRunway;
|
FGRunway* _departureRunway, *_destinationRunway;
|
||||||
|
|
|
@ -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(!skipped);
|
||||||
assert(legCourseValid && next.legCourseValid);
|
assert(legCourseValid && next.legCourseValid);
|
||||||
|
@ -201,7 +201,7 @@ public:
|
||||||
// through turnAngle
|
// through turnAngle
|
||||||
double xtk = turnRadius * (1 - cos(turnAngle * SG_DEGREES_TO_RADIANS));
|
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
|
// next leg course is constrained. We need to swing back onto the
|
||||||
// desired course, using a compensation turn
|
// desired course, using a compensation turn
|
||||||
|
|
||||||
|
@ -410,8 +410,11 @@ class RoutePath::RoutePathPrivate
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
WayptDataVec waypoints;
|
WayptDataVec waypoints;
|
||||||
PerformanceBracketVec perf;
|
|
||||||
|
|
||||||
|
char aircraftCategory;
|
||||||
|
PerformanceBracketVec perf;
|
||||||
|
double pathTurnRate;
|
||||||
|
bool constrainLegCourses;
|
||||||
|
|
||||||
PerformanceBracketVec::const_iterator
|
PerformanceBracketVec::const_iterator
|
||||||
findPerformanceBracket(double altFt) const
|
findPerformanceBracket(double altFt) const
|
||||||
|
@ -621,10 +624,16 @@ public:
|
||||||
|
|
||||||
double groundSpeedForAltitude(double altitude) 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
|
||||||
if (0) {
|
if (0) {
|
||||||
PerformanceBracketVec::const_iterator it = findPerformanceBracket(altitude);
|
|
||||||
double mach;
|
double mach;
|
||||||
|
|
||||||
if (it->speedIsMach) {
|
if (it->speedIsMach) {
|
||||||
|
@ -652,8 +661,6 @@ public:
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return 250.0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
double distanceBetweenIndices(int from, int to) const
|
double distanceBetweenIndices(int from, int to) const
|
||||||
|
@ -669,11 +676,36 @@ public:
|
||||||
|
|
||||||
void initPerfData()
|
void initPerfData()
|
||||||
{
|
{
|
||||||
// assume category C/D aircraft for now
|
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(4000, 1800, 1800, 180));
|
||||||
perf.push_back(PerformanceBracket(10000, 1800, 1800, 230));
|
perf.push_back(PerformanceBracket(10000, 1800, 1800, 230));
|
||||||
perf.push_back(PerformanceBracket(18000, 1200, 1800, 270));
|
perf.push_back(PerformanceBracket(18000, 1200, 1800, 270));
|
||||||
perf.push_back(PerformanceBracket(60000, 800, 1200, 0.85, true /* is Mach */));
|
perf.push_back(PerformanceBracket(60000, 800, 1200, 0.87, true /* is Mach */));
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const WayptData& previousValidWaypoint(int index) const
|
const WayptData& previousValidWaypoint(int index) const
|
||||||
|
@ -688,29 +720,20 @@ public:
|
||||||
|
|
||||||
}; // of RoutePathPrivate class
|
}; // 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) :
|
RoutePath::RoutePath(const flightgear::FlightPlan* fp) :
|
||||||
d(new RoutePathPrivate)
|
d(new RoutePathPrivate)
|
||||||
{
|
{
|
||||||
for (int l=0; l<fp->numLegs(); ++l) {
|
for (int l=0; l<fp->numLegs(); ++l) {
|
||||||
d->waypoints.push_back(WayptData(fp->legAtIndex(l)->waypoint()));
|
d->waypoints.push_back(WayptData(fp->legAtIndex(l)->waypoint()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
d->aircraftCategory = fp->icaoAircraftCategory()[0];
|
||||||
|
d->constrainLegCourses = fp->followLegTrackToFixes();
|
||||||
commonInit();
|
commonInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RoutePath::commonInit()
|
void RoutePath::commonInit()
|
||||||
{
|
{
|
||||||
_pathTurnRate = 3.0; // 3 deg/sec = 180deg/min = standard rate turn
|
|
||||||
|
|
||||||
d->initPerfData();
|
d->initPerfData();
|
||||||
|
|
||||||
WayptDataVec::iterator it;
|
WayptDataVec::iterator it;
|
||||||
|
@ -734,7 +757,7 @@ void RoutePath::commonInit()
|
||||||
|
|
||||||
double alt = 0.0; // FIXME
|
double alt = 0.0; // FIXME
|
||||||
double gs = d->groundSpeedForAltitude(alt);
|
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)) {
|
if (i < (d->waypoints.size() - 1)) {
|
||||||
int nextIndex = i + 1;
|
int nextIndex = i + 1;
|
||||||
|
@ -745,7 +768,7 @@ void RoutePath::commonInit()
|
||||||
next.computeLegCourse(d->waypoints[i]);
|
next.computeLegCourse(d->waypoints[i]);
|
||||||
|
|
||||||
if (next.legCourseValid) {
|
if (next.legCourseValid) {
|
||||||
d->waypoints[i].computeTurn(radiusM, prev, next);
|
d->waypoints[i].computeTurn(radiusM, d->constrainLegCourses, prev, next);
|
||||||
} else {
|
} else {
|
||||||
// next waypoint has indeterminate course. Let's create a sharp turn
|
// next waypoint has indeterminate course. Let's create a sharp turn
|
||||||
// this can happen when the following point is ATC vectors, for example.
|
// this can happen when the following point is ATC vectors, for example.
|
||||||
|
@ -861,7 +884,7 @@ SGGeodVec RoutePath::pathForHold(Hold* hold) const
|
||||||
|
|
||||||
SGGeodVec r;
|
SGGeodVec r;
|
||||||
double az2;
|
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 stepDist = gsKts * (stepTime / 3600.0) * SG_NM_TO_METER;
|
||||||
double legDist = hold->isDistance() ?
|
double legDist = hold->isDistance() ?
|
||||||
hold->timeOrDistance()
|
hold->timeOrDistance()
|
||||||
|
|
|
@ -37,7 +37,6 @@ typedef std::vector<SGGeod> SGGeodVec;
|
||||||
class RoutePath
|
class RoutePath
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
RoutePath(const flightgear::WayptVec& wpts);
|
|
||||||
RoutePath(const flightgear::FlightPlan* fp);
|
RoutePath(const flightgear::FlightPlan* fp);
|
||||||
|
|
||||||
SGGeodVec pathForIndex(int index) const;
|
SGGeodVec pathForIndex(int index) const;
|
||||||
|
@ -66,9 +65,6 @@ private:
|
||||||
|
|
||||||
|
|
||||||
RoutePathPrivate* d;
|
RoutePathPrivate* d;
|
||||||
|
|
||||||
|
|
||||||
double _pathTurnRate; ///< degrees-per-second, defaults to 3, i.e 180 in a minute
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -463,6 +463,10 @@ static void waypointCommonSetMember(naContext c, Waypt* wpt, const char* fieldNa
|
||||||
}
|
}
|
||||||
|
|
||||||
wpt->setFlag(f, true);
|
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, "star_trans")) *out = ghostForProcedure(c, fp->starTransition());
|
||||||
else if (!strcmp(fieldName, "approach")) *out = ghostForProcedure(c, fp->approach());
|
else if (!strcmp(fieldName, "approach")) *out = ghostForProcedure(c, fp->approach());
|
||||||
else if (!strcmp(fieldName, "current")) *out = naNum(fp->currentIndex());
|
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 {
|
else {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -691,6 +697,12 @@ static void flightplanGhostSetMember(naContext c, void* g, naRef field, naRef va
|
||||||
}
|
}
|
||||||
|
|
||||||
naRuntimeError(c, "bad argument type setting approach");
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue