1
0
Fork 0

GPS/FLightPlan test updates

- fix reseting of the NavData each test
- improve the test-pilot’s GPS following (reduce turn range close
   to the desired course)
- test DME intercepts
This commit is contained in:
James Turner 2020-04-20 14:50:24 +01:00
parent fdd509a080
commit b85048db2d
12 changed files with 190 additions and 7 deletions

View file

@ -452,6 +452,11 @@ bool FGAirport::TypeRunwayFilter::pass(FGPositioned* pos) const
return true;
}
void FGAirport::clearAirportsCache()
{
airportCache.clear();
}
//------------------------------------------------------------------------------
FGAirportRef FGAirport::findByIdent(const std::string& aIdent)
{

View file

@ -304,6 +304,8 @@ class FGAirport : public FGPositioned
flightgear::CommStationList commStationsOfType(FGPositioned::Type aTy) const;
flightgear::CommStationList commStations() const;
static void clearAirportsCache();
private:
static flightgear::AirportCache airportCache;

View file

@ -1264,12 +1264,17 @@ NavDataCache::NavDataCache()
NavDataCache::~NavDataCache()
{
assert(static_instance == this);
static_instance = NULL;
static_instance = nullptr;
d.reset();
// ensure we wip the airports cache too, or we'll get out
// of sync during tests
FGAirport::clearAirportsCache();
}
NavDataCache* NavDataCache::createInstance()
{
assert(static_instance == nullptr);
static_instance = new NavDataCache;
return static_instance;
}

View file

@ -9,6 +9,9 @@ namespace setUp {
void initNavDataCache()
{
if (flightgear::NavDataCache::instance())
return;
flightgear::NavDataCache* cache = flightgear::NavDataCache::createInstance();
if (cache->isRebuildRequired()) {
std::cerr << "Navcache rebuild for testing" << std::flush;

View file

@ -141,9 +141,24 @@ void TestPilot::updateValues(double dt)
}
// set how aggressively we try to correct our course
const double courseCorrectionFactor = 8.0;
double courseCorrectionFactor = 8.0;
double crossTrack = _gpsNode->getDoubleValue("wp/wp[1]/course-error-nm");
double correction = courseCorrectionFactor * deviationDeg;
SG_CLAMP_RANGE(correction, -45.0, 45.0);
double maxOffset = 45;
// don't make really sharp turns if we're getting close
if (fabs(crossTrack) < 1.0) {
maxOffset *= 0.5;
}
// *really* don't make sharp turns if we're getting really close :)
if (fabs(crossTrack) < 0.2) {
maxOffset *= 0.5;
}
SG_CLAMP_RANGE(correction, -maxOffset, maxOffset);
_targetCourseDeg += correction;
SG_NORMALIZE_RANGE(_targetCourseDeg, 0.0, 360.0);

View file

@ -214,6 +214,11 @@ void populateFPWithNasal(flightgear::FlightPlanRef f,
global_kmlStream << pos.getLongitudeDeg() << "," << pos.getLatitudeDeg() << " " << endl;
}
void rawLogCoordinate(const SGGeod& pos)
{
global_kmlStream << pos.getLongitudeDeg() << "," << pos.getLatitudeDeg() << " " << endl;
}
void endCurrentLineString()
{
global_lineStringOpen = false;
@ -308,6 +313,10 @@ void writeFlightPlanToKML(flightgear::FlightPlanRef fp)
for (int i=0; i<fp->numLegs(); ++i) {
SGGeodVec legPath = rpath.pathForIndex(i);
fullPath.insert(fullPath.end(), legPath.begin(), legPath.end());
auto wp = fp->legAtIndex(i)->waypoint();
SGGeod legWPPosition = wp->position();
writePointToKML("WP " + wp->ident(), legWPPosition);
}
writeGeodsToKML("FlightPlan", fullPath);
@ -328,6 +337,18 @@ void writeGeodsToKML(const std::string &label, const flightgear::SGGeodVec& geod
endCurrentLineString();
}
void writePointToKML(const std::string& ident, const SGGeod& pos)
{
global_kmlStream << "<Placemark>\n";
global_kmlStream << "<name>" << ident << "</name>\n";
global_kmlStream << "<Point>\n";
global_kmlStream << "<coordinates>\n";
rawLogCoordinate(pos);
global_kmlStream << "</coordinates>\n";
global_kmlStream << "</Point>\n";
global_kmlStream << "</Placemark>\n";
}
bool executeNasal(const std::string& code)
{
auto nasal = globals->get_subsystem<FGNasalSys>();

View file

@ -52,6 +52,7 @@ bool runForTimeWithCheck(double t, RunCheck check);
void writeFlightPlanToKML(flightgear::FlightPlanRef fp);
void writeGeodsToKML(const std::string &label, const flightgear::SGGeodVec& geods);
void writePointToKML(const std::string& ident, const SGGeod& pos);
bool executeNasal(const std::string& code);

View file

@ -766,7 +766,7 @@ void GPSTests::testOverflightSequencing()
FGTestApi::setUp::populateFPWithoutNasal(fp, "NZCH", "02", "NZAA", "05L",
"ALADA NS WB WN MAMOD KAPTI OH");
// FGTestApi::writeFlightPlanToKML(fp);
// FGTestApi::writeFlightPlanToKML(fp);
// takes the place of the Nasal delegates
auto testDelegate = new TestFPDelegate;
@ -1217,7 +1217,7 @@ void GPSTests::testTurnAnticipation()
void GPSTests::testRadialIntercept()
{
//FGTestApi::setUp::logPositionToKML("gps_radial_intercept");
// FGTestApi::setUp::logPositionToKML("gps_radial_intercept");
auto rm = globals->get_subsystem<FGRouteMgr>();
auto fp = new FlightPlan;
@ -1225,6 +1225,9 @@ void GPSTests::testRadialIntercept()
FGTestApi::setUp::populateFPWithoutNasal(fp, "LFKC", "36", "LIRF", "25", "BUNAX BEBEV AJO");
// set BUNAX as overflight
fp->legAtIndex(1)->waypoint()->setFlag(WPT_OVERFLIGHT);
// insert KC502 manually
fp->insertWayptAtIndex(new BasicWaypt(SGGeod::fromDeg(8.78333, 42.566), "KC502", fp), 1);
@ -1233,7 +1236,7 @@ void GPSTests::testRadialIntercept()
auto intc = new RadialIntercept(fp, "INTC", pos, 230, 5);
fp->insertWayptAtIndex(intc, 3);
// FGTestApi::writeFlightPlanToKML(fp);
//FGTestApi::writeFlightPlanToKML(fp);
// position slightly before BUNAX
SGGeod initPos = fp->pointAlongRoute(2, -3.0);
@ -1273,3 +1276,69 @@ void GPSTests::testRadialIntercept()
FGTestApi::runForTime(30.0);
}
void GPSTests::testDMEIntercept()
{
//FGTestApi::setUp::logPositionToKML("gps_dme_intercept");
auto rm = globals->get_subsystem<FGRouteMgr>();
auto fp = new FlightPlan;
rm->setFlightPlan(fp);
// manually consturct something close to the publichsed approach transition for EGPH ILS 06
FGTestApi::setUp::populateFPWithoutNasal(fp, "EGLL", "27R", "EGPH", "06", "SHAPP TLA");
// DMEIntercept(RouteBase* aOwner, const std::string& aIdent, const SGGeod& aPos,
// double aCourseDeg, double aDistanceNm);
auto dmeArc = new DMEIntercept(fp, "TLA-11", SGGeod::fromDeg(-3.352833, 55.499145), 323.0, 11.0);
fp->insertWayptAtIndex(dmeArc, 3);
// now a normal WP
fp->insertWayptAtIndex(new BasicWaypt(SGGeod::fromDeg(-3.636708, 55.689981), "D3230", fp), 4);
auto dmeArc2 = new DMEIntercept(fp, "TLA-20", SGGeod::fromDeg(-3.352833, 55.499145), 323.0, 20.0);
fp->insertWayptAtIndex(dmeArc2, 5);
// and another normal WP
fp->insertWayptAtIndex(new BasicWaypt(SGGeod::fromDeg(-3.751056, 55.766122), "D323U", fp), 6);
// and another one
fp->insertWayptAtIndex(new BasicWaypt(SGGeod::fromDeg(-3.690845, 55.841378), "CI06", fp), 7);
// FGTestApi::writeFlightPlanToKML(fp);
// position slightly before TLA
SGGeod initPos = fp->pointAlongRoute(2, -2.0);
// takes the place of the Nasal delegates
auto testDelegate = new TestFPDelegate;
testDelegate->thePlan = fp;
CPPUNIT_ASSERT(rm->activate());
fp->addDelegate(testDelegate);
auto gps = setupStandardGPS();
FGTestApi::setPositionAndStabilise(initPos);
auto gpsNode = globals->get_props()->getNode("instrumentation/gps");
gpsNode->setBoolValue("config/delegate-sequencing", true);
gpsNode->setStringValue("command", "leg");
fp->setCurrentIndex(2);
CPPUNIT_ASSERT_EQUAL(string{"TLA"}, string{gpsNode->getStringValue("wp/wp[1]/ID")});
// CPPUNIT_ASSERT_DOUBLES_EQUAL(312, gpsNode->getDoubleValue("wp/leg-true-course-deg"), 1.0);
auto pilot = SGSharedPtr<FGTestApi::TestPilot>(new FGTestApi::TestPilot);
pilot->resetAtPosition(initPos);
pilot->setCourseTrue(fp->legAtIndex(2)->courseDeg());
pilot->setSpeedKts(220);
pilot->flyGPSCourse(gps);
bool ok = FGTestApi::runForTimeWithCheck(1200.0, [fp]() {
return fp->currentIndex() == 8;
});
CPPUNIT_ASSERT(ok);
}

View file

@ -53,6 +53,7 @@ class GPSTests : public CppUnit::TestFixture
CPPUNIT_TEST(testDirectToLegOnFlightplanAndResumeBuiltin);
CPPUNIT_TEST(testBuiltinRevertToOBSAtEnd);
CPPUNIT_TEST(testRadialIntercept);
CPPUNIT_TEST(testDMEIntercept);
CPPUNIT_TEST_SUITE_END();
@ -86,6 +87,7 @@ public:
void testDirectToLegOnFlightplanAndResumeBuiltin();
void testBuiltinRevertToOBSAtEnd();
void testRadialIntercept();
void testDMEIntercept();
};
#endif // _FG_GPS_UNIT_TESTS_HXX

View file

@ -462,3 +462,60 @@ void RNAVProcedureTests::testEGPH_TLA6C()
CPPUNIT_ASSERT_EQUAL(std::string{"TLA"}, fp->legAtIndex(5)->waypoint()->ident());
CPPUNIT_ASSERT_EQUAL(std::string{"TLA"}, std::string{m_gpsNode->getStringValue("wp/wp[1]/ID")});
}
void RNAVProcedureTests::testLFKC_AJO1R()
{
auto lfkc = FGAirport::findByIdent("LFKC");
auto sid = lfkc->findSIDWithIdent("AJO1R");
// procedures not loaded, abandon test
if (!sid)
return;
// FGTestApi::setUp::logPositionToKML("procedure_LFKC_AJO1R");
auto rm = globals->get_subsystem<FGRouteMgr>();
auto fp = new FlightPlan;
auto testDelegate = new TestFPDelegate;
testDelegate->thePlan = fp;
fp->addDelegate(testDelegate);
rm->setFlightPlan(fp);
FGTestApi::setUp::populateFPWithNasal(fp, "LFKC", "36", "EGLL", "27R", "");
fp->setSID(sid);
CPPUNIT_ASSERT_EQUAL(std::string{"BEBEV"}, fp->legAtIndex(4)->waypoint()->ident());
CPPUNIT_ASSERT_EQUAL(std::string{"AJO"}, fp->legAtIndex(5)->waypoint()->ident());
double d = fp->legAtIndex(5)->distanceAlongRoute();
CPPUNIT_ASSERT_DOUBLES_EQUAL(72, d, 1.0); // ensure the route didn't blow up to 0,0
FGRunwayRef departureRunway = fp->departureRunway();
// FGTestApi::writeFlightPlanToKML(fp);
CPPUNIT_ASSERT(rm->activate());
setupStandardGPS();
FGTestApi::setPositionAndStabilise(departureRunway->threshold());
m_gpsNode->setStringValue("command", "leg");
auto pilot = SGSharedPtr<FGTestApi::TestPilot>(new FGTestApi::TestPilot);
pilot->resetAtPosition(globals->get_aircraft_position());
CPPUNIT_ASSERT_DOUBLES_EQUAL(departureRunway->headingDeg(), m_gpsNode->getDoubleValue("wp/leg-true-course-deg"), 0.5);
pilot->setCourseTrue(m_gpsNode->getDoubleValue("wp/leg-true-course-deg"));
pilot->setSpeedKts(220);
pilot->flyGPSCourse(m_gps);
FGTestApi::runForTime(20.0);
// check we're somewhere along the runway, on the centerline
// and still on waypoint zero
bool ok = FGTestApi::runForTimeWithCheck(180.0, [fp] () {
if (fp->currentIndex() == 1) {
return true;
}
return false;
});
CPPUNIT_ASSERT(ok);
}

View file

@ -41,7 +41,7 @@ class RNAVProcedureTests : public CppUnit::TestFixture
CPPUNIT_TEST(testEGPH_TLA6C);
CPPUNIT_TEST(testHeadingToAlt);
CPPUNIT_TEST(testUglyHeadingToAlt);
CPPUNIT_TEST(testLFKC_AJO1R);
CPPUNIT_TEST_SUITE_END();
void setPositionAndStabilise(const SGGeod& g);
@ -63,6 +63,7 @@ public:
void testEGPH_TLA6C();
void testHeadingToAlt();
void testUglyHeadingToAlt();
void testLFKC_AJO1R();
private:
GPS* m_gps = nullptr;
SGPropertyNode_ptr m_gpsNode;

View file

@ -495,6 +495,8 @@ void FlightplanTests::testRadialIntercept()
// replicate AJO1R departure
FlightPlanRef f = makeTestFP("LFKC", "36", "LIRF", "25", "BUNAX BEBEV AJO");
// set BUNAX as overflight
f->legAtIndex(1)->waypoint()->setFlag(WPT_OVERFLIGHT);
f->insertWayptAtIndex(new BasicWaypt(SGGeod::fromDeg(8.78333, 42.566), "KC502", f), 1);
SGGeod pos = SGGeod::fromDeg(8.445556,42.216944);