diff --git a/src/Navaids/routePath.cxx b/src/Navaids/routePath.cxx index 8aee6051e..de7d14fed 100644 --- a/src/Navaids/routePath.cxx +++ b/src/Navaids/routePath.cxx @@ -287,7 +287,8 @@ public: // compute leg courses for all static legs (both ends are fixed) void initPass1(const WayptData& previous, WayptData* next) { - if (wpt->type() == "vectors") { + const auto ty = wpt->type(); + if ((ty == "vectors") || (ty == "discontinuity")) { // relying on the fact vectors will be followed by a static fix/wpt if (next && next->posValid) { posValid = true; @@ -295,7 +296,7 @@ public: } } - if (wpt->type() == "via") { + if (ty == "via") { // even though both ends may be known, we don't // want to compute a leg course for a VIA } else if (posValid && !legCourseValid && previous.posValid) { @@ -309,7 +310,11 @@ public: // use the runway departure end pos FGRunway* rwy = static_cast(previous.wpt.get())->runway(); legCourseTrue = SGGeodesy::courseDeg(rwy->end(), pos); - } else if (wpt->type() != "runway") { + } else if (ty == "discontinuity") { + // we want to keep the path across DISCONs sharp (no turn entry/exit) + // so we keep the leg course invalid, even though we actually + // could compute it + } else if (ty != "runway") { // need to wait to compute runway leg course legCourseTrue = SGGeodesy::courseDeg(previous.pos, pos); legCourseValid = true; @@ -323,7 +328,7 @@ public: return; } - if ((wpt->type() == "discontinuity") || (wpt->type() == "via")) + if ((wpt->type() == "via") || (wpt->type() == "discontinuty")) { // 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. @@ -792,14 +797,13 @@ public: WayptDataVec::iterator previousValidWaypoint(unsigned int index) { - if (index == 0) { - return waypoints.end(); - } - - while (waypoints[--index].skipped) { - // waypoint zero should be unskippable, this assert verified that - assert(index > 0); - } + do { + if (index == 0) { + return waypoints.end(); + } + + --index; + } while (waypoints.at(index).skipped || (waypoints.at(index).wpt->type() == "discontinuity")); return waypoints.begin() + index; } @@ -822,7 +826,7 @@ public: } ++it; - while ((it != waypoints.end()) && it->skipped) { + while ((it != waypoints.end()) && (it->skipped || (it->wpt->type() == "discontinuity"))) { ++it; } return it; @@ -870,7 +874,8 @@ void RoutePath::commonInit() for (unsigned int i=1; iwaypoints.size(); ++i) { WayptData* nextPtr = ((i + 1) < d->waypoints.size()) ? &d->waypoints[i+1] : nullptr; - d->waypoints[i].initPass1(d->waypoints[i-1], nextPtr); + auto prev = d->previousValidWaypoint(i); + d->waypoints[i].initPass1(*prev, nextPtr); } for (unsigned int i=0; iwaypoints.size(); ++i) { @@ -1073,9 +1078,14 @@ double RoutePath::computeDistanceForIndex(int index) const return 0.0; } - if (it->wpt->type() == "via") { + const auto ty = it->wpt->type(); + if (ty == "via") { return distanceForVia(static_cast(it->wpt.get()), index); } + + if (ty == "discontinuity") { + return 0.0; + } auto prevIt = d->previousValidWaypoint(index); assert(prevIt != d->waypoints.end()); diff --git a/test_suite/FGTestApi/testGlobals.cxx b/test_suite/FGTestApi/testGlobals.cxx index 4144ecaa0..183aa2e9e 100644 --- a/test_suite/FGTestApi/testGlobals.cxx +++ b/test_suite/FGTestApi/testGlobals.cxx @@ -156,6 +156,10 @@ void populateFPWithoutNasal(flightgear::FlightPlanRef f, for (auto ws : simgear::strutils::split(waypoints)) { WayptRef wpt = f->waypointFromString(ws); + if (!wpt) { + SG_LOG(SG_NAVAID, SG_ALERT, "No waypoint created for:" << ws); + continue; + } f->insertWayptAtIndex(wpt, -1); } diff --git a/test_suite/unit_tests/Navaids/test_flightplan.cxx b/test_suite/unit_tests/Navaids/test_flightplan.cxx index fe5fbd7be..256ef6ffd 100644 --- a/test_suite/unit_tests/Navaids/test_flightplan.cxx +++ b/test_suite/unit_tests/Navaids/test_flightplan.cxx @@ -394,3 +394,68 @@ void FlightplanTests::testLoadSaveMachRestriction() CPPUNIT_ASSERT_EQUAL(8, f2->legAtIndex(2)->holdCount()); } + +void FlightplanTests::testBasicDiscontinuity() +{ + FlightPlanRef fp1 = makeTestFP("LIRF", "34L", "LEBL", "25R", + "ESINO GITRI BALEN MUREN TOSNU"); + + const auto tdBefore = fp1->totalDistanceNm(); + + + const SGGeod balenPos = fp1->legAtIndex(3)->waypoint()->position(); + const SGGeod murenPos = fp1->legAtIndex(4)->waypoint()->position(); + + // total distance should not change + fp1->insertWayptAtIndex(new Discontinuity(fp1), 4); // betwee BALEN and MUREN + + CPPUNIT_ASSERT_DOUBLES_EQUAL(tdBefore, fp1->totalDistanceNm(), 1.0); + + + CPPUNIT_ASSERT_DOUBLES_EQUAL(fp1->legAtIndex(4)->courseDeg(), + SGGeodesy::courseDeg(balenPos, murenPos), 0.1); + CPPUNIT_ASSERT_DOUBLES_EQUAL(fp1->legAtIndex(4)->distanceNm(), 0.0, 0.1); + CPPUNIT_ASSERT_DOUBLES_EQUAL(fp1->legAtIndex(5)->courseDeg(), + SGGeodesy::courseDeg(balenPos, murenPos), 0.1); + CPPUNIT_ASSERT_DOUBLES_EQUAL(fp1->legAtIndex(5)->distanceNm(), + SGGeodesy::distanceNm(balenPos, murenPos), 0.1); + + // remove the discontinuity + fp1->deleteIndex(4); + CPPUNIT_ASSERT_DOUBLES_EQUAL(fp1->legAtIndex(4)->courseDeg(), + SGGeodesy::courseDeg(balenPos, murenPos), 0.1); + CPPUNIT_ASSERT_DOUBLES_EQUAL(fp1->legAtIndex(4)->distanceNm(), + SGGeodesy::distanceNm(balenPos, murenPos), 0.1); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(tdBefore, fp1->totalDistanceNm(), 1.0); + +} + +void FlightplanTests::testOnlyDiscontinuityRoute() +{ + FlightPlanRef f = new FlightPlan; + FGAirportRef depApt = FGAirport::getByIdent("LFBD"); + f->setDeparture(depApt); + + + FGAirportRef destApt = FGAirport::getByIdent("EHAM"); + f->setDestination(destApt); + + f->insertWayptAtIndex(new Discontinuity(f), 0); + + RoutePath rp1(f); + + // discontinuity should act like a straight segment between preceeding and following + const double d = SGGeodesy::distanceNm(depApt->geod(), destApt->geod()); + // CPPUNIT_ASSERT_DOUBLES_EQUAL(rp1.distanceForIndex(0), d, 0.5); + + // start inserting waypoints ahead of the DISCON, Boeing FPL style + WayptRef wpt = f->waypointFromString("LMG"); + f->insertWayptAtIndex(wpt, 0); + + wpt = f->waypointFromString("KUKOR"); + f->insertWayptAtIndex(wpt, 1); + + wpt = f->waypointFromString("EPL"); + f->insertWayptAtIndex(wpt, 2); +} diff --git a/test_suite/unit_tests/Navaids/test_flightplan.hxx b/test_suite/unit_tests/Navaids/test_flightplan.hxx index 1be0b5209..ce7dd03b2 100644 --- a/test_suite/unit_tests/Navaids/test_flightplan.hxx +++ b/test_suite/unit_tests/Navaids/test_flightplan.hxx @@ -41,6 +41,8 @@ class FlightplanTests : public CppUnit::TestFixture CPPUNIT_TEST(testRoutPathWpt0Midflight); CPPUNIT_TEST(testRoutePathVec); CPPUNIT_TEST(testLoadSaveMachRestriction); + CPPUNIT_TEST(testOnlyDiscontinuityRoute); + CPPUNIT_TEST(testBasicDiscontinuity); // CPPUNIT_TEST(testParseICAORoute); // CPPUNIT_TEST(testParseICANLowLevelRoute); @@ -66,6 +68,8 @@ public: void testRoutPathWpt0Midflight(); void testRoutePathVec(); void testLoadSaveMachRestriction(); + void testBasicDiscontinuity(); + void testOnlyDiscontinuityRoute(); }; #endif // FG_FLIGHTPLAN_UNIT_TESTS_HXX