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),
|
||||
_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));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue