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:
parent
fdd509a080
commit
b85048db2d
12 changed files with 190 additions and 7 deletions
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>();
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Reference in a new issue