From d1fc4b58cb72f1fcdbf3ba5b3753d696734d4f47 Mon Sep 17 00:00:00 2001 From: James Turner Date: Wed, 2 Sep 2020 19:49:33 +0100 Subject: [PATCH] Start creating tests of AIModel code First test just creates the manager, and ensures the user aircraft updates in sync with the real aircraft. --- src/AIModel/AIAircraft.cxx | 15 ++-- src/AIModel/AIBase.hxx | 5 ++ src/AIModel/performancedata.cxx | 14 ++-- src/AIModel/performancedata.hxx | 48 ++++++----- src/Aircraft/AircraftPerformance.cxx | 24 +++++- src/Aircraft/AircraftPerformance.hxx | 4 + test_suite/FGTestApi/TestPilot.cxx | 20 ++--- test_suite/FGTestApi/TestPilot.hxx | 7 +- test_suite/unit_tests/AI/CMakeLists.txt | 15 ++++ test_suite/unit_tests/AI/TestSuite.cxx | 24 ++++++ test_suite/unit_tests/AI/test_AIManager.cxx | 89 +++++++++++++++++++++ test_suite/unit_tests/AI/test_AIManager.hxx | 49 ++++++++++++ test_suite/unit_tests/AI/test_traffic.cxx | 46 +++++++++++ test_suite/unit_tests/AI/test_traffic.hxx | 49 ++++++++++++ test_suite/unit_tests/CMakeLists.txt | 1 + 15 files changed, 361 insertions(+), 49 deletions(-) create mode 100644 test_suite/unit_tests/AI/CMakeLists.txt create mode 100644 test_suite/unit_tests/AI/TestSuite.cxx create mode 100644 test_suite/unit_tests/AI/test_AIManager.cxx create mode 100644 test_suite/unit_tests/AI/test_AIManager.hxx create mode 100644 test_suite/unit_tests/AI/test_traffic.cxx create mode 100644 test_suite/unit_tests/AI/test_traffic.hxx diff --git a/src/AIModel/AIAircraft.cxx b/src/AIModel/AIAircraft.cxx index 4b4704ca9..088670c0d 100644 --- a/src/AIModel/AIAircraft.cxx +++ b/src/AIModel/AIAircraft.cxx @@ -53,11 +53,10 @@ using std::endl; //#include -FGAIAircraft::FGAIAircraft(FGAISchedule *ref) : - /* HOT must be disabled for AI Aircraft, +FGAIAircraft::FGAIAircraft(FGAISchedule* ref) : /* HOT must be disabled for AI Aircraft, * otherwise traffic detection isn't working as expected.*/ - FGAIBaseAircraft(), - _performance(0) + FGAIBaseAircraft(), + _performance(nullptr) { trafficRef = ref; if (trafficRef) { @@ -98,7 +97,8 @@ FGAIAircraft::FGAIAircraft(FGAISchedule *ref) : if (perfDB) { _performance = perfDB->getDefaultPerformance(); } else { - SG_LOG(SG_AI, SG_ALERT, "no performance DB found"); + SG_LOG(SG_AI, SG_DEV_ALERT, "no performance DB loaded"); + _performance = PerformanceData::getDefaultData(); } takeOffStatus = 0; @@ -159,6 +159,11 @@ void FGAIAircraft::setPerformance(const std::string& acType, const std::string& if (perfDB) { _performance = perfDB->getDataFor(acType, acclass); } + + if (!_performance) { + SG_LOG(SG_AI, SG_DEV_ALERT, "no AI performance data found for: " << acType << "/" << acclass); + _performance = PerformanceData::getDefaultData(); + } } #if 0 diff --git a/src/AIModel/AIBase.hxx b/src/AIModel/AIBase.hxx index 18621bf84..d656290e8 100644 --- a/src/AIModel/AIBase.hxx +++ b/src/AIModel/AIBase.hxx @@ -137,6 +137,11 @@ public: const simgear::BVHMaterial** material) const; + double getTrueHeadingDeg() const + { + return hdg; + } + double _getCartPosX() const; double _getCartPosY() const; double _getCartPosZ() const; diff --git a/src/AIModel/performancedata.cxx b/src/AIModel/performancedata.cxx index f69c017b0..9143784dc 100644 --- a/src/AIModel/performancedata.cxx +++ b/src/AIModel/performancedata.cxx @@ -1,7 +1,5 @@ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif +#include "config.h" #include "performancedata.hxx" @@ -14,6 +12,13 @@ // to the AIAircraft. #define BRAKE_SETTING 1.6 + +PerformanceData* PerformanceData::getDefaultData() +{ + static PerformanceData static_instance; + return &static_instance; +} + PerformanceData::PerformanceData() : _acceleration(4.0), _deceleration(2.0), @@ -54,9 +59,6 @@ PerformanceData::PerformanceData(PerformanceData* clone) : _maxbank = clone->_maxbank; } -PerformanceData::~PerformanceData() -{} - // helper to try various names of a property, in order. static double readRenamedProp(SGPropertyNode_ptr db, const string_list& namesToTry, double defValue) { diff --git a/src/AIModel/performancedata.hxx b/src/AIModel/performancedata.hxx index 08e3f6e44..88d28fe95 100644 --- a/src/AIModel/performancedata.hxx +++ b/src/AIModel/performancedata.hxx @@ -1,9 +1,6 @@ #ifndef PERFORMANCEDATA_HXX #define PERFORMANCEDATA_HXX -#include -#include - class FGAIAircraft; class SGPropertyNode; @@ -20,8 +17,8 @@ public: PerformanceData(PerformanceData* clone); void initFromProps(SGPropertyNode* props); - - ~PerformanceData(); + + ~PerformanceData() = default; double actualSpeed(FGAIAircraft* ac, double tgt_speed, double dt, bool needMaxBrake); double actualBankAngle(FGAIAircraft* ac, double tgt_roll, double dt); @@ -32,24 +29,31 @@ public: bool gearExtensible(const FGAIAircraft* ac); - inline double climbRate () { return _climbRate; }; - inline double descentRate () { return _descentRate; }; - inline double vRotate () { return _vRotate; }; - inline double maximumBankAngle () { return _maxbank; }; - inline double acceleration () { return _acceleration; }; - inline double deceleration () { return _deceleration; }; - inline double vTaxi () { return _vTaxi; }; - inline double vTakeoff () { return _vTakeOff; }; - inline double vClimb () { return _vClimb; }; - inline double vDescent () { return _vDescent; }; - inline double vApproach () { return _vApproach; }; - inline double vTouchdown () { return _vTouchdown; }; - inline double vCruise () { return _vCruise; }; - inline double wingSpan () { return _wingSpan; }; - inline double wingChord () { return _wingChord; }; - inline double weight () { return _weight; }; - + double climbRate() const { return _climbRate; }; + double descentRate() const { return _descentRate; }; + double vRotate() const { return _vRotate; }; + double maximumBankAngle() const { return _maxbank; }; + double acceleration() const { return _acceleration; }; + double deceleration() const { return _deceleration; }; + double vTaxi() const { return _vTaxi; }; + double vTakeoff() const { return _vTakeOff; }; + double vClimb() const { return _vClimb; }; + double vDescent() const { return _vDescent; }; + double vApproach() const { return _vApproach; }; + double vTouchdown() const { return _vTouchdown; }; + double vCruise() const { return _vCruise; }; + double wingSpan() const { return _wingSpan; }; + double wingChord() const { return _wingChord; }; + double weight() const { return _weight; }; + double decelerationOnGround() const; + + /** + @brief Last-resort fallback performance data. This is to avoid special-casing + logic in the AIAircraft code, by ensuring we always have a valid _performance pointer. + */ + static PerformanceData* getDefaultData(); + private: double _acceleration; double _deceleration; diff --git a/src/Aircraft/AircraftPerformance.cxx b/src/Aircraft/AircraftPerformance.cxx index e228255da..12e5349d0 100644 --- a/src/Aircraft/AircraftPerformance.cxx +++ b/src/Aircraft/AircraftPerformance.cxx @@ -321,6 +321,25 @@ double computeMachFromIAS(int iasKnots, int altitudeFt) return M; } +double AircraftPerformance::machForCAS(int altitudeFt, double cas) +{ + return computeMachFromIAS(static_cast(cas), altitudeFt); +} + + +double AircraftPerformance::groundSpeedForCAS(int altitudeFt, double cas) +{ + return groundSpeedForMach(altitudeFt, computeMachFromIAS(cas, altitudeFt)); +} + +double AircraftPerformance::groundSpeedForMach(int altitudeFt, double mach) +{ + // CS = sound speed= 38.967854*sqrt(T+273.15) where T is the OAT in celsius. + const double CS = 38.967854 * sqrt(oatKForAltitudeFt(altitudeFt)); + const double TAS = mach * CS; + return TAS; +} + int AircraftPerformance::Bracket::gsForAltitude(int altitude) const { double M = 0.0; @@ -330,10 +349,7 @@ int AircraftPerformance::Bracket::gsForAltitude(int altitude) const M = computeMachFromIAS(speedIASOrMach, altitude); } - // CS = sound speed= 38.967854*sqrt(T+273.15) where T is the OAT in celsius. - const double CS = 38.967854 * sqrt(oatKForAltitudeFt(altitude)); - const double TAS = M * CS; - return TAS; + return groundSpeedForMach(altitude, M); } double AircraftPerformance::Bracket::climbTime(int alt1, int alt2) const diff --git a/src/Aircraft/AircraftPerformance.hxx b/src/Aircraft/AircraftPerformance.hxx index fcf6878fa..b1553e124 100644 --- a/src/Aircraft/AircraftPerformance.hxx +++ b/src/Aircraft/AircraftPerformance.hxx @@ -55,6 +55,10 @@ public: double timeToCruise(double cruiseDistanceNm, int cruiseAltitudeFt) const; + static double groundSpeedForCAS(int altitudeFt, double cas); + static double machForCAS(int altitudeFt, double cas); + static double groundSpeedForMach(int altitudeFt, double mach); + private: void readPerformanceData(); diff --git a/test_suite/FGTestApi/TestPilot.cxx b/test_suite/FGTestApi/TestPilot.cxx index fc8ce9309..fa8171277 100644 --- a/test_suite/FGTestApi/TestPilot.cxx +++ b/test_suite/FGTestApi/TestPilot.cxx @@ -25,6 +25,7 @@ #include #include +#include // for formulae #include
#include "TestDataLogger.hxx" @@ -43,16 +44,10 @@ TestPilot::TestPilot(SGPropertyNode_ptr props) : _lonProp = _propRoot->getNode("position/longitude-deg", true); _altitudeProp = _propRoot->getNode("position/altitude-ft", true); _headingProp = _propRoot->getNode("orientation/heading-deg", true); - _speedKnotsProp = _propRoot->getNode("velocities/speed-knots", true); + _speedKnotsProp = _propRoot->getNode("velocities/airspeed-kt", true); + _speedMachProp = _propRoot->getNode("velocities/mach", true); + _groundspeedKnotsProp = _propRoot->getNode("velocities/groundspeed-kt", true); _verticalFPMProp = _propRoot->getNode("velocities/vertical-fpm", true); - - - SGPropertyNode_ptr _latProp; - SGPropertyNode_ptr _lonProp; - SGPropertyNode_ptr _altitudeProp; - SGPropertyNode_ptr _headingProp; - SGPropertyNode_ptr _speedKnotsProp; - SGPropertyNode_ptr _verticalFPMProp; globals->add_subsystem("flight", this, SGSubsystemMgr::FDM); } @@ -232,7 +227,12 @@ void TestPilot::updateValues(double dt) } SGGeod currentPos = globals->get_aircraft_position(); - double d = _speedKnots * SG_KT_TO_MPS * dt; + + const double M = flightgear::AircraftPerformance::machForCAS(currentPos.getElevationFt(), _speedKnots); + _speedMachProp->setDoubleValue(M); + const double gs = flightgear::AircraftPerformance::groundSpeedForMach(currentPos.getElevationFt(), M); + _groundspeedKnotsProp->setDoubleValue(gs); + double d = gs * SG_KT_TO_MPS * dt; SGGeod newPos = SGGeodesy::direct(currentPos, _trueCourseDeg, d); if (_altActive) { diff --git a/test_suite/FGTestApi/TestPilot.hxx b/test_suite/FGTestApi/TestPilot.hxx index c8e6dab39..6087c8f7c 100644 --- a/test_suite/FGTestApi/TestPilot.hxx +++ b/test_suite/FGTestApi/TestPilot.hxx @@ -62,6 +62,7 @@ public: void flyGPSCourseOffset(GPS *gps, double offsetNm); bool isOnHeading(double heading) const; + private: enum class LateralMode { @@ -77,7 +78,7 @@ private: SGPropertyNode_ptr _propRoot; double _trueCourseDeg = 0.0; - double _speedKnots = 0.0; + double _speedKnots = 0.0; // IAS double _vspeedFPM = 0.0; bool _turnActive = false; @@ -96,7 +97,9 @@ private: SGPropertyNode_ptr _headingProp; SGPropertyNode_ptr _speedKnotsProp; SGPropertyNode_ptr _verticalFPMProp; - + SGPropertyNode_ptr _groundspeedKnotsProp; + SGPropertyNode_ptr _speedMachProp; + SGPropertyNode_ptr _gpsNode; SGPropertyNode_ptr _gpsLegCourse; SGPropertyNode_ptr _courseErrorNm; diff --git a/test_suite/unit_tests/AI/CMakeLists.txt b/test_suite/unit_tests/AI/CMakeLists.txt new file mode 100644 index 000000000..768974473 --- /dev/null +++ b/test_suite/unit_tests/AI/CMakeLists.txt @@ -0,0 +1,15 @@ + +set(TESTSUITE_SOURCES + ${TESTSUITE_SOURCES} + ${CMAKE_CURRENT_SOURCE_DIR}/TestSuite.cxx + ${CMAKE_CURRENT_SOURCE_DIR}/test_AIManager.cxx + ${CMAKE_CURRENT_SOURCE_DIR}/test_traffic.cxx + PARENT_SCOPE +) + +set(TESTSUITE_HEADERS + ${TESTSUITE_HEADERS} + ${CMAKE_CURRENT_SOURCE_DIR}/test_AIManager.cxx + ${CMAKE_CURRENT_SOURCE_DIR}/test_traffic.hxx + PARENT_SCOPE +) diff --git a/test_suite/unit_tests/AI/TestSuite.cxx b/test_suite/unit_tests/AI/TestSuite.cxx new file mode 100644 index 000000000..5297b1eec --- /dev/null +++ b/test_suite/unit_tests/AI/TestSuite.cxx @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2020 James Turner + * + * This file is part of the program FlightGear. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "test_AIManager.hxx" +#include "test_traffic.hxx" + +CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(AIManagerTests, "Unit tests"); +CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(TrafficTests, "Unit tests"); \ No newline at end of file diff --git a/test_suite/unit_tests/AI/test_AIManager.cxx b/test_suite/unit_tests/AI/test_AIManager.cxx new file mode 100644 index 000000000..d37891508 --- /dev/null +++ b/test_suite/unit_tests/AI/test_AIManager.cxx @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2020 James Turner + * + * This file is part of the program FlightGear. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "test_AIManager.hxx" + +#include +#include + +#include "test_suite/FGTestApi/NavDataCache.hxx" +#include "test_suite/FGTestApi/TestDataLogger.hxx" +#include "test_suite/FGTestApi/TestPilot.hxx" +#include "test_suite/FGTestApi/testGlobals.hxx" + +#include +#include +#include +#include
+#include
+ +///////////////////////////////////////////////////////////////////////////// + +// Set up function for each test. +void AIManagerTests::setUp() +{ + FGTestApi::setUp::initTestGlobals("AI"); + FGTestApi::setUp::initNavDataCache(); + + globals->add_new_subsystem(); + + auto props = globals->get_props(); + props->setBoolValue("sim/ai/enabled", true); + + globals->get_subsystem_mgr()->bind(); + globals->get_subsystem_mgr()->init(); + globals->get_subsystem_mgr()->postinit(); +} + +// Clean up after each test. +void AIManagerTests::tearDown() +{ + FGTestApi::tearDown::shutdownTestGlobals(); +} + +void AIManagerTests::testBasic() +{ + auto aim = globals->get_subsystem(); + + auto bikf = FGAirport::findByIdent("BIKF"); + + auto pilot = SGSharedPtr(new FGTestApi::TestPilot); + FGTestApi::setPosition(bikf->geod()); + pilot->resetAtPosition(bikf->geod()); + + + pilot->setSpeedKts(220); + pilot->setCourseTrue(0.0); + pilot->setTargetAltitudeFtMSL(10000); + + FGTestApi::runForTime(10.0); + + auto aiUserAircraft = aim->getUserAircraft(); + CPPUNIT_ASSERT(aiUserAircraft->isValid()); + CPPUNIT_ASSERT(!aiUserAircraft->getDie()); + + const SGGeod g = globals->get_aircraft_position(); + CPPUNIT_ASSERT_DOUBLES_EQUAL(g.getLongitudeDeg(), aiUserAircraft->getGeodPos().getLongitudeDeg(), 0.01); + CPPUNIT_ASSERT_DOUBLES_EQUAL(g.getLatitudeDeg(), aiUserAircraft->getGeodPos().getLatitudeDeg(), 0.01); + + // disable, the AI user aircraft doesn't track altitude?! + // CPPUNIT_ASSERT_DOUBLES_EQUAL(g.getElevationFt(), aiUserAircraft->getGeodPos().getElevationFt(), 1); + CPPUNIT_ASSERT_DOUBLES_EQUAL(fgGetDouble("orientation/heading-deg"), aiUserAircraft->getTrueHeadingDeg(), 1); + CPPUNIT_ASSERT_DOUBLES_EQUAL(fgGetDouble("velocities/groundspeed-kt"), aiUserAircraft->getSpeed(), 1); +} diff --git a/test_suite/unit_tests/AI/test_AIManager.hxx b/test_suite/unit_tests/AI/test_AIManager.hxx new file mode 100644 index 000000000..17c5c8e3e --- /dev/null +++ b/test_suite/unit_tests/AI/test_AIManager.hxx @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2020 James Turner + * + * This file is part of the program FlightGear. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include + +#include + +#include + +class SGGeod; + +// The flight plan unit tests. +class AIManagerTests : public CppUnit::TestFixture +{ + // Set up the test suite. + CPPUNIT_TEST_SUITE(AIManagerTests); + CPPUNIT_TEST(testBasic); + CPPUNIT_TEST_SUITE_END(); + + +public: + // Set up function for each test. + void setUp(); + + // Clean up after each test. + void tearDown(); + + // The tests. + void testBasic(); +}; diff --git a/test_suite/unit_tests/AI/test_traffic.cxx b/test_suite/unit_tests/AI/test_traffic.cxx new file mode 100644 index 000000000..d5d8e6266 --- /dev/null +++ b/test_suite/unit_tests/AI/test_traffic.cxx @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2020 James Turner + * + * This file is part of the program FlightGear. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "test_traffic.hxx" + +#include +#include + +#include "test_suite/FGTestApi/NavDataCache.hxx" +#include "test_suite/FGTestApi/TestDataLogger.hxx" +#include "test_suite/FGTestApi/testGlobals.hxx" + +///////////////////////////////////////////////////////////////////////////// + +// Set up function for each test. +void TrafficTests::setUp() +{ + FGTestApi::setUp::initTestGlobals("Traffic"); + FGTestApi::setUp::initNavDataCache(); +} + +// Clean up after each test. +void TrafficTests::tearDown() +{ + FGTestApi::tearDown::shutdownTestGlobals(); +} + +void TrafficTests::testBasic() +{ +} diff --git a/test_suite/unit_tests/AI/test_traffic.hxx b/test_suite/unit_tests/AI/test_traffic.hxx new file mode 100644 index 000000000..8d9aa8902 --- /dev/null +++ b/test_suite/unit_tests/AI/test_traffic.hxx @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2020 James Turner + * + * This file is part of the program FlightGear. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include + +#include + +#include + +class SGGeod; + +// The flight plan unit tests. +class TrafficTests : public CppUnit::TestFixture +{ + // Set up the test suite. + CPPUNIT_TEST_SUITE(TrafficTests); + CPPUNIT_TEST(testBasic); + CPPUNIT_TEST_SUITE_END(); + + +public: + // Set up function for each test. + void setUp(); + + // Clean up after each test. + void tearDown(); + + // The tests. + void testBasic(); +}; diff --git a/test_suite/unit_tests/CMakeLists.txt b/test_suite/unit_tests/CMakeLists.txt index da57f3367..d36b376ba 100644 --- a/test_suite/unit_tests/CMakeLists.txt +++ b/test_suite/unit_tests/CMakeLists.txt @@ -8,6 +8,7 @@ foreach( unit_test_category Navaids Instrumentation Scripting + AI ) add_subdirectory(${unit_test_category})