1
0
Fork 0

FlightPlan: don’t assert on invalid waypoints

When there is no first anchor waypoint (eg a runway or navaid), avoid
asserts from the RoutePath code.
This commit is contained in:
James Turner 2020-04-19 12:27:42 +01:00
parent 682293c0d0
commit 1a925b152d
3 changed files with 95 additions and 58 deletions

View file

@ -239,6 +239,10 @@ TurnInfo turnCenterAndAngleFromExit(const SGGeod& pt, double outHeadingDeg,
return r; return r;
} }
class WayptData;
using WayptDataVec = std::vector<WayptData>;
using WpDataIt = WayptDataVec::iterator;
class WayptData class WayptData
{ {
public: public:
@ -285,7 +289,7 @@ public:
} }
// compute leg courses for all static legs (both ends are fixed) // compute leg courses for all static legs (both ends are fixed)
void initPass1(const WayptData& previous, WayptData* next) void initPass1(const WayptData* previous, WayptData* next)
{ {
const auto ty = wpt->type(); const auto ty = wpt->type();
if ((ty == "vectors") || (ty == "discontinuity")) { if ((ty == "vectors") || (ty == "discontinuity")) {
@ -296,19 +300,21 @@ public:
} }
} }
const bool previousPosValid = previous && previous->posValid;
if (ty == "via") { if (ty == "via") {
// even though both ends may be known, we don't // even though both ends may be known, we don't
// want to compute a leg course for a VIA // want to compute a leg course for a VIA
} else if (posValid && !legCourseValid && previous.posValid) { } else if (posValid && !legCourseValid && previousPosValid) {
const auto prevWP = previous->wpt;
// check for duplicate points now // check for duplicate points now
if (previous.wpt->matches(wpt)) { if (prevWP->matches(wpt)) {
skipped = true; skipped = true;
} }
// we can compute leg course now // we can compute leg course now
if (previous.wpt->type() == "runway") { if (prevWP->type() == "runway") {
// use the runway departure end pos // use the runway departure end pos
FGRunway* rwy = static_cast<RunwayWaypt*>(previous.wpt.get())->runway(); FGRunway* rwy = static_cast<RunwayWaypt*>(prevWP.get())->runway();
legCourseTrue = SGGeodesy::courseDeg(rwy->end(), pos); legCourseTrue = SGGeodesy::courseDeg(rwy->end(), pos);
} else if (ty == "discontinuity") { } else if (ty == "discontinuity") {
// we want to keep the path across DISCONs sharp (no turn entry/exit) // we want to keep the path across DISCONs sharp (no turn entry/exit)
@ -316,51 +322,61 @@ public:
// could compute it // could compute it
} else if (ty != "runway") { } else if (ty != "runway") {
// need to wait to compute runway leg course // need to wait to compute runway leg course
legCourseTrue = SGGeodesy::courseDeg(previous.pos, pos); legCourseTrue = SGGeodesy::courseDeg(previous->pos, pos);
legCourseValid = true; legCourseValid = true;
} }
} // of not a VIA } // of not a VIA
} }
void computeLegCourse(const WayptData& previous, double radiusM) void computeLegCourse(const WayptData* previous, double radiusM)
{ {
if (legCourseValid) { if (legCourseValid) {
return; return;
} }
if ((wpt->type() == "via") || (wpt->type() == "discontinuty")) if ((wpt->type() == "via") || (wpt->type() == "discontinuty"))
{ {
// do nothing, we can't compute a valid leg course for these types // do nothing, we can't compute a valid leg course for these types
// we'll generate sharp turns in the path but that's no problem. // we'll generate sharp turns in the path but that's no problem.
} else if (wpt->type() == "runway") { return;
FGRunway* rwy = static_cast<RunwayWaypt*>(wpt.get())->runway(); }
flyOver = true;
if (!previous || !previous->posValid) {
TurnInfo ti = turnCenterAndAngleFromExit(rwy->threshold(), // all the cases below assume previous pas exists and has a valid position
rwy->headingDeg(), SG_LOG(SG_NAVAID, SG_WARN, "RoutePath: Asked to compute leg course, but, previous is invalid");
radiusM, previous.pos); return;
if (ti.valid) { }
legCourseTrue = ti.inboundCourseDeg;
turnEntryAngle = ti.turnAngleDeg; if (wpt->type() == "runway") {
turnEntryCenter = ti.turnCenter; FGRunway* rwy = static_cast<RunwayWaypt*>(wpt.get())->runway();
turnRadius = radiusM; flyOver = true;
hasEntry = true;
turnEntryPos = pointOnEntryTurnFromHeading(ti.inboundCourseDeg); TurnInfo ti = turnCenterAndAngleFromExit(rwy->threshold(),
} else { rwy->headingDeg(),
// couldn't compute entry, never mind radiusM, previous->pos);
legCourseTrue = SGGeodesy::courseDeg(previous.pos, rwy->threshold()); if (ti.valid) {
} legCourseTrue = ti.inboundCourseDeg;
legCourseValid = true; turnEntryAngle = ti.turnAngleDeg;
turnEntryCenter = ti.turnCenter;
turnRadius = radiusM;
hasEntry = true;
turnEntryPos = pointOnEntryTurnFromHeading(ti.inboundCourseDeg);
} else { } else {
if (posValid) { // couldn't compute entry, never mind
legCourseTrue = SGGeodesy::courseDeg(previous.pos, pos); legCourseTrue = SGGeodesy::courseDeg(previous->pos, rwy->threshold());
legCourseValid = true; }
} else if (isCourseConstrained()) { legCourseValid = true;
double magVar = magVarFor(previous.pos); return;
legCourseTrue = wpt->headingRadialDeg() + magVar; }
legCourseValid = true;
} // of pos not valid if (posValid) {
} // of general case legCourseTrue = SGGeodesy::courseDeg(previous->pos, pos);
legCourseValid = true;
} else if (isCourseConstrained()) {
double magVar = magVarFor(previous->pos);
legCourseTrue = wpt->headingRadialDeg() + magVar;
legCourseValid = true;
} // of pos not valid
} }
SGGeod pointOnEntryTurnFromHeading(double headingDeg) const SGGeod pointOnEntryTurnFromHeading(double headingDeg) const
@ -600,8 +616,6 @@ public:
bool flyOver; bool flyOver;
}; };
typedef std::vector<WayptData> WayptDataVec;
bool isDescentWaypoint(const WayptRef& wpt) bool isDescentWaypoint(const WayptRef& wpt)
{ {
return (wpt->flag(WPT_APPROACH) && !wpt->flag(WPT_MISS)) || wpt->flag(WPT_ARRIVAL); return (wpt->flag(WPT_APPROACH) && !wpt->flag(WPT_MISS)) || wpt->flag(WPT_ARRIVAL);
@ -618,9 +632,13 @@ public:
void computeDynamicPosition(int index) void computeDynamicPosition(int index)
{ {
auto previous(previousValidWaypoint(index)); auto previous(previousValidWaypoint(index));
if ((previous == waypoints.end()) || !previous->posValid);
{
SG_LOG(SG_NAVAID, SG_WARN, "couldn't compute position for dynamic waypoint: no preceeding valid waypoint");
return;
}
WayptRef wpt = waypoints[index].wpt; WayptRef wpt = waypoints[index].wpt;
assert(previous != waypoints.end());
assert(previous->posValid);
const std::string& ty(wpt->type()); const std::string& ty(wpt->type());
if (ty == "hdgToAlt") { if (ty == "hdgToAlt") {
@ -866,16 +884,16 @@ RoutePath::~RoutePath()
} }
void RoutePath::commonInit() void RoutePath::commonInit()
{ {
WayptDataVec::iterator it; for (auto& w : d->waypoints) {
for (it = d->waypoints.begin(); it != d->waypoints.end(); ++it) { w.initPass0();
it->initPass0();
} }
for (unsigned int i=1; i<d->waypoints.size(); ++i) { for (unsigned int i=1; i<d->waypoints.size(); ++i) {
WayptData* nextPtr = ((i + 1) < d->waypoints.size()) ? &d->waypoints[i+1] : nullptr; WayptData* nextPtr = ((i + 1) < d->waypoints.size()) ? &d->waypoints[i+1] : nullptr;
auto prev = d->previousValidWaypoint(i); auto prev = d->previousValidWaypoint(i);
d->waypoints[i].initPass1(*prev, nextPtr); WayptData* prevPtr = (prev == d->waypoints.end()) ? nullptr : &(*prev);
d->waypoints[i].initPass1(prevPtr, nextPtr);
} }
for (unsigned int i=0; i<d->waypoints.size(); ++i) { for (unsigned int i=0; i<d->waypoints.size(); ++i) {
@ -888,14 +906,15 @@ void RoutePath::commonInit()
if (i > 0) { if (i > 0) {
auto prevIt = d->previousValidWaypoint(i); auto prevIt = d->previousValidWaypoint(i);
assert(prevIt != d->waypoints.end()); WayptData* prevPtr = (prevIt == d->waypoints.end()) ? nullptr : &(*prevIt);
d->waypoints[i].computeLegCourse(*prevIt, radiusM);
d->waypoints[i].computeLegCourse(prevPtr, radiusM);
d->computeDynamicPosition(i); d->computeDynamicPosition(i);
} }
auto nextIt = d->nextValidWaypoint(i); auto nextIt = d->nextValidWaypoint(i);
if (nextIt != d->waypoints.end()) { if (nextIt != d->waypoints.end()) {
nextIt->computeLegCourse(d->waypoints[i], radiusM); nextIt->computeLegCourse(&(d->waypoints[i]), radiusM);
if (nextIt->legCourseValid) { if (nextIt->legCourseValid) {
d->waypoints[i].computeTurn(radiusM, d->constrainLegCourses, *nextIt); d->waypoints[i].computeTurn(radiusM, d->constrainLegCourses, *nextIt);
@ -1008,7 +1027,6 @@ SGGeodVec RoutePath::pathForVia(Via* via, int index) const
auto prevIt = d->previousValidWaypoint(index); auto prevIt = d->previousValidWaypoint(index);
if (prevIt == d->waypoints.end()) { if (prevIt == d->waypoints.end()) {
return SGGeodVec(); return SGGeodVec();
} }
WayptVec enrouteWaypoints = via->expandToWaypoints(prevIt->wpt); WayptVec enrouteWaypoints = via->expandToWaypoints(prevIt->wpt);
@ -1087,8 +1105,10 @@ double RoutePath::computeDistanceForIndex(int index) const
return 0.0; return 0.0;
} }
auto prevIt = d->previousValidWaypoint(index); auto prevIt = d->previousValidWaypoint(index);
assert(prevIt != d->waypoints.end()); if (prevIt == d->waypoints.end()) {
return 0.0;
}
double dist = SGGeodesy::distanceM(prevIt->turnExitPos, it->turnEntryPos); double dist = SGGeodesy::distanceM(prevIt->turnExitPos, it->turnEntryPos);
dist += prevIt->turnDistanceM(); dist += prevIt->turnDistanceM();

View file

@ -459,3 +459,18 @@ void FlightplanTests::testOnlyDiscontinuityRoute()
wpt = f->waypointFromString("EPL"); wpt = f->waypointFromString("EPL");
f->insertWayptAtIndex(wpt, 2); f->insertWayptAtIndex(wpt, 2);
} }
void FlightplanTests::testLeadingWPDynamic()
{
FlightPlanRef f = new FlightPlan;
// plan has no departure, so this discon is floating
f->insertWayptAtIndex(new Discontinuity(f), 0);
auto ha = new HeadingToAltitude(f, "TO_3000", 90);
ha->setAltitude(3000, RESTRICT_AT);
f->insertWayptAtIndex(ha, 1);
RoutePath rp1(f);
// distance will be invalid, but shouldn;t assert or crash :)
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, f->totalDistanceNm(), 0.001);
}

View file

@ -43,6 +43,7 @@ class FlightplanTests : public CppUnit::TestFixture
CPPUNIT_TEST(testLoadSaveMachRestriction); CPPUNIT_TEST(testLoadSaveMachRestriction);
CPPUNIT_TEST(testOnlyDiscontinuityRoute); CPPUNIT_TEST(testOnlyDiscontinuityRoute);
CPPUNIT_TEST(testBasicDiscontinuity); CPPUNIT_TEST(testBasicDiscontinuity);
CPPUNIT_TEST(testLeadingWPDynamic);
// CPPUNIT_TEST(testParseICAORoute); // CPPUNIT_TEST(testParseICAORoute);
// CPPUNIT_TEST(testParseICANLowLevelRoute); // CPPUNIT_TEST(testParseICANLowLevelRoute);
@ -70,6 +71,7 @@ public:
void testLoadSaveMachRestriction(); void testLoadSaveMachRestriction();
void testBasicDiscontinuity(); void testBasicDiscontinuity();
void testOnlyDiscontinuityRoute(); void testOnlyDiscontinuityRoute();
void testLeadingWPDynamic();
}; };
#endif // FG_FLIGHTPLAN_UNIT_TESTS_HXX #endif // FG_FLIGHTPLAN_UNIT_TESTS_HXX