1
0
Fork 0

Start creating tests of AIModel code

First test just creates the manager, and ensures the user aircraft
updates in sync with the real aircraft.
This commit is contained in:
James Turner 2020-09-02 19:49:33 +01:00
parent ce1e85f656
commit d1fc4b58cb
15 changed files with 361 additions and 49 deletions

View file

@ -53,11 +53,10 @@ using std::endl;
//#include <Airports/trafficcontroller.hxx> //#include <Airports/trafficcontroller.hxx>
FGAIAircraft::FGAIAircraft(FGAISchedule *ref) : FGAIAircraft::FGAIAircraft(FGAISchedule* ref) : /* HOT must be disabled for AI Aircraft,
/* HOT must be disabled for AI Aircraft,
* otherwise traffic detection isn't working as expected.*/ * otherwise traffic detection isn't working as expected.*/
FGAIBaseAircraft(), FGAIBaseAircraft(),
_performance(0) _performance(nullptr)
{ {
trafficRef = ref; trafficRef = ref;
if (trafficRef) { if (trafficRef) {
@ -98,7 +97,8 @@ FGAIAircraft::FGAIAircraft(FGAISchedule *ref) :
if (perfDB) { if (perfDB) {
_performance = perfDB->getDefaultPerformance(); _performance = perfDB->getDefaultPerformance();
} else { } 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; takeOffStatus = 0;
@ -159,6 +159,11 @@ void FGAIAircraft::setPerformance(const std::string& acType, const std::string&
if (perfDB) { if (perfDB) {
_performance = perfDB->getDataFor(acType, acclass); _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 #if 0

View file

@ -137,6 +137,11 @@ public:
const simgear::BVHMaterial** material) const; const simgear::BVHMaterial** material) const;
double getTrueHeadingDeg() const
{
return hdg;
}
double _getCartPosX() const; double _getCartPosX() const;
double _getCartPosY() const; double _getCartPosY() const;
double _getCartPosZ() const; double _getCartPosZ() const;

View file

@ -1,7 +1,5 @@
#ifdef HAVE_CONFIG_H #include "config.h"
# include "config.h"
#endif
#include "performancedata.hxx" #include "performancedata.hxx"
@ -14,6 +12,13 @@
// to the AIAircraft. // to the AIAircraft.
#define BRAKE_SETTING 1.6 #define BRAKE_SETTING 1.6
PerformanceData* PerformanceData::getDefaultData()
{
static PerformanceData static_instance;
return &static_instance;
}
PerformanceData::PerformanceData() : PerformanceData::PerformanceData() :
_acceleration(4.0), _acceleration(4.0),
_deceleration(2.0), _deceleration(2.0),
@ -54,9 +59,6 @@ PerformanceData::PerformanceData(PerformanceData* clone) :
_maxbank = clone->_maxbank; _maxbank = clone->_maxbank;
} }
PerformanceData::~PerformanceData()
{}
// helper to try various names of a property, in order. // helper to try various names of a property, in order.
static double readRenamedProp(SGPropertyNode_ptr db, const string_list& namesToTry, double defValue) static double readRenamedProp(SGPropertyNode_ptr db, const string_list& namesToTry, double defValue)
{ {

View file

@ -1,9 +1,6 @@
#ifndef PERFORMANCEDATA_HXX #ifndef PERFORMANCEDATA_HXX
#define PERFORMANCEDATA_HXX #define PERFORMANCEDATA_HXX
#include <string>
#include <map>
class FGAIAircraft; class FGAIAircraft;
class SGPropertyNode; class SGPropertyNode;
@ -20,8 +17,8 @@ public:
PerformanceData(PerformanceData* clone); PerformanceData(PerformanceData* clone);
void initFromProps(SGPropertyNode* props); void initFromProps(SGPropertyNode* props);
~PerformanceData(); ~PerformanceData() = default;
double actualSpeed(FGAIAircraft* ac, double tgt_speed, double dt, bool needMaxBrake); double actualSpeed(FGAIAircraft* ac, double tgt_speed, double dt, bool needMaxBrake);
double actualBankAngle(FGAIAircraft* ac, double tgt_roll, double dt); double actualBankAngle(FGAIAircraft* ac, double tgt_roll, double dt);
@ -32,24 +29,31 @@ public:
bool gearExtensible(const FGAIAircraft* ac); bool gearExtensible(const FGAIAircraft* ac);
inline double climbRate () { return _climbRate; }; double climbRate() const { return _climbRate; };
inline double descentRate () { return _descentRate; }; double descentRate() const { return _descentRate; };
inline double vRotate () { return _vRotate; }; double vRotate() const { return _vRotate; };
inline double maximumBankAngle () { return _maxbank; }; double maximumBankAngle() const { return _maxbank; };
inline double acceleration () { return _acceleration; }; double acceleration() const { return _acceleration; };
inline double deceleration () { return _deceleration; }; double deceleration() const { return _deceleration; };
inline double vTaxi () { return _vTaxi; }; double vTaxi() const { return _vTaxi; };
inline double vTakeoff () { return _vTakeOff; }; double vTakeoff() const { return _vTakeOff; };
inline double vClimb () { return _vClimb; }; double vClimb() const { return _vClimb; };
inline double vDescent () { return _vDescent; }; double vDescent() const { return _vDescent; };
inline double vApproach () { return _vApproach; }; double vApproach() const { return _vApproach; };
inline double vTouchdown () { return _vTouchdown; }; double vTouchdown() const { return _vTouchdown; };
inline double vCruise () { return _vCruise; }; double vCruise() const { return _vCruise; };
inline double wingSpan () { return _wingSpan; }; double wingSpan() const { return _wingSpan; };
inline double wingChord () { return _wingChord; }; double wingChord() const { return _wingChord; };
inline double weight () { return _weight; }; double weight() const { return _weight; };
double decelerationOnGround() const; 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: private:
double _acceleration; double _acceleration;
double _deceleration; double _deceleration;

View file

@ -321,6 +321,25 @@ double computeMachFromIAS(int iasKnots, int altitudeFt)
return M; return M;
} }
double AircraftPerformance::machForCAS(int altitudeFt, double cas)
{
return computeMachFromIAS(static_cast<int>(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 int AircraftPerformance::Bracket::gsForAltitude(int altitude) const
{ {
double M = 0.0; double M = 0.0;
@ -330,10 +349,7 @@ int AircraftPerformance::Bracket::gsForAltitude(int altitude) const
M = computeMachFromIAS(speedIASOrMach, altitude); M = computeMachFromIAS(speedIASOrMach, altitude);
} }
// CS = sound speed= 38.967854*sqrt(T+273.15) where T is the OAT in celsius. return groundSpeedForMach(altitude, M);
const double CS = 38.967854 * sqrt(oatKForAltitudeFt(altitude));
const double TAS = M * CS;
return TAS;
} }
double AircraftPerformance::Bracket::climbTime(int alt1, int alt2) const double AircraftPerformance::Bracket::climbTime(int alt1, int alt2) const

View file

@ -55,6 +55,10 @@ public:
double timeToCruise(double cruiseDistanceNm, int cruiseAltitudeFt) const; 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: private:
void readPerformanceData(); void readPerformanceData();

View file

@ -25,6 +25,7 @@
#include <simgear/props/props.hxx> #include <simgear/props/props.hxx>
#include <simgear/math/SGGeod.hxx> #include <simgear/math/SGGeod.hxx>
#include <Aircraft/AircraftPerformance.hxx> // for formulae
#include <Main/globals.hxx> #include <Main/globals.hxx>
#include "TestDataLogger.hxx" #include "TestDataLogger.hxx"
@ -43,16 +44,10 @@ TestPilot::TestPilot(SGPropertyNode_ptr props) :
_lonProp = _propRoot->getNode("position/longitude-deg", true); _lonProp = _propRoot->getNode("position/longitude-deg", true);
_altitudeProp = _propRoot->getNode("position/altitude-ft", true); _altitudeProp = _propRoot->getNode("position/altitude-ft", true);
_headingProp = _propRoot->getNode("orientation/heading-deg", 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); _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); globals->add_subsystem("flight", this, SGSubsystemMgr::FDM);
} }
@ -232,7 +227,12 @@ void TestPilot::updateValues(double dt)
} }
SGGeod currentPos = globals->get_aircraft_position(); 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); SGGeod newPos = SGGeodesy::direct(currentPos, _trueCourseDeg, d);
if (_altActive) { if (_altActive) {

View file

@ -62,6 +62,7 @@ public:
void flyGPSCourseOffset(GPS *gps, double offsetNm); void flyGPSCourseOffset(GPS *gps, double offsetNm);
bool isOnHeading(double heading) const; bool isOnHeading(double heading) const;
private: private:
enum class LateralMode enum class LateralMode
{ {
@ -77,7 +78,7 @@ private:
SGPropertyNode_ptr _propRoot; SGPropertyNode_ptr _propRoot;
double _trueCourseDeg = 0.0; double _trueCourseDeg = 0.0;
double _speedKnots = 0.0; double _speedKnots = 0.0; // IAS
double _vspeedFPM = 0.0; double _vspeedFPM = 0.0;
bool _turnActive = false; bool _turnActive = false;
@ -96,7 +97,9 @@ private:
SGPropertyNode_ptr _headingProp; SGPropertyNode_ptr _headingProp;
SGPropertyNode_ptr _speedKnotsProp; SGPropertyNode_ptr _speedKnotsProp;
SGPropertyNode_ptr _verticalFPMProp; SGPropertyNode_ptr _verticalFPMProp;
SGPropertyNode_ptr _groundspeedKnotsProp;
SGPropertyNode_ptr _speedMachProp;
SGPropertyNode_ptr _gpsNode; SGPropertyNode_ptr _gpsNode;
SGPropertyNode_ptr _gpsLegCourse; SGPropertyNode_ptr _gpsLegCourse;
SGPropertyNode_ptr _courseErrorNm; SGPropertyNode_ptr _courseErrorNm;

View file

@ -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
)

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include "test_AIManager.hxx"
#include "test_traffic.hxx"
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(AIManagerTests, "Unit tests");
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(TrafficTests, "Unit tests");

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include "test_AIManager.hxx"
#include <cstring>
#include <memory>
#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 <AIModel/AIAircraft.hxx>
#include <AIModel/AIManager.hxx>
#include <Airports/airport.hxx>
#include <Main/fg_props.hxx>
#include <Main/globals.hxx>
/////////////////////////////////////////////////////////////////////////////
// Set up function for each test.
void AIManagerTests::setUp()
{
FGTestApi::setUp::initTestGlobals("AI");
FGTestApi::setUp::initNavDataCache();
globals->add_new_subsystem<FGAIManager>();
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<FGAIManager>();
auto bikf = FGAirport::findByIdent("BIKF");
auto pilot = SGSharedPtr<FGTestApi::TestPilot>(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);
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <cppunit/TestFixture.h>
#include <cppunit/extensions/HelperMacros.h>
#include <memory>
#include <simgear/props/props.hxx>
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();
};

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include "test_traffic.hxx"
#include <cstring>
#include <memory>
#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()
{
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <cppunit/TestFixture.h>
#include <cppunit/extensions/HelperMacros.h>
#include <memory>
#include <simgear/props/props.hxx>
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();
};

View file

@ -8,6 +8,7 @@ foreach( unit_test_category
Navaids Navaids
Instrumentation Instrumentation
Scripting Scripting
AI
) )
add_subdirectory(${unit_test_category}) add_subdirectory(${unit_test_category})