1
0
Fork 0

Route-path bug fixes.

- explicit aircraft performance categories for turn radius
- allow overflight leg course behaviour to be selected
This commit is contained in:
James Turner 2015-01-08 19:46:04 +00:00
parent 55b092771f
commit e94371ebfc
7 changed files with 132 additions and 37 deletions

View 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));

View 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

View 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

View 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;

View 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()

View 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

View 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);
}
}