first commit
This commit is contained in:
25
test_suite/unit_tests/AI/CMakeLists.txt
Normal file
25
test_suite/unit_tests/AI/CMakeLists.txt
Normal file
@@ -0,0 +1,25 @@
|
||||
|
||||
set(TESTSUITE_SOURCES
|
||||
${TESTSUITE_SOURCES}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/TestSuite.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_AIFlightPlan.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_AIManager.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_traffic.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_TrafficMgr.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_groundnet.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_submodels.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_VectorMath.cxx
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
set(TESTSUITE_HEADERS
|
||||
${TESTSUITE_HEADERS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_AIFlightPlan.hxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_AIManager.hxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_traffic.hxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_TrafficMgr.hxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_groundnet.hxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_submodels.hxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_VectorMath.hxx
|
||||
PARENT_SCOPE
|
||||
)
|
||||
35
test_suite/unit_tests/AI/TestSuite.cxx
Normal file
35
test_suite/unit_tests/AI/TestSuite.cxx
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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_AIFlightPlan.hxx"
|
||||
#include "test_AIManager.hxx"
|
||||
#include "test_groundnet.hxx"
|
||||
#include "test_traffic.hxx"
|
||||
#include "test_TrafficMgr.hxx"
|
||||
#include "test_submodels.hxx"
|
||||
#include "test_AIFlightPlan.hxx"
|
||||
#include "test_VectorMath.hxx"
|
||||
|
||||
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(AIFlightPlanTests, "Unit tests");
|
||||
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(AIManagerTests, "Unit tests");
|
||||
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(GroundnetTests, "Unit tests");
|
||||
// CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(TrafficTests, "Unit tests");
|
||||
// CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(TrafficMgrTests, "Unit tests");
|
||||
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(SubmodelsTests, "Unit tests");
|
||||
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(VectorMathTests, "Unit tests");
|
||||
363
test_suite/unit_tests/AI/test_AIFlightPlan.cxx
Normal file
363
test_suite/unit_tests/AI/test_AIFlightPlan.cxx
Normal file
@@ -0,0 +1,363 @@
|
||||
/*
|
||||
* 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_AIFlightPlan.hxx"
|
||||
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
|
||||
#include "config.h"
|
||||
#include "test_suite/FGTestApi/testGlobals.hxx"
|
||||
#include "test_suite/FGTestApi/NavDataCache.hxx"
|
||||
#include "test_suite/FGTestApi/TestDataLogger.hxx"
|
||||
#include "test_suite/FGTestApi/TestPilot.hxx"
|
||||
|
||||
#include <AIModel/AIAircraft.hxx>
|
||||
#include <AIModel/AIFlightPlan.hxx>
|
||||
#include <AIModel/AIManager.hxx>
|
||||
|
||||
#include <Airports/airport.hxx>
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <Main/globals.hxx>
|
||||
#include <Navaids/NavDataCache.hxx>
|
||||
#include <Navaids/navrecord.hxx>
|
||||
|
||||
|
||||
using namespace flightgear;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Set up function for each test.
|
||||
void AIFlightPlanTests::setUp()
|
||||
{
|
||||
FGTestApi::setUp::initTestGlobals("AI");
|
||||
FGTestApi::setUp::initNavDataCache();
|
||||
|
||||
globals->add_new_subsystem<FGAIManager>(SGSubsystemMgr::GENERAL);
|
||||
|
||||
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 AIFlightPlanTests::tearDown()
|
||||
{
|
||||
FGTestApi::tearDown::shutdownTestGlobals();
|
||||
}
|
||||
|
||||
void AIFlightPlanTests::testAIFlightPlan()
|
||||
{
|
||||
std::unique_ptr<FGAIFlightPlan> aiFP(new FGAIFlightPlan);
|
||||
aiFP->setName("Bob");
|
||||
aiFP->setRunway("24");
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(string{"Bob"}, aiFP->getName());
|
||||
CPPUNIT_ASSERT_EQUAL(string{"24"}, aiFP->getRunway());
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(0, aiFP->getNrOfWayPoints());
|
||||
CPPUNIT_ASSERT_EQUAL(static_cast<FGAIWaypoint*>(nullptr), aiFP->getPreviousWaypoint());
|
||||
CPPUNIT_ASSERT_EQUAL(static_cast<FGAIWaypoint*>(nullptr), aiFP->getCurrentWaypoint());
|
||||
CPPUNIT_ASSERT_EQUAL(static_cast<FGAIWaypoint*>(nullptr), aiFP->getNextWaypoint());
|
||||
CPPUNIT_ASSERT_EQUAL(0, aiFP->getLeg());
|
||||
|
||||
FGPositioned::TypeFilter ty(FGPositioned::VOR);
|
||||
auto cache = flightgear::NavDataCache::instance();
|
||||
auto shannonVOR = cache->findClosestWithIdent("SHA", SGGeod::fromDeg(-8, 52), &ty);
|
||||
CPPUNIT_ASSERT_EQUAL(string{"SHANNON VOR-DME"}, shannonVOR->name());
|
||||
|
||||
auto wp1 = new FGAIWaypoint;
|
||||
wp1->setPos(shannonVOR->geod());
|
||||
wp1->setName("testWp_0");
|
||||
wp1->setOn_ground(true);
|
||||
wp1->setGear_down(true);
|
||||
wp1->setSpeed(100);
|
||||
|
||||
auto wp2 = new FGAIWaypoint;
|
||||
const auto g1 = SGGeodesy::direct(shannonVOR->geod(), 10.0, SG_NM_TO_METER * 5.0);
|
||||
wp2->setPos(g1);
|
||||
wp2->setName("upInTheAir");
|
||||
wp2->setOn_ground(false);
|
||||
wp2->setGear_down(true);
|
||||
wp2->setSpeed(150);
|
||||
|
||||
|
||||
aiFP->addWaypoint(wp1);
|
||||
aiFP->addWaypoint(wp2);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(2, aiFP->getNrOfWayPoints());
|
||||
CPPUNIT_ASSERT_EQUAL(wp1, aiFP->getCurrentWaypoint());
|
||||
CPPUNIT_ASSERT_EQUAL(wp2, aiFP->getNextWaypoint());
|
||||
CPPUNIT_ASSERT_EQUAL(0, aiFP->getLeg());
|
||||
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(10.0, aiFP->getBearing(wp1, wp2), 0.1);
|
||||
|
||||
time_t startTime = 1498;
|
||||
aiFP->setTime(startTime);
|
||||
CPPUNIT_ASSERT(!aiFP->isActive(1400));
|
||||
CPPUNIT_ASSERT(aiFP->isActive(1500));
|
||||
|
||||
aiFP->IncrementWaypoint(false);
|
||||
CPPUNIT_ASSERT_EQUAL(2, aiFP->getNrOfWayPoints());
|
||||
CPPUNIT_ASSERT_EQUAL(wp1, aiFP->getPreviousWaypoint());
|
||||
CPPUNIT_ASSERT_EQUAL(wp2, aiFP->getCurrentWaypoint());
|
||||
CPPUNIT_ASSERT_EQUAL(static_cast<FGAIWaypoint*>(nullptr), aiFP->getNextWaypoint());
|
||||
CPPUNIT_ASSERT_EQUAL(0, aiFP->getLeg());
|
||||
|
||||
auto wp3 = new FGAIWaypoint;
|
||||
auto diganWpt = cache->findClosestWithIdent("DIGAN", shannonVOR->geod(), nullptr);
|
||||
|
||||
wp3->setPos(diganWpt->geod());
|
||||
wp3->setName("overDIGAN");
|
||||
wp3->setOn_ground(false);
|
||||
wp3->setGear_down(false);
|
||||
wp3->setSpeed(180);
|
||||
|
||||
// check that adding a waypoint doesn't mess up the iterators or
|
||||
// current position
|
||||
aiFP->addWaypoint(wp3);
|
||||
CPPUNIT_ASSERT_EQUAL(3, aiFP->getNrOfWayPoints());
|
||||
CPPUNIT_ASSERT_EQUAL(wp1, aiFP->getPreviousWaypoint());
|
||||
CPPUNIT_ASSERT_EQUAL(wp2, aiFP->getCurrentWaypoint());
|
||||
CPPUNIT_ASSERT_EQUAL(wp3, aiFP->getNextWaypoint());
|
||||
CPPUNIT_ASSERT_EQUAL(0, aiFP->getLeg());
|
||||
|
||||
auto p3 = SGGeodesy::direct(diganWpt->geod(), 45, SG_NM_TO_METER * 4);
|
||||
p3.setElevationFt(12000);
|
||||
auto wp4 = new FGAIWaypoint;
|
||||
wp4->setPos(p3);
|
||||
wp4->setName("passDIGAN");
|
||||
wp4->setSpeed(200);
|
||||
aiFP->addWaypoint(wp4);
|
||||
|
||||
auto ingur = cache->findClosestWithIdent("INGUR", shannonVOR->geod(), nullptr);
|
||||
auto p4 = ingur->geod();
|
||||
p4.setElevationFt(16000);
|
||||
auto wp5 = new FGAIWaypoint;
|
||||
wp5->setPos(p4);
|
||||
wp5->setName("INGUR");
|
||||
wp5->setSpeed(250);
|
||||
aiFP->addWaypoint(wp5);
|
||||
|
||||
aiFP->IncrementWaypoint(false);
|
||||
CPPUNIT_ASSERT_EQUAL(5, aiFP->getNrOfWayPoints());
|
||||
CPPUNIT_ASSERT_EQUAL(wp2, aiFP->getPreviousWaypoint());
|
||||
CPPUNIT_ASSERT_EQUAL(wp3, aiFP->getCurrentWaypoint());
|
||||
CPPUNIT_ASSERT_EQUAL(wp4, aiFP->getNextWaypoint());
|
||||
CPPUNIT_ASSERT_EQUAL(0, aiFP->getLeg());
|
||||
|
||||
// let's increment to the end
|
||||
aiFP->IncrementWaypoint(false);
|
||||
aiFP->IncrementWaypoint(false);
|
||||
CPPUNIT_ASSERT_EQUAL(5, aiFP->getNrOfWayPoints());
|
||||
CPPUNIT_ASSERT_EQUAL(wp4, aiFP->getPreviousWaypoint());
|
||||
CPPUNIT_ASSERT_EQUAL(wp5, aiFP->getCurrentWaypoint());
|
||||
CPPUNIT_ASSERT_EQUAL(static_cast<FGAIWaypoint*>(nullptr), aiFP->getNextWaypoint());
|
||||
CPPUNIT_ASSERT_EQUAL(0, aiFP->getLeg());
|
||||
|
||||
// one more increment 'off the end'
|
||||
aiFP->IncrementWaypoint(false);
|
||||
CPPUNIT_ASSERT_EQUAL(5, aiFP->getNrOfWayPoints());
|
||||
CPPUNIT_ASSERT_EQUAL(wp5, aiFP->getPreviousWaypoint());
|
||||
CPPUNIT_ASSERT_EQUAL(static_cast<FGAIWaypoint*>(nullptr), aiFP->getCurrentWaypoint());
|
||||
CPPUNIT_ASSERT_EQUAL(static_cast<FGAIWaypoint*>(nullptr), aiFP->getNextWaypoint());
|
||||
|
||||
// should put us back on the last waypoint
|
||||
aiFP->DecrementWaypoint();
|
||||
CPPUNIT_ASSERT_EQUAL(5, aiFP->getNrOfWayPoints());
|
||||
CPPUNIT_ASSERT_EQUAL(wp4, aiFP->getPreviousWaypoint());
|
||||
CPPUNIT_ASSERT_EQUAL(wp5, aiFP->getCurrentWaypoint());
|
||||
CPPUNIT_ASSERT_EQUAL(static_cast<FGAIWaypoint*>(nullptr), aiFP->getNextWaypoint());
|
||||
CPPUNIT_ASSERT_EQUAL(0, aiFP->getLeg());
|
||||
|
||||
aiFP->DecrementWaypoint(); // back to wp4
|
||||
aiFP->DecrementWaypoint(); // back to wp3
|
||||
aiFP->DecrementWaypoint(); // back to wp2
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(5, aiFP->getNrOfWayPoints());
|
||||
CPPUNIT_ASSERT_EQUAL(wp1, aiFP->getPreviousWaypoint());
|
||||
CPPUNIT_ASSERT_EQUAL(wp2, aiFP->getCurrentWaypoint());
|
||||
CPPUNIT_ASSERT_EQUAL(wp3, aiFP->getNextWaypoint());
|
||||
CPPUNIT_ASSERT_EQUAL(0, aiFP->getLeg());
|
||||
|
||||
// restart to the beginning
|
||||
aiFP->restart();
|
||||
CPPUNIT_ASSERT_EQUAL(5, aiFP->getNrOfWayPoints());
|
||||
CPPUNIT_ASSERT_EQUAL(static_cast<FGAIWaypoint*>(nullptr), aiFP->getPreviousWaypoint());
|
||||
CPPUNIT_ASSERT_EQUAL(wp1, aiFP->getCurrentWaypoint());
|
||||
CPPUNIT_ASSERT_EQUAL(wp2, aiFP->getNextWaypoint());
|
||||
CPPUNIT_ASSERT_EQUAL(0, aiFP->getLeg());
|
||||
|
||||
// test increment with delete
|
||||
aiFP->IncrementWaypoint(true);
|
||||
CPPUNIT_ASSERT_EQUAL(5, aiFP->getNrOfWayPoints());
|
||||
CPPUNIT_ASSERT_EQUAL(wp1, aiFP->getPreviousWaypoint());
|
||||
CPPUNIT_ASSERT_EQUAL(wp2, aiFP->getCurrentWaypoint());
|
||||
CPPUNIT_ASSERT_EQUAL(wp3, aiFP->getNextWaypoint());
|
||||
|
||||
aiFP->IncrementWaypoint(true);
|
||||
CPPUNIT_ASSERT_EQUAL(4, aiFP->getNrOfWayPoints());
|
||||
CPPUNIT_ASSERT_EQUAL(wp2, aiFP->getPreviousWaypoint());
|
||||
CPPUNIT_ASSERT_EQUAL(wp3, aiFP->getCurrentWaypoint());
|
||||
CPPUNIT_ASSERT_EQUAL(wp4, aiFP->getNextWaypoint());
|
||||
|
||||
aiFP->IncrementWaypoint(true);
|
||||
CPPUNIT_ASSERT_EQUAL(3, aiFP->getNrOfWayPoints());
|
||||
CPPUNIT_ASSERT_EQUAL(wp3, aiFP->getPreviousWaypoint());
|
||||
CPPUNIT_ASSERT_EQUAL(wp4, aiFP->getCurrentWaypoint());
|
||||
CPPUNIT_ASSERT_EQUAL(wp5, aiFP->getNextWaypoint());
|
||||
|
||||
// let's run up to the end and check nothing explodes
|
||||
aiFP->IncrementWaypoint(true);
|
||||
CPPUNIT_ASSERT_EQUAL(2, aiFP->getNrOfWayPoints());
|
||||
CPPUNIT_ASSERT_EQUAL(wp4, aiFP->getPreviousWaypoint());
|
||||
CPPUNIT_ASSERT_EQUAL(wp5, aiFP->getCurrentWaypoint());
|
||||
CPPUNIT_ASSERT_EQUAL(static_cast<FGAIWaypoint*>(nullptr), aiFP->getNextWaypoint());
|
||||
|
||||
aiFP->IncrementWaypoint(true);
|
||||
CPPUNIT_ASSERT_EQUAL(1, aiFP->getNrOfWayPoints());
|
||||
CPPUNIT_ASSERT_EQUAL(wp5, aiFP->getPreviousWaypoint());
|
||||
CPPUNIT_ASSERT_EQUAL(static_cast<FGAIWaypoint*>(nullptr), aiFP->getCurrentWaypoint());
|
||||
CPPUNIT_ASSERT_EQUAL(static_cast<FGAIWaypoint*>(nullptr), aiFP->getNextWaypoint());
|
||||
}
|
||||
|
||||
void AIFlightPlanTests::testAIFlightPlanLeftCircle()
|
||||
{
|
||||
auto aiFP = new FGAIFlightPlan;
|
||||
aiFP->setName("Bob");
|
||||
aiFP->setRunway("24");
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(string{"Bob"}, aiFP->getName());
|
||||
CPPUNIT_ASSERT_EQUAL(string{"24"}, aiFP->getRunway());
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(0, aiFP->getNrOfWayPoints());
|
||||
CPPUNIT_ASSERT_EQUAL(static_cast<FGAIWaypoint*>(nullptr), aiFP->getPreviousWaypoint());
|
||||
CPPUNIT_ASSERT_EQUAL(static_cast<FGAIWaypoint*>(nullptr), aiFP->getCurrentWaypoint());
|
||||
CPPUNIT_ASSERT_EQUAL(static_cast<FGAIWaypoint*>(nullptr), aiFP->getNextWaypoint());
|
||||
CPPUNIT_ASSERT_EQUAL(0, aiFP->getLeg());
|
||||
|
||||
FGPositioned::TypeFilter ty(FGPositioned::VOR);
|
||||
auto cache = flightgear::NavDataCache::instance();
|
||||
auto shannonVOR = cache->findClosestWithIdent("SHA", SGGeod::fromDeg(-8, 52), &ty);
|
||||
CPPUNIT_ASSERT_EQUAL(string{"SHANNON VOR-DME"}, shannonVOR->name());
|
||||
auto wp1 = new FGAIWaypoint;
|
||||
wp1->setPos(shannonVOR->geod());
|
||||
wp1->setName("testWp_0");
|
||||
wp1->setOn_ground(true);
|
||||
wp1->setGear_down(true);
|
||||
wp1->setSpeed(10);
|
||||
aiFP->addWaypoint(wp1);
|
||||
|
||||
auto lastWp = wp1;
|
||||
|
||||
int course = 0;
|
||||
|
||||
for(int i = 1; i <= 10; i++) {
|
||||
auto wp = new FGAIWaypoint;
|
||||
course += 10;
|
||||
const auto g1 = SGGeodesy::direct(lastWp->getPos(), course, SG_NM_TO_METER * 5.0);
|
||||
wp->setPos(g1);
|
||||
wp->setName("testWp_" + std::to_string(i));
|
||||
wp->setOn_ground(true);
|
||||
wp->setGear_down(true);
|
||||
wp->setSpeed(10);
|
||||
aiFP->addWaypoint(wp);
|
||||
lastWp = wp;
|
||||
}
|
||||
CPPUNIT_ASSERT_EQUAL(aiFP->getNrOfWayPoints(), 11);
|
||||
}
|
||||
|
||||
void AIFlightPlanTests::testAIFlightPlanLoadXML()
|
||||
{
|
||||
const auto xml = R"(<?xml version="1.0" encoding="UTF-8"?>
|
||||
<PropertyList>
|
||||
<flightplan>
|
||||
<wp>
|
||||
<name>onGroundWP</name>
|
||||
<lat>57</lat>
|
||||
<lon>3</lon>
|
||||
<ktas>10</ktas>
|
||||
<on-ground>1</on-ground>
|
||||
</wp>
|
||||
<wp>
|
||||
<name>someWP</name>
|
||||
<lat>57</lat>
|
||||
<lon>4</lon>
|
||||
<ktas>200</ktas>
|
||||
<alt>8000</alt>
|
||||
</wp>
|
||||
<wp>
|
||||
<name>END</name>
|
||||
</wp>
|
||||
</flightplan>
|
||||
</PropertyList>
|
||||
)";
|
||||
|
||||
std::istringstream is(xml);
|
||||
|
||||
std::unique_ptr<FGAIFlightPlan> aiFP(new FGAIFlightPlan);
|
||||
bool ok = aiFP->readFlightplan(is, sg_location("In-memory test_ai_fp.xml"));
|
||||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(false, aiFP->getCurrentWaypoint()->getInAir());
|
||||
CPPUNIT_ASSERT_EQUAL(true, aiFP->getCurrentWaypoint()->getGear_down());
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, aiFP->getCurrentWaypoint()->getFlaps(), 0.1);
|
||||
|
||||
auto wp2 = aiFP->getNextWaypoint();
|
||||
CPPUNIT_ASSERT_EQUAL(true, wp2->getInAir());
|
||||
CPPUNIT_ASSERT_EQUAL(false, wp2->getGear_down());
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, wp2->getFlaps(), 0.1);
|
||||
}
|
||||
|
||||
void AIFlightPlanTests::testLeftTurnFlightplanXML()
|
||||
{
|
||||
std::unique_ptr<FGAIFlightPlan> aiFP(new FGAIFlightPlan);
|
||||
const auto fpath = SGPath::fromUtf8(FG_TEST_SUITE_DATA) / "AI"/"Flightplan"/"left_onground.xml";
|
||||
|
||||
std::fstream fs(fpath.c_str());
|
||||
|
||||
bool ok = aiFP->readFlightplan(fs, sg_location("In-memory test_ai_fp.xml"));
|
||||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(false, aiFP->getCurrentWaypoint()->getInAir());
|
||||
|
||||
auto wp2 = aiFP->getNextWaypoint();
|
||||
CPPUNIT_ASSERT_EQUAL(false, wp2->getInAir());
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(10.0, wp2->getSpeed(), 0.1);
|
||||
}
|
||||
|
||||
void AIFlightPlanTests::testRightTurnFlightplanXML()
|
||||
{
|
||||
std::unique_ptr<FGAIFlightPlan> aiFP(new FGAIFlightPlan);
|
||||
const auto fpath = SGPath::fromUtf8(FG_TEST_SUITE_DATA) / "AI"/"Flightplan"/"right_onground.xml";
|
||||
|
||||
std::fstream fs(fpath.c_str());
|
||||
|
||||
bool ok = aiFP->readFlightplan(fs, sg_location("In-memory test_ai_fp.xml"));
|
||||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(false, aiFP->getCurrentWaypoint()->getInAir());
|
||||
|
||||
auto wp2 = aiFP->getNextWaypoint();
|
||||
CPPUNIT_ASSERT_EQUAL(false, wp2->getInAir());
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(10.0, wp2->getSpeed(), 0.1);
|
||||
}
|
||||
57
test_suite/unit_tests/AI/test_AIFlightPlan.hxx
Normal file
57
test_suite/unit_tests/AI/test_AIFlightPlan.hxx
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Keith Paterson
|
||||
*
|
||||
* 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 AI flight plan unit tests.
|
||||
class AIFlightPlanTests : public CppUnit::TestFixture
|
||||
{
|
||||
// Set up the test suite.
|
||||
CPPUNIT_TEST_SUITE(AIFlightPlanTests);
|
||||
CPPUNIT_TEST(testAIFlightPlan);
|
||||
CPPUNIT_TEST(testAIFlightPlanLeftCircle);
|
||||
CPPUNIT_TEST(testAIFlightPlanLoadXML);
|
||||
CPPUNIT_TEST(testLeftTurnFlightplanXML);
|
||||
CPPUNIT_TEST(testRightTurnFlightplanXML);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
|
||||
public:
|
||||
// Set up function for each test.
|
||||
void setUp();
|
||||
|
||||
// Clean up after each test.
|
||||
void tearDown();
|
||||
|
||||
// The tests.
|
||||
void testAIFlightPlan();
|
||||
void testAIFlightPlanLeftCircle();
|
||||
void testAIFlightPlanLoadXML();
|
||||
void testLeftTurnFlightplanXML();
|
||||
void testRightTurnFlightplanXML();
|
||||
};
|
||||
131
test_suite/unit_tests/AI/test_AIManager.cxx
Normal file
131
test_suite/unit_tests/AI/test_AIManager.cxx
Normal file
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* 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/AIFlightPlan.hxx>
|
||||
#include <AIModel/AIManager.hxx>
|
||||
|
||||
#include <Airports/airport.hxx>
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <Main/globals.hxx>
|
||||
#include <Navaids/NavDataCache.hxx>
|
||||
#include <Navaids/navrecord.hxx>
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Set up function for each test.
|
||||
void AIManagerTests::setUp()
|
||||
{
|
||||
FGTestApi::setUp::initTestGlobals("AI");
|
||||
FGTestApi::setUp::initNavDataCache();
|
||||
|
||||
globals->add_new_subsystem<FGAIManager>(SGSubsystemMgr::GENERAL);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// test for AIFLightPlan leg / legEnd pieces.
|
||||
|
||||
void AIManagerTests::testAircraftWaypoints()
|
||||
{
|
||||
auto aim = globals->get_subsystem<FGAIManager>();
|
||||
|
||||
SGPropertyNode_ptr aircraftDefinition(new SGPropertyNode);
|
||||
aircraftDefinition->setStringValue("type", "aircraft");
|
||||
aircraftDefinition->setStringValue("callsign", "G-ARTA");
|
||||
// set class for performance data
|
||||
|
||||
|
||||
auto eggd = FGAirport::findByIdent("EGGD");
|
||||
aircraftDefinition->setDoubleValue("heading", 90.0);
|
||||
aircraftDefinition->setDoubleValue("latitude", eggd->geod().getLatitudeDeg());
|
||||
aircraftDefinition->setDoubleValue("longitude", eggd->geod().getLongitudeDeg());
|
||||
aircraftDefinition->setDoubleValue("altitude", 6000.0);
|
||||
aircraftDefinition->setDoubleValue("speed", 250.0); // IAS or TAS?
|
||||
|
||||
FGTestApi::setPositionAndStabilise(eggd->geod());
|
||||
|
||||
auto ai = aim->addObject(aircraftDefinition);
|
||||
CPPUNIT_ASSERT(ai);
|
||||
CPPUNIT_ASSERT_EQUAL(FGAIBase::object_type::otAircraft, ai->getType());
|
||||
CPPUNIT_ASSERT_EQUAL(std::string{"aircraft"}, std::string{ai->getTypeString()});
|
||||
|
||||
auto aiAircraft = static_cast<FGAIAircraft*>(ai.get());
|
||||
|
||||
const auto aiPos = aiAircraft->getGeodPos();
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(eggd->geod().getLatitudeDeg(), aiPos.getLatitudeDeg(), 0.01);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(eggd->geod().getLongitudeDeg(), aiPos.getLongitudeDeg(), 0.01);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(90.0, aiAircraft->getTrueHeadingDeg(), 1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(250.0, aiAircraft->getSpeed(), 1);
|
||||
|
||||
std::unique_ptr<FGAIFlightPlan> aiFP(new FGAIFlightPlan);
|
||||
ai->setFlightPlan(std::move(aiFP));
|
||||
}
|
||||
52
test_suite/unit_tests/AI/test_AIManager.hxx
Normal file
52
test_suite/unit_tests/AI/test_AIManager.hxx
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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(testAircraftWaypoints);
|
||||
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
|
||||
public:
|
||||
// Set up function for each test.
|
||||
void setUp();
|
||||
|
||||
// Clean up after each test.
|
||||
void tearDown();
|
||||
|
||||
// The tests.
|
||||
void testBasic();
|
||||
void testAircraftWaypoints();
|
||||
};
|
||||
152
test_suite/unit_tests/AI/test_TrafficMgr.cxx
Normal file
152
test_suite/unit_tests/AI/test_TrafficMgr.cxx
Normal file
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Keith Paterson
|
||||
*
|
||||
* 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 "config.h"
|
||||
|
||||
#include "test_TrafficMgr.hxx"
|
||||
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
|
||||
#include "test_suite/FGTestApi/NavDataCache.hxx"
|
||||
#include "test_suite/FGTestApi/TestDataLogger.hxx"
|
||||
#include "test_suite/FGTestApi/testGlobals.hxx"
|
||||
|
||||
#include <Airports/airport.hxx>
|
||||
#include <Traffic/TrafficMgr.hxx>
|
||||
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <Main/globals.hxx>
|
||||
|
||||
// Set up function for each test.
|
||||
void TrafficMgrTests::setUp()
|
||||
{
|
||||
FGTestApi::setUp::initTestGlobals("TrafficMgr");
|
||||
FGTestApi::setUp::initNavDataCache();
|
||||
fgSetBool("sim/ai/enabled", true);
|
||||
fgSetBool("sim/traffic-manager/enabled", true);
|
||||
fgSetBool("/environment/realwx/enabled", false);
|
||||
fgSetBool("/environment/metar/valid", false);
|
||||
//Otherwise TrafficMgr won't load
|
||||
fgSetBool("sim/signals/fdm-initialized", true);
|
||||
globals->set_fg_root(SGPath::fromUtf8(FG_TEST_SUITE_DATA));
|
||||
}
|
||||
|
||||
// Clean up after each test.
|
||||
void TrafficMgrTests::tearDown()
|
||||
{
|
||||
FGTestApi::tearDown::shutdownTestGlobals();
|
||||
}
|
||||
|
||||
void TrafficMgrTests::testParse() {
|
||||
globals->add_new_subsystem<FGTrafficManager>(SGSubsystemMgr::GENERAL);
|
||||
|
||||
globals->get_subsystem_mgr()->bind();
|
||||
globals->get_subsystem_mgr()->init();
|
||||
globals->get_subsystem_mgr()->postinit();
|
||||
|
||||
FGTrafficManager *tmgr = (FGTrafficManager *) globals->get_subsystem("traffic-manager");
|
||||
FGScheduledFlightVecIterator fltBegin, fltEnd;
|
||||
|
||||
for (size_t i = 0; i < 1000000; i++)
|
||||
{
|
||||
FGTestApi::runForTime(10.0);
|
||||
// We have to wait for async parser
|
||||
fltBegin = tmgr->getFirstFlight("TST_BN_2");
|
||||
fltEnd = tmgr->getLastFlight("TST_BN_2");
|
||||
if (fltBegin != fltEnd) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int counter = 0;
|
||||
for (FGScheduledFlightVecIterator i = fltBegin; i != fltEnd; i++) {
|
||||
cout << (*i)->getCallSign() << counter++ << endl;
|
||||
}
|
||||
CPPUNIT_ASSERT_EQUAL(2, counter);
|
||||
}
|
||||
|
||||
void TrafficMgrTests::testTrafficManager()
|
||||
{
|
||||
FGAirportRef egeo = FGAirport::getByIdent("EGEO");
|
||||
|
||||
fgSetString("/sim/presets/airport-id", "EGEO");
|
||||
|
||||
std::cout << globals->get_fg_root() << "\r\n";
|
||||
globals->set_fg_root(SGPath::fromUtf8(FG_TEST_SUITE_DATA));
|
||||
std::cout << globals->get_fg_root() << "\r\n";
|
||||
|
||||
fgSetBool("/sim/traffic-manager/enabled", true);
|
||||
fgSetBool("/sim/traffic-manager/active", false);
|
||||
fgSetBool("/sim/ai/enabled", true);
|
||||
fgSetBool("/environment/realwx/enabled", false);
|
||||
fgSetBool("/environment/metar/valid", false);
|
||||
fgSetBool("/sim/terrasync/ai-data-update-now", false);
|
||||
|
||||
fgSetBool("/sim/traffic-manager/instantaneous-action", true);
|
||||
fgSetBool("/sim/traffic-manager/heuristics", true);
|
||||
fgSetBool("/sim/traffic-manager/dumpdata", false);
|
||||
|
||||
fgSetBool("/sim/signals/fdm-initialized", true);
|
||||
|
||||
FGTestApi::setPositionAndStabilise(egeo->geod());
|
||||
|
||||
auto tmgr = globals->add_new_subsystem<FGTrafficManager>(SGSubsystemMgr::GENERAL);
|
||||
|
||||
tmgr->bind();
|
||||
tmgr->init();
|
||||
|
||||
for( int i = 0; i < 30; i++) {
|
||||
bool active = fgGetBool("/sim/traffic-manager/inited");
|
||||
// std::cout << "Inited " << "\t" << i << "\t" << active << "\r\n";
|
||||
FGTestApi::runForTime(5.0);
|
||||
if(active) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const SGPropertyNode *tm = fgGetNode("/sim/traffic-manager", true);
|
||||
|
||||
for (int i = 0; i < tm->nChildren(); i++) {
|
||||
const SGPropertyNode *model = tm->getChild(i);
|
||||
std::cout << "TM : " << model->getDisplayName() << "\t" << model->nChildren() << "\n";
|
||||
for (int g = 0; g < model->nChildren(); g++) {
|
||||
const SGPropertyNode *v;
|
||||
v = model->getChild(g);
|
||||
std::cout << "Node " << g << "\t" << v->getDisplayName() << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
FGTestApi::runForTime(360.0);
|
||||
|
||||
FGScheduledFlightVecIterator fltBegin, fltEnd;
|
||||
fltBegin = tmgr->getFirstFlight("HBR_BN_2");
|
||||
fltEnd = tmgr->getLastFlight("HBR_BN_2");
|
||||
|
||||
if (fltBegin == fltEnd) {
|
||||
CPPUNIT_FAIL("No Traffic found");
|
||||
}
|
||||
|
||||
int counter = 0;
|
||||
for (FGScheduledFlightVecIterator i = fltBegin; i != fltEnd; i++) {
|
||||
cout << (*i)->getDepartureAirport()->getId() << "\t" << (*i)->getArrivalAirport()->getId() << "\t" << (*i)->getDepartureTime() << "\n";
|
||||
counter++;
|
||||
}
|
||||
CPPUNIT_ASSERT_EQUAL(25, counter);
|
||||
}
|
||||
53
test_suite/unit_tests/AI/test_TrafficMgr.hxx
Normal file
53
test_suite/unit_tests/AI/test_TrafficMgr.hxx
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Keith Paterson
|
||||
*
|
||||
* 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;
|
||||
|
||||
class FGAIAircraft;
|
||||
|
||||
// The flight plan unit tests.
|
||||
class TrafficMgrTests : public CppUnit::TestFixture
|
||||
{
|
||||
// Set up the test suite.
|
||||
CPPUNIT_TEST_SUITE(TrafficMgrTests);
|
||||
CPPUNIT_TEST(testParse);
|
||||
CPPUNIT_TEST(testTrafficManager);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
|
||||
public:
|
||||
// Set up function for each test.
|
||||
void setUp();
|
||||
|
||||
// Clean up after each test.
|
||||
void tearDown();
|
||||
|
||||
// The tests.
|
||||
void testTrafficManager();
|
||||
void testParse();
|
||||
};
|
||||
84
test_suite/unit_tests/AI/test_VectorMath.cxx
Normal file
84
test_suite/unit_tests/AI/test_VectorMath.cxx
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Keith Paterson
|
||||
*
|
||||
* 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_VectorMath.hxx"
|
||||
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
|
||||
#include <cppunit/TestAssert.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "test_suite/FGTestApi/testGlobals.hxx"
|
||||
|
||||
#include <AIModel/VectorMath.hxx>
|
||||
|
||||
using namespace flightgear;
|
||||
|
||||
// Set up function for each test.
|
||||
void VectorMathTests::setUp()
|
||||
{
|
||||
}
|
||||
|
||||
// Clean up after each test.
|
||||
void VectorMathTests::tearDown()
|
||||
{
|
||||
}
|
||||
|
||||
void VectorMathTests::testInnerTanget()
|
||||
{
|
||||
double r1 = 10;
|
||||
double r2 = 10;
|
||||
// when the circles are dist appart the angle will be 45°
|
||||
double dist = 2 * r1 + 2 * r2;
|
||||
SGGeod m1 = SGGeod::fromDeg(9,51);
|
||||
SGGeod m2 = SGGeodesy::direct(m1, 90, dist);
|
||||
|
||||
auto angles = VectorMath::innerTangentsAngle(m1, m2, r1, r2);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL( 60, angles[0], 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL( 120, angles[1], 0.1);
|
||||
}
|
||||
|
||||
void VectorMathTests::testInnerTangent2()
|
||||
{
|
||||
double r1 = 10;
|
||||
double r2 = 10;
|
||||
// when the circles are dist appart the angle will be 45°
|
||||
double dist = 2 * r1 + 2 * r2;
|
||||
SGGeod m1 = SGGeod::fromDeg(9,51);
|
||||
SGGeod m2 = SGGeodesy::direct(m1, 0, dist);
|
||||
|
||||
auto angles = VectorMath::innerTangentsAngle(m1, m2, r1, r2);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL( 330, angles[0], 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL( 30, angles[1], 0.1);
|
||||
}
|
||||
|
||||
void VectorMathTests::testOuterTanget()
|
||||
{
|
||||
double r1 = 10;
|
||||
double r2 = 50;
|
||||
// when the circles are dist appart the angle will be 45°
|
||||
double dist = 40;
|
||||
SGGeod m1 = SGGeod::fromDeg(9,51);
|
||||
SGGeod m2 = SGGeodesy::direct(m1, 90, dist);
|
||||
|
||||
auto angles = VectorMath::outerTangentsAngle(m1, m2, r1, r2);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL( 45, angles[0], 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL( 135, angles[1], 0.1);
|
||||
}
|
||||
54
test_suite/unit_tests/AI/test_VectorMath.hxx
Normal file
54
test_suite/unit_tests/AI/test_VectorMath.hxx
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Keith Paterson
|
||||
*
|
||||
* 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 AI flight plan unit tests.
|
||||
class VectorMathTests : public CppUnit::TestFixture
|
||||
{
|
||||
// Set up the test suite.
|
||||
CPPUNIT_TEST_SUITE(VectorMathTests);
|
||||
CPPUNIT_TEST(testInnerTanget);
|
||||
CPPUNIT_TEST(testInnerTangent2);
|
||||
CPPUNIT_TEST(testOuterTanget);
|
||||
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
|
||||
public:
|
||||
// Set up function for each test.
|
||||
void setUp();
|
||||
|
||||
// Clean up after each test.
|
||||
void tearDown();
|
||||
|
||||
// The tests.
|
||||
void testInnerTanget();
|
||||
void testInnerTangent2();
|
||||
void testOuterTanget();
|
||||
};
|
||||
122
test_suite/unit_tests/AI/test_groundnet.cxx
Normal file
122
test_suite/unit_tests/AI/test_groundnet.cxx
Normal file
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Keith Paterson
|
||||
*
|
||||
* 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 "config.h"
|
||||
|
||||
#include "test_groundnet.hxx"
|
||||
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
#include "test_suite/FGTestApi/NavDataCache.hxx"
|
||||
#include "test_suite/FGTestApi/TestDataLogger.hxx"
|
||||
#include "test_suite/FGTestApi/testGlobals.hxx"
|
||||
|
||||
#include <AIModel/AIAircraft.hxx>
|
||||
#include <AIModel/AIFlightPlan.hxx>
|
||||
#include <AIModel/AIManager.hxx>
|
||||
#include <AIModel/performancedb.hxx>
|
||||
#include <Airports/airport.hxx>
|
||||
#include <Airports/airportdynamicsmanager.hxx>
|
||||
#include <Airports/groundnetwork.hxx>
|
||||
#include <Airports/parking.hxx>
|
||||
#include <Traffic/TrafficMgr.hxx>
|
||||
|
||||
#include <ATC/atc_mgr.hxx>
|
||||
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <Main/globals.hxx>
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Set up function for each test.
|
||||
void GroundnetTests::setUp()
|
||||
{
|
||||
FGTestApi::setUp::initTestGlobals("Traffic");
|
||||
FGTestApi::setUp::initNavDataCache();
|
||||
|
||||
|
||||
auto props = globals->get_props();
|
||||
props->setBoolValue("sim/ai/enabled", true);
|
||||
props->setBoolValue("sim/signals/fdm-initialized", false);
|
||||
|
||||
|
||||
// ensure EGPH has a valid ground net for parking testing
|
||||
FGAirport::clearAirportsCache();
|
||||
FGAirportRef egph = FGAirport::getByIdent("EGPH");
|
||||
egph->testSuiteInjectGroundnetXML(SGPath::fromUtf8(FG_TEST_SUITE_DATA) / "EGPH.groundnet.xml");
|
||||
|
||||
FGAirportRef ybbn = FGAirport::getByIdent("YBBN");
|
||||
ybbn->testSuiteInjectGroundnetXML(SGPath::fromUtf8(FG_TEST_SUITE_DATA) / "YBBN.groundnet.xml");
|
||||
|
||||
|
||||
globals->add_new_subsystem<PerformanceDB>(SGSubsystemMgr::GENERAL);
|
||||
globals->add_new_subsystem<FGATCManager>(SGSubsystemMgr::GENERAL);
|
||||
globals->add_new_subsystem<FGAIManager>(SGSubsystemMgr::GENERAL);
|
||||
globals->add_new_subsystem<flightgear::AirportDynamicsManager>(SGSubsystemMgr::GENERAL);
|
||||
|
||||
globals->get_subsystem_mgr()->bind();
|
||||
globals->get_subsystem_mgr()->init();
|
||||
globals->get_subsystem_mgr()->postinit();
|
||||
}
|
||||
|
||||
// Clean up after each test.
|
||||
void GroundnetTests::tearDown()
|
||||
{
|
||||
FGTestApi::tearDown::shutdownTestGlobals();
|
||||
}
|
||||
|
||||
void GroundnetTests::testShortestRoute()
|
||||
{
|
||||
FGAirportRef egph = FGAirport::getByIdent("EGPH");
|
||||
|
||||
FGGroundNetwork* network = egph->groundNetwork();
|
||||
FGParkingRef startParking = network->findParkingByName("main-apron10");
|
||||
FGRunwayRef runway = egph->getRunwayByIndex(0);
|
||||
FGTaxiNodeRef end = network->findNearestNodeOnRunwayEntry(runway->threshold());
|
||||
FGTaxiRoute route = network->findShortestRoute(startParking, end);
|
||||
CPPUNIT_ASSERT_EQUAL(true, network->exists());
|
||||
CPPUNIT_ASSERT_EQUAL(29, route.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests various find methods.
|
||||
*/
|
||||
|
||||
void GroundnetTests::testFind()
|
||||
{
|
||||
FGAirportRef ybbn = FGAirport::getByIdent("YBBN");
|
||||
|
||||
FGGroundNetwork* network = ybbn->groundNetwork();
|
||||
FGParkingRef startParking = network->findParkingByName("GA1");
|
||||
CPPUNIT_ASSERT_EQUAL(1020, startParking->getIndex());
|
||||
FGTaxiSegment* segment1 = network->findSegment(startParking, NULL);
|
||||
CPPUNIT_ASSERT(segment1);
|
||||
FGTaxiSegment* segment2 = network->findSegment(startParking, segment1->getEnd());
|
||||
CPPUNIT_ASSERT(segment2);
|
||||
FGTaxiNodeVector segmentList = network->findSegmentsFrom(startParking);
|
||||
CPPUNIT_ASSERT_EQUAL(2, (int)segmentList.size());
|
||||
CPPUNIT_ASSERT_EQUAL(1026, segmentList.front()->getIndex());
|
||||
CPPUNIT_ASSERT_EQUAL(1027, segmentList.back()->getIndex());
|
||||
FGTaxiSegment* pushForwardSegment = network->findSegmentByHeading(startParking, startParking->getHeading());
|
||||
CPPUNIT_ASSERT(pushForwardSegment);
|
||||
CPPUNIT_ASSERT_EQUAL(1027, pushForwardSegment->getEnd()->getIndex());
|
||||
}
|
||||
52
test_suite/unit_tests/AI/test_groundnet.hxx
Normal file
52
test_suite/unit_tests/AI/test_groundnet.hxx
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Keith Paterson
|
||||
*
|
||||
* 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 groundnet unit tests.
|
||||
class GroundnetTests : public CppUnit::TestFixture
|
||||
{
|
||||
// Set up the test suite.
|
||||
CPPUNIT_TEST_SUITE(GroundnetTests);
|
||||
CPPUNIT_TEST(testShortestRoute);
|
||||
CPPUNIT_TEST(testFind);
|
||||
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
|
||||
public:
|
||||
// Set up function for each test.
|
||||
void setUp();
|
||||
|
||||
// Clean up after each test.
|
||||
void tearDown();
|
||||
|
||||
// The tests.
|
||||
void testShortestRoute();
|
||||
void testFind();
|
||||
};
|
||||
259
test_suite/unit_tests/AI/test_submodels.cxx
Normal file
259
test_suite/unit_tests/AI/test_submodels.cxx
Normal file
@@ -0,0 +1,259 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Colin Geniet
|
||||
*
|
||||
* 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_submodels.hxx"
|
||||
|
||||
#include "test_suite/FGTestApi/NavDataCache.hxx"
|
||||
#include "test_suite/FGTestApi/TestPilot.hxx"
|
||||
#include "test_suite/FGTestApi/testGlobals.hxx"
|
||||
|
||||
#include <AIModel/AIAircraft.hxx>
|
||||
#include <AIModel/AIManager.hxx>
|
||||
#include <AIModel/submodel.hxx>
|
||||
|
||||
#include <Airports/airport.hxx>
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <Main/globals.hxx>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
using std::string;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Set up function for each test.
|
||||
void SubmodelsTests::setUp()
|
||||
{
|
||||
FGTestApi::setUp::initTestGlobals("Submodels");
|
||||
FGTestApi::setUp::initNavDataCache();
|
||||
|
||||
globals->append_aircraft_path(SGPath::fromUtf8(FG_TEST_SUITE_DATA) / "Aircraft");
|
||||
|
||||
auto props = globals->get_props();
|
||||
props->setBoolValue("sim/ai/enabled", true);
|
||||
props->setStringValue("sim/submodels/path", "Aircraft/Test/submodels.xml");
|
||||
|
||||
globals->add_new_subsystem<FGAIManager>(SGSubsystemMgr::GENERAL);
|
||||
globals->add_new_subsystem<FGSubmodelMgr>(SGSubsystemMgr::GENERAL);
|
||||
|
||||
globals->get_subsystem_mgr()->bind();
|
||||
globals->get_subsystem_mgr()->init();
|
||||
globals->get_subsystem_mgr()->postinit();
|
||||
}
|
||||
|
||||
// Clean up after each test.
|
||||
void SubmodelsTests::tearDown()
|
||||
{
|
||||
FGTestApi::tearDown::shutdownTestGlobals();
|
||||
}
|
||||
|
||||
void SubmodelsTests::testLoadXML()
|
||||
{
|
||||
auto props = globals->get_props();
|
||||
auto sm_node = props->getNode("ai/submodels");
|
||||
CPPUNIT_ASSERT(sm_node->hasChild("submodel", 0));
|
||||
sm_node = sm_node->getChild("submodel", 0);
|
||||
CPPUNIT_ASSERT_EQUAL(string{"testLoadXML"}, static_cast<string>(sm_node->getStringValue("name")));
|
||||
CPPUNIT_ASSERT_EQUAL(0, sm_node->getIntValue("id"));
|
||||
CPPUNIT_ASSERT_EQUAL(42, sm_node->getIntValue("count"));
|
||||
CPPUNIT_ASSERT(sm_node->getBoolValue("serviceable"));
|
||||
}
|
||||
|
||||
FGAIBase* SubmodelsTests::findAIModel(std::string &name) {
|
||||
auto ai_list = globals->get_subsystem<FGAIManager>()->get_ai_list();
|
||||
auto filter = [name](FGAIBase *model) { return model->_getName() == name; };
|
||||
return *std::find_if(ai_list.begin(), ai_list.end(), filter);
|
||||
}
|
||||
|
||||
int SubmodelsTests::countAIModels(std::string &name) {
|
||||
auto ai_list = globals->get_subsystem<FGAIManager>()->get_ai_list();
|
||||
auto filter = [name](FGAIBase *model) { return model->_getName() == name; };
|
||||
return static_cast<int>(std::count_if(ai_list.begin(), ai_list.end(), filter));
|
||||
}
|
||||
|
||||
void SubmodelsTests::testRelease()
|
||||
{
|
||||
auto props = globals->get_props();
|
||||
auto sm_node = props->getNode("ai/submodels/submodel[1]");
|
||||
std::string name = sm_node->getStringValue("name");
|
||||
|
||||
// Setup reasonable flight conditions.
|
||||
auto bikf = FGAirport::findByIdent("BIKF");
|
||||
auto pilot = SGSharedPtr<FGTestApi::TestPilot>(new FGTestApi::TestPilot);
|
||||
FGTestApi::setPosition(bikf->geod());
|
||||
pilot->resetAtPosition(bikf->geod());
|
||||
pilot->setSpeedKts(0);
|
||||
pilot->setCourseTrue(0.0);
|
||||
pilot->setTargetAltitudeFtMSL(0);
|
||||
|
||||
// Sanity check
|
||||
CPPUNIT_ASSERT_EQUAL(0, countAIModels(name));
|
||||
|
||||
// Don't release anything
|
||||
FGTestApi::runForTime(10.0);
|
||||
CPPUNIT_ASSERT_EQUAL(0, countAIModels(name));
|
||||
|
||||
// Release submodel
|
||||
sm_node->setBoolValue("trigger", true);
|
||||
FGTestApi::runForTime(1);
|
||||
sm_node->setBoolValue("trigger", false);
|
||||
FGTestApi::runForTime(1);
|
||||
CPPUNIT_ASSERT_EQUAL(1, countAIModels(name));
|
||||
|
||||
// Check validity
|
||||
auto sm = findAIModel(name);
|
||||
CPPUNIT_ASSERT(sm->isValid());
|
||||
CPPUNIT_ASSERT(!sm->getDie());
|
||||
|
||||
// Second time
|
||||
sm_node->setBoolValue("trigger", true);
|
||||
FGTestApi::runForTime(5);
|
||||
sm_node->setBoolValue("trigger", false);
|
||||
FGTestApi::runForTime(1);
|
||||
CPPUNIT_ASSERT_EQUAL(2, countAIModels(name));
|
||||
|
||||
// Let submodels expire
|
||||
FGTestApi::runForTime(20);
|
||||
CPPUNIT_ASSERT_EQUAL(0, countAIModels(name));
|
||||
|
||||
// Switch to repeat release
|
||||
sm_node->setBoolValue("repeat", true);
|
||||
sm_node->setBoolValue("trigger", true);
|
||||
FGTestApi::runForTime(4.2); // release interval is 1s
|
||||
sm_node->setBoolValue("trigger", false);
|
||||
CPPUNIT_ASSERT_EQUAL(4, countAIModels(name));
|
||||
|
||||
// Let submodels expire
|
||||
FGTestApi::runForTime(20);
|
||||
CPPUNIT_ASSERT_EQUAL(0, countAIModels(name));
|
||||
|
||||
// In repeat mode, release timer persists when trigger is false.
|
||||
// Currently it is at ~0.2s, so this must not release anything.
|
||||
sm_node->setBoolValue("trigger", true);
|
||||
FGTestApi::runForTime(0.5);
|
||||
sm_node->setBoolValue("trigger", false);
|
||||
CPPUNIT_ASSERT_EQUAL(0, countAIModels(name));
|
||||
|
||||
// Set limited count
|
||||
sm_node->setIntValue("count", 3);
|
||||
sm_node->setBoolValue("trigger", true);
|
||||
FGTestApi::runForTime(1);
|
||||
CPPUNIT_ASSERT_EQUAL(2, sm_node->getIntValue("count"));
|
||||
CPPUNIT_ASSERT_EQUAL(1, countAIModels(name));
|
||||
FGTestApi::runForTime(5);
|
||||
CPPUNIT_ASSERT_EQUAL(0, sm_node->getIntValue("count"));
|
||||
CPPUNIT_ASSERT_EQUAL(3, countAIModels(name));
|
||||
}
|
||||
|
||||
void SubmodelsTests::testInitialState()
|
||||
{
|
||||
auto props = globals->get_props();
|
||||
auto sm_node = props->getNode("ai/submodels/submodel[2]");
|
||||
std::string name = sm_node->getStringValue("name");
|
||||
|
||||
// Submodel parameters
|
||||
double x_offset = 10, y_offset = 6, z_offset = 1, yaw_offset = 30, pitch_offset = 50, speed = 100;
|
||||
|
||||
// Setup reasonable flight conditions.
|
||||
auto bikf = FGAirport::findByIdent("BIKF");
|
||||
auto pilot = SGSharedPtr<FGTestApi::TestPilot>(new FGTestApi::TestPilot);
|
||||
FGTestApi::setPosition(bikf->geod());
|
||||
pilot->resetAtPosition(bikf->geod());
|
||||
pilot->setSpeedKts(0);
|
||||
pilot->setCourseTrue(90);
|
||||
pilot->setTargetAltitudeFtMSL(0);
|
||||
props->setDoubleValue("/orientation/pitch-deg", 0);
|
||||
props->setDoubleValue("/orientation/roll-deg", 0);
|
||||
FGTestApi::runForTime(1);
|
||||
|
||||
sm_node->setBoolValue("trigger", true);
|
||||
// Run update loop with dt=0 to capture initial state.
|
||||
globals->get_subsystem<FGSubmodelMgr>()->update(0);
|
||||
globals->get_subsystem<FGAIManager>()->update(0);
|
||||
sm_node->setBoolValue("trigger", false);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(1, countAIModels(name));
|
||||
auto sm = findAIModel(name);
|
||||
auto sm_pos = sm->getCartPos();
|
||||
|
||||
auto ac_pos = globals->get_aircraft_position_cart(); //ac->getCartPosAt(SGVec3d(0, 0, 0));
|
||||
double heading, pitch, roll;
|
||||
globals->get_aircraft_orientation(heading, pitch, roll);
|
||||
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(90, heading, 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0, pitch, 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0, roll, 0.1);
|
||||
|
||||
// Submodel release point
|
||||
// Submodels offsets are in x-back,y-right,z-up frame.
|
||||
// Computation is in x-forward,y-irght,z-down frame.
|
||||
SGVec3d offset(-x_offset, y_offset, -z_offset);
|
||||
SGQuatd local_frame_rotation = SGQuatd::fromLonLat(globals->get_aircraft_position());
|
||||
local_frame_rotation *= SGQuatd::fromYawPitchRollDeg(heading, pitch, roll);
|
||||
offset = local_frame_rotation.backTransform(offset);
|
||||
auto release_pos = ac_pos + offset;
|
||||
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(release_pos.x(), sm_pos.x(), 0.01);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(release_pos.y(), sm_pos.y(), 0.01);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(release_pos.z(), sm_pos.z(), 0.01);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(remainder(heading + yaw_offset, 360), remainder(sm->getTrueHeadingDeg(), 360), 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(pitch + pitch_offset, sm->_getPitch(), 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(speed, sm->_getSpeed() * SG_KT_TO_FPS, 0.1);
|
||||
|
||||
// Let submodels expire
|
||||
FGTestApi::runForTime(20);
|
||||
CPPUNIT_ASSERT_EQUAL(0, countAIModels(name));
|
||||
|
||||
// Second test for velocities
|
||||
double speed_east = 100, wind_north = 20;
|
||||
pilot->setSpeedKts(100 * SG_FPS_TO_KT);
|
||||
props->setDoubleValue("/orientation/pitch-deg", 0);
|
||||
props->setDoubleValue("/orientation/roll-deg", 0);
|
||||
props->setDoubleValue("/velocities/speed-north-fps", 0);
|
||||
props->setDoubleValue("/velocities/speed-east-fps", speed_east);
|
||||
props->setDoubleValue("/velocities/speed-down-fps", 0);
|
||||
props->setDoubleValue("/environment/wind-from-north-fps", wind_north);
|
||||
props->setDoubleValue("/environment/wind-from-east-fps", 0);
|
||||
FGTestApi::runForTime(1);
|
||||
|
||||
sm_node->setBoolValue("trigger", true);
|
||||
// Run update loop with dt=0 to capture initial state.
|
||||
globals->get_subsystem<FGSubmodelMgr>()->update(0);
|
||||
globals->get_subsystem<FGAIManager>()->update(0);
|
||||
sm_node->setBoolValue("trigger", false);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(1, countAIModels(name));
|
||||
sm = findAIModel(name);
|
||||
|
||||
// Check initial speeds.
|
||||
// _get_speed_*_fps gives airspeed for submodels, which initially must be the vector
|
||||
// parent_ground_velocity + opposed_wind_velocity + submodel_launch_velocity
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(cos((heading + yaw_offset) * SG_DEGREES_TO_RADIANS)
|
||||
* cos((pitch + pitch_offset) * SG_DEGREES_TO_RADIANS)
|
||||
* speed
|
||||
+ wind_north,
|
||||
sm->_get_speed_north_fps(), 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(sin((heading + yaw_offset) * SG_DEGREES_TO_RADIANS)
|
||||
* cos((pitch + pitch_offset) * SG_DEGREES_TO_RADIANS)
|
||||
* speed
|
||||
+ speed_east,
|
||||
sm->_get_speed_east_fps(), 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(sin((pitch + pitch_offset) * SG_DEGREES_TO_RADIANS) * speed,
|
||||
sm->_getVS_fps(), 0.1);
|
||||
}
|
||||
57
test_suite/unit_tests/AI/test_submodels.hxx
Normal file
57
test_suite/unit_tests/AI/test_submodels.hxx
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Colin Geniet
|
||||
*
|
||||
* 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 <AIModel/AIBase.hxx>
|
||||
#include <simgear/props/props.hxx>
|
||||
|
||||
class SGGeod;
|
||||
|
||||
// The flight plan unit tests.
|
||||
class SubmodelsTests : public CppUnit::TestFixture
|
||||
{
|
||||
// Set up the test suite.
|
||||
CPPUNIT_TEST_SUITE(SubmodelsTests);
|
||||
CPPUNIT_TEST(testLoadXML);
|
||||
CPPUNIT_TEST(testRelease);
|
||||
CPPUNIT_TEST(testInitialState);
|
||||
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
|
||||
public:
|
||||
// Set up function for each test.
|
||||
void setUp();
|
||||
|
||||
// Clean up after each test.
|
||||
void tearDown();
|
||||
|
||||
// The tests.
|
||||
void testLoadXML();
|
||||
void testRelease();
|
||||
void testInitialState();
|
||||
|
||||
private:
|
||||
FGAIBase* findAIModel(std::string &name);
|
||||
int countAIModels(std::string &name);
|
||||
};
|
||||
1109
test_suite/unit_tests/AI/test_traffic.cxx
Normal file
1109
test_suite/unit_tests/AI/test_traffic.cxx
Normal file
File diff suppressed because it is too large
Load Diff
78
test_suite/unit_tests/AI/test_traffic.hxx
Normal file
78
test_suite/unit_tests/AI/test_traffic.hxx
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
class FGAIAircraft;
|
||||
|
||||
// The flight plan unit tests.
|
||||
class TrafficTests : public CppUnit::TestFixture
|
||||
{
|
||||
// Set up the test suite.
|
||||
CPPUNIT_TEST_SUITE(TrafficTests);
|
||||
CPPUNIT_TEST(testPushback);
|
||||
CPPUNIT_TEST(testPushbackCargo);
|
||||
CPPUNIT_TEST(testPushbackCargoInProgress);
|
||||
CPPUNIT_TEST(testPushbackCargoInProgressDownWind);
|
||||
CPPUNIT_TEST(testPushbackCargoInProgressNotBeyond);
|
||||
CPPUNIT_TEST(testPushbackCargoInProgressBeyond);
|
||||
CPPUNIT_TEST(testChangeRunway);
|
||||
CPPUNIT_TEST(testPushforward);
|
||||
CPPUNIT_TEST(testPushforwardSpeedy);
|
||||
CPPUNIT_TEST(testPushforwardParkYBBN);
|
||||
CPPUNIT_TEST(testPushforwardParkYBBNRepeatGa);
|
||||
CPPUNIT_TEST(testPushforwardParkYBBNRepeatGaDelayed);
|
||||
CPPUNIT_TEST(testPushforwardParkYBBNRepeatGate);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
public:
|
||||
// Set up function for each test.
|
||||
void setUp();
|
||||
|
||||
// Clean up after each test.
|
||||
void tearDown();
|
||||
|
||||
// Pushback Tests
|
||||
void testPushback();
|
||||
void testPushbackCargo();
|
||||
void testPushbackCargoInProgress();
|
||||
void testPushbackCargoInProgressDownWind();
|
||||
void testPushbackCargoInProgressNotBeyond();
|
||||
void testPushbackCargoInProgressBeyond();
|
||||
void testChangeRunway();
|
||||
//GA Tests with forward push
|
||||
void testPushforward();
|
||||
void testPushforwardSpeedy();
|
||||
void testPushforwardParkYBBN();
|
||||
void testPushforwardParkYBBNRepeatGa();
|
||||
void testPushforwardParkYBBNRepeatGaDelayed();
|
||||
void testPushforwardParkYBBNRepeatGate();
|
||||
private:
|
||||
long currentWorldTime;
|
||||
std::string getTimeString(int timeOffset);
|
||||
FGAIAircraft * flyAI(SGSharedPtr<FGAIAircraft> aiAircraft, std::string fName);
|
||||
};
|
||||
12
test_suite/unit_tests/Add-ons/CMakeLists.txt
Normal file
12
test_suite/unit_tests/Add-ons/CMakeLists.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
set(TESTSUITE_SOURCES
|
||||
${TESTSUITE_SOURCES}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/TestSuite.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_AddonManagement.cxx
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
set(TESTSUITE_HEADERS
|
||||
${TESTSUITE_HEADERS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_AddonManagement.hxx
|
||||
PARENT_SCOPE
|
||||
)
|
||||
24
test_suite/unit_tests/Add-ons/TestSuite.cxx
Normal file
24
test_suite/unit_tests/Add-ons/TestSuite.cxx
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Edward d'Auvergne
|
||||
*
|
||||
* 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_AddonManagement.hxx"
|
||||
|
||||
|
||||
// Set up the unit tests.
|
||||
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(AddonManagementTests, "Unit tests");
|
||||
247
test_suite/unit_tests/Add-ons/test_AddonManagement.cxx
Normal file
247
test_suite/unit_tests/Add-ons/test_AddonManagement.cxx
Normal file
@@ -0,0 +1,247 @@
|
||||
// -*- coding: utf-8 -*-
|
||||
//
|
||||
// test_AddonManagement.cxx --- Automated tests for FlightGear classes dealing
|
||||
// with add-ons
|
||||
// Copyright (C) 2017 Florent Rougon
|
||||
//
|
||||
// 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, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
#include "test_AddonManagement.hxx"
|
||||
|
||||
#include "config.h"
|
||||
#include "test_suite/FGTestApi/testGlobals.hxx"
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
|
||||
#include "Add-ons/Addon.hxx"
|
||||
#include "Add-ons/AddonVersion.hxx"
|
||||
|
||||
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
using flightgear::addons::Addon;
|
||||
using flightgear::addons::AddonVersion;
|
||||
using flightgear::addons::AddonVersionSuffix;
|
||||
|
||||
|
||||
|
||||
void AddonManagementTests::testAddonVersionSuffix()
|
||||
{
|
||||
using AddonRelType = flightgear::addons::AddonVersionSuffixPrereleaseType;
|
||||
|
||||
FGTestApi::setUp::initTestGlobals("AddonVersionSuffix");
|
||||
|
||||
AddonVersionSuffix v1(AddonRelType::beta, 2, true, 5);
|
||||
AddonVersionSuffix v1Copy(v1);
|
||||
AddonVersionSuffix v1NonDev(AddonRelType::beta, 2, false);
|
||||
CPPUNIT_ASSERT_EQUAL(v1, v1Copy);
|
||||
CPPUNIT_ASSERT_EQUAL(v1, AddonVersionSuffix("b2.dev5"));
|
||||
CPPUNIT_ASSERT(v1.makeTuple() ==
|
||||
std::make_tuple(AddonRelType::beta, 2, true, 5));
|
||||
CPPUNIT_ASSERT_EQUAL(AddonVersionSuffix(), AddonVersionSuffix(""));
|
||||
// A simple comparison
|
||||
CPPUNIT_ASSERT(v1 < v1NonDev); // b2.dev5 < b2
|
||||
|
||||
// Check string representation with str()
|
||||
CPPUNIT_ASSERT(AddonVersionSuffix(AddonRelType::none).str() == "");
|
||||
CPPUNIT_ASSERT(AddonVersionSuffix(AddonRelType::none, 0, true, 12).str() ==
|
||||
".dev12");
|
||||
CPPUNIT_ASSERT(AddonVersionSuffix(AddonRelType::alpha, 1).str() == "a1");
|
||||
CPPUNIT_ASSERT(AddonVersionSuffix(AddonRelType::alpha, 1, false).str() == "a1");
|
||||
CPPUNIT_ASSERT(AddonVersionSuffix(AddonRelType::alpha, 2, true, 4).str() ==
|
||||
"a2.dev4");
|
||||
|
||||
CPPUNIT_ASSERT(AddonVersionSuffix(AddonRelType::beta, 1).str() == "b1");
|
||||
CPPUNIT_ASSERT(AddonVersionSuffix(AddonRelType::beta, 1, false).str() == "b1");
|
||||
CPPUNIT_ASSERT(AddonVersionSuffix(AddonRelType::beta, 2, true, 4).str() ==
|
||||
"b2.dev4");
|
||||
|
||||
CPPUNIT_ASSERT(AddonVersionSuffix(AddonRelType::candidate, 1).str() == "rc1");
|
||||
CPPUNIT_ASSERT(AddonVersionSuffix(AddonRelType::candidate, 1, false).str() ==
|
||||
"rc1");
|
||||
CPPUNIT_ASSERT(AddonVersionSuffix(AddonRelType::candidate, 2, true, 4).str() ==
|
||||
"rc2.dev4");
|
||||
|
||||
// Check stream output
|
||||
std::ostringstream oss;
|
||||
oss << AddonVersionSuffix(AddonRelType::candidate, 2, true, 4);
|
||||
CPPUNIT_ASSERT(oss.str() == "rc2.dev4");
|
||||
|
||||
// Check ordering with all types of transitions, using operator<()
|
||||
auto checkStrictOrdering = [](const vector<AddonVersionSuffix>& v) {
|
||||
assert(v.size() > 1);
|
||||
for (std::size_t i=0; i < v.size() - 1; i++) {
|
||||
CPPUNIT_ASSERT(v[i] < v[i+1]);
|
||||
}
|
||||
};
|
||||
|
||||
checkStrictOrdering({
|
||||
{AddonRelType::none, 0, true, 1},
|
||||
{AddonRelType::none, 0, true, 2},
|
||||
{AddonRelType::alpha, 1, true, 1},
|
||||
{AddonRelType::alpha, 1, true, 2},
|
||||
{AddonRelType::alpha, 1, true, 3},
|
||||
{AddonRelType::alpha, 1, false},
|
||||
{AddonRelType::alpha, 2, true, 1},
|
||||
{AddonRelType::alpha, 2, true, 3},
|
||||
{AddonRelType::alpha, 2, false},
|
||||
{AddonRelType::beta, 1, true, 1},
|
||||
{AddonRelType::beta, 1, true, 25},
|
||||
{AddonRelType::beta, 1, false},
|
||||
{AddonRelType::beta, 2, true, 1},
|
||||
{AddonRelType::beta, 2, true, 2},
|
||||
{AddonRelType::beta, 2, false},
|
||||
{AddonRelType::candidate, 1, true, 1},
|
||||
{AddonRelType::candidate, 1, true, 2},
|
||||
{AddonRelType::candidate, 1, false},
|
||||
{AddonRelType::candidate, 2, true, 1},
|
||||
{AddonRelType::candidate, 2, true, 2},
|
||||
{AddonRelType::candidate, 2, false},
|
||||
{AddonRelType::candidate, 21, false},
|
||||
{AddonRelType::none}
|
||||
});
|
||||
|
||||
// Check operator>() and operator!=()
|
||||
CPPUNIT_ASSERT(AddonVersionSuffix(AddonRelType::none) >
|
||||
AddonVersionSuffix(AddonRelType::candidate, 21, false));
|
||||
|
||||
CPPUNIT_ASSERT(AddonVersionSuffix(AddonRelType::none) !=
|
||||
AddonVersionSuffix(AddonRelType::candidate, 21, false));
|
||||
|
||||
// Check operator<=() and operator>=()
|
||||
CPPUNIT_ASSERT(AddonVersionSuffix(AddonRelType::candidate, 2, false) <=
|
||||
AddonVersionSuffix(AddonRelType::candidate, 2, false));
|
||||
CPPUNIT_ASSERT(AddonVersionSuffix(AddonRelType::candidate, 2, false) <=
|
||||
AddonVersionSuffix(AddonRelType::none));
|
||||
|
||||
CPPUNIT_ASSERT(AddonVersionSuffix(AddonRelType::none) >=
|
||||
AddonVersionSuffix(AddonRelType::none));
|
||||
CPPUNIT_ASSERT(AddonVersionSuffix(AddonRelType::none) >=
|
||||
AddonVersionSuffix(AddonRelType::candidate, 21, false));
|
||||
|
||||
FGTestApi::tearDown::shutdownTestGlobals();
|
||||
}
|
||||
|
||||
void AddonManagementTests::testAddonVersion()
|
||||
{
|
||||
using AddonRelType = flightgear::addons::AddonVersionSuffixPrereleaseType;
|
||||
|
||||
FGTestApi::setUp::initTestGlobals("AddonVersion");
|
||||
|
||||
AddonVersionSuffix suffix(AddonRelType::beta, 2, true, 5);
|
||||
AddonVersion v1(2017, 4, 7, suffix);
|
||||
AddonVersion v1Copy(v1);
|
||||
AddonVersion v2 = v1;
|
||||
AddonVersion v3(std::move(v1Copy));
|
||||
AddonVersion v4 = std::move(v2);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(v1, AddonVersion("2017.4.7b2.dev5"));
|
||||
CPPUNIT_ASSERT_EQUAL(v1, AddonVersion(std::make_tuple(2017, 4, 7, suffix)));
|
||||
CPPUNIT_ASSERT_EQUAL(v1, v3);
|
||||
CPPUNIT_ASSERT_EQUAL(v1, v4);
|
||||
CPPUNIT_ASSERT(v1 < AddonVersion("2017.4.7b2"));
|
||||
CPPUNIT_ASSERT(v1 <= AddonVersion("2017.4.7b2"));
|
||||
CPPUNIT_ASSERT(v1 <= v1);
|
||||
CPPUNIT_ASSERT(AddonVersion("2017.4.7b2") > v1);
|
||||
CPPUNIT_ASSERT(AddonVersion("2017.4.7b2") >= v1);
|
||||
CPPUNIT_ASSERT(v1 >= v1);
|
||||
CPPUNIT_ASSERT(v1 != AddonVersion("2017.4.7b3"));
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(v1.majorNumber(), 2017);
|
||||
CPPUNIT_ASSERT_EQUAL(v1.minorNumber(), 4);
|
||||
CPPUNIT_ASSERT_EQUAL(v1.patchLevel(), 7);
|
||||
CPPUNIT_ASSERT_EQUAL(v1.suffix(), suffix);
|
||||
|
||||
// Round-trips std::string <-> AddonVersion
|
||||
CPPUNIT_ASSERT(AddonVersion("2017.4.7.dev13").str() == "2017.4.7.dev13");
|
||||
CPPUNIT_ASSERT(AddonVersion("2017.4.7a2.dev8").str() == "2017.4.7a2.dev8");
|
||||
CPPUNIT_ASSERT(AddonVersion("2017.4.7a2").str() == "2017.4.7a2");
|
||||
CPPUNIT_ASSERT(AddonVersion("2017.4.7b2.dev5").str() == "2017.4.7b2.dev5");
|
||||
CPPUNIT_ASSERT(AddonVersion("2017.4.7b2").str() == "2017.4.7b2");
|
||||
CPPUNIT_ASSERT(AddonVersion("2017.4.7rc1.dev3").str() == "2017.4.7rc1.dev3");
|
||||
CPPUNIT_ASSERT(AddonVersion("2017.4.7rc1").str() == "2017.4.7rc1");
|
||||
CPPUNIT_ASSERT(AddonVersion("2017.4.7").str() == "2017.4.7");
|
||||
|
||||
// Check stream output
|
||||
std::ostringstream oss;
|
||||
oss << AddonVersion("2017.4.7b2.dev5");
|
||||
CPPUNIT_ASSERT(oss.str() == "2017.4.7b2.dev5");
|
||||
|
||||
// Check ordering with all types of transitions, using operator<()
|
||||
auto checkStrictOrdering = [](const vector<AddonVersion>& v) {
|
||||
assert(v.size() > 1);
|
||||
for (std::size_t i=0; i < v.size() - 1; i++) {
|
||||
CPPUNIT_ASSERT(v[i] < v[i+1]);
|
||||
}
|
||||
};
|
||||
|
||||
checkStrictOrdering({
|
||||
"3.12.8.dev1", "3.12.8.dev2", "3.12.8.dev12", "3.12.8a1.dev1",
|
||||
"3.12.8a1.dev2", "3.12.8a1", "3.12.8a2", "3.12.8b1.dev1",
|
||||
"3.12.8b1.dev2", "3.12.8b1", "3.12.8b2", "3.12.8b10",
|
||||
"3.12.8rc1.dev1", "3.12.8rc1.dev2", "3.12.8rc1.dev3",
|
||||
"3.12.8rc1", "3.12.8rc2", "3.12.8rc3", "3.12.8", "3.12.9.dev1",
|
||||
"3.12.9", "3.13.0", "4.0.0.dev1", "4.0.0.dev10", "4.0.0a1", "4.0.0",
|
||||
"2017.4.0", "2017.4.1", "2017.4.10", "2017.5.0", "2018.0.0"});
|
||||
|
||||
FGTestApi::tearDown::shutdownTestGlobals();
|
||||
}
|
||||
|
||||
void AddonManagementTests::testAddon()
|
||||
{
|
||||
FGTestApi::setUp::initTestGlobals("Addon");
|
||||
|
||||
std::string addonId = "org.FlightGear.addons.MyGreatAddon";
|
||||
Addon addon{addonId};
|
||||
addon.setVersion(AddonVersion("2017.2.5rc3"));
|
||||
addon.setBasePath(SGPath("/path/to/MyGreatAddon"));
|
||||
addon.setMinFGVersionRequired("2017.4.1");
|
||||
addon.setMaxFGVersionRequired("none");
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(addon.getId(), addonId);
|
||||
CPPUNIT_ASSERT_EQUAL(*addon.getVersion(), AddonVersion("2017.2.5rc3"));
|
||||
CPPUNIT_ASSERT_EQUAL(addon.getBasePath(), SGPath("/path/to/MyGreatAddon"));
|
||||
CPPUNIT_ASSERT(addon.getMinFGVersionRequired() == "2017.4.1");
|
||||
|
||||
const string refText = "addon '" + addonId + "' (version = 2017.2.5rc3, "
|
||||
"base path = '/path/to/MyGreatAddon', "
|
||||
"minFGVersionRequired = '2017.4.1', "
|
||||
"maxFGVersionRequired = 'none')";
|
||||
CPPUNIT_ASSERT_EQUAL(addon.str(), refText);
|
||||
|
||||
// Check stream output
|
||||
std::ostringstream oss;
|
||||
oss << addon;
|
||||
CPPUNIT_ASSERT_EQUAL(oss.str(), refText);
|
||||
|
||||
// Set a max FG version and recheck
|
||||
addon.setMaxFGVersionRequired("2018.2.5");
|
||||
const string refText2 = "addon '" + addonId + "' (version = 2017.2.5rc3, "
|
||||
"base path = '/path/to/MyGreatAddon', "
|
||||
"minFGVersionRequired = '2017.4.1', "
|
||||
"maxFGVersionRequired = '2018.2.5')";
|
||||
CPPUNIT_ASSERT(addon.getMaxFGVersionRequired() == "2018.2.5");
|
||||
CPPUNIT_ASSERT_EQUAL(addon.str(), refText2);
|
||||
|
||||
FGTestApi::tearDown::shutdownTestGlobals();
|
||||
}
|
||||
52
test_suite/unit_tests/Add-ons/test_AddonManagement.hxx
Normal file
52
test_suite/unit_tests/Add-ons/test_AddonManagement.hxx
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Edward d'Auvergne
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _FG_ADDON_MANAGEMENT_UNIT_TESTS_HXX
|
||||
#define _FG_ADDON_MANAGEMENT_UNIT_TESTS_HXX
|
||||
|
||||
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
#include <cppunit/TestFixture.h>
|
||||
|
||||
|
||||
// The unit tests of the Add-on system.
|
||||
class AddonManagementTests : public CppUnit::TestFixture
|
||||
{
|
||||
// Set up the test suite.
|
||||
CPPUNIT_TEST_SUITE(AddonManagementTests);
|
||||
CPPUNIT_TEST(testAddon);
|
||||
CPPUNIT_TEST(testAddonVersion);
|
||||
CPPUNIT_TEST(testAddonVersionSuffix);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
// Set up function for each test.
|
||||
void setUp() {}
|
||||
|
||||
// Clean up after each test.
|
||||
void tearDown() {}
|
||||
|
||||
// The tests.
|
||||
void testAddon();
|
||||
void testAddonVersion();
|
||||
void testAddonVersionSuffix();
|
||||
};
|
||||
|
||||
#endif // _FG_ADDON_MANAGEMENT_UNIT_TESTS_HXX
|
||||
15
test_suite/unit_tests/Airports/CMakeLists.txt
Normal file
15
test_suite/unit_tests/Airports/CMakeLists.txt
Normal file
@@ -0,0 +1,15 @@
|
||||
|
||||
set(TESTSUITE_SOURCES
|
||||
${TESTSUITE_SOURCES}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/TestSuite.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_airport.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_runway.cxx
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
set(TESTSUITE_HEADERS
|
||||
${TESTSUITE_HEADERS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_airport.hxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_runway.cxx
|
||||
PARENT_SCOPE
|
||||
)
|
||||
24
test_suite/unit_tests/Airports/TestSuite.cxx
Normal file
24
test_suite/unit_tests/Airports/TestSuite.cxx
Normal 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_airport.hxx"
|
||||
#include "test_runway.hxx"
|
||||
|
||||
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(AirportTests, "Unit tests");
|
||||
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(RunwayTests, "Unit tests");
|
||||
84
test_suite/unit_tests/Airports/test_airport.cxx
Normal file
84
test_suite/unit_tests/Airports/test_airport.cxx
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Keith Paterson
|
||||
*
|
||||
* 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 "config.h"
|
||||
|
||||
#include "test_airport.hxx"
|
||||
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
|
||||
#include "test_suite/FGTestApi/NavDataCache.hxx"
|
||||
#include "test_suite/FGTestApi/TestDataLogger.hxx"
|
||||
#include "test_suite/FGTestApi/testGlobals.hxx"
|
||||
|
||||
#include <simgear/math/sg_geodesy.hxx>
|
||||
#include <simgear/math/SGGeod.hxx>
|
||||
#include <AIModel/AIAircraft.hxx>
|
||||
#include <AIModel/AIFlightPlan.hxx>
|
||||
#include <AIModel/AIManager.hxx>
|
||||
#include <AIModel/performancedb.hxx>
|
||||
|
||||
#include <Airports/airport.hxx>
|
||||
#include <Airports/airportdynamicsmanager.hxx>
|
||||
#include <Airports/runways.hxx>
|
||||
#include <Traffic/TrafficMgr.hxx>
|
||||
#include <Time/TimeManager.hxx>
|
||||
|
||||
#include <ATC/atc_mgr.hxx>
|
||||
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <Main/globals.hxx>
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Set up function for each test.
|
||||
void AirportTests::setUp()
|
||||
{
|
||||
FGTestApi::setUp::initTestGlobals("Airports");
|
||||
FGTestApi::setUp::initNavDataCache();
|
||||
}
|
||||
|
||||
// Clean up after each test.
|
||||
void AirportTests::tearDown()
|
||||
{
|
||||
FGTestApi::tearDown::shutdownTestGlobals();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read an airport from the apt.dat
|
||||
*
|
||||
*/
|
||||
void AirportTests::testAirport()
|
||||
{
|
||||
FGAirportRef departureAirport = FGAirport::getByIdent("YSSY");
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE("Must have correct id", (std::string)"YSSY", departureAirport->getId());
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE("Must have runways", (unsigned int) 6, departureAirport->numRunways());
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE("Must have runway 16R", true, departureAirport->hasRunwayWithIdent("16R"));
|
||||
|
||||
int length = 3962;
|
||||
|
||||
FGRunwayRef runway = departureAirport->getRunwayByIdent("16R");
|
||||
int calculated = SGGeodesy::distanceM(runway->begin(), runway->end());
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Distance between the runway endpoints should be runway length", length, calculated, 1);
|
||||
calculated = SGGeodesy::distanceM(runway->begin(), runway->pointOnCenterline(-length));
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Distance between the runway start and point on centerline should be runway length", length, calculated, 1);
|
||||
|
||||
}
|
||||
49
test_suite/unit_tests/Airports/test_airport.hxx
Normal file
49
test_suite/unit_tests/Airports/test_airport.hxx
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Keith Paterson
|
||||
*
|
||||
* 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 AirportTests : public CppUnit::TestFixture
|
||||
{
|
||||
// Set up the test suite.
|
||||
CPPUNIT_TEST_SUITE(AirportTests);
|
||||
CPPUNIT_TEST(testAirport);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
|
||||
public:
|
||||
// Set up function for each test.
|
||||
void setUp();
|
||||
|
||||
// Clean up after each test.
|
||||
void tearDown();
|
||||
|
||||
// The tests.
|
||||
void testAirport();
|
||||
};
|
||||
80
test_suite/unit_tests/Airports/test_runway.cxx
Normal file
80
test_suite/unit_tests/Airports/test_runway.cxx
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Keith Paterson
|
||||
*
|
||||
* 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 "config.h"
|
||||
|
||||
#include "test_runway.hxx"
|
||||
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
|
||||
#include <simgear/math/sg_geodesy.hxx>
|
||||
#include <simgear/math/SGGeod.hxx>
|
||||
#include <AIModel/AIAircraft.hxx>
|
||||
#include <AIModel/AIFlightPlan.hxx>
|
||||
#include <AIModel/AIManager.hxx>
|
||||
#include <AIModel/performancedb.hxx>
|
||||
#include <Airports/airport.hxx>
|
||||
#include <Airports/airportdynamicsmanager.hxx>
|
||||
#include <Airports/runways.hxx>
|
||||
#include <Traffic/TrafficMgr.hxx>
|
||||
#include <Time/TimeManager.hxx>
|
||||
|
||||
#include <ATC/atc_mgr.hxx>
|
||||
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <Main/globals.hxx>
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Set up function for each test.
|
||||
void RunwayTests::setUp()
|
||||
{
|
||||
}
|
||||
|
||||
// Clean up after each test.
|
||||
void RunwayTests::tearDown()
|
||||
{
|
||||
}
|
||||
|
||||
void RunwayTests::testRunway()
|
||||
{
|
||||
PositionedID aAirport = 0;
|
||||
SGGeod aGeod = SGGeod::fromDeg(-33.92935800, 151.17160300);
|
||||
double heading = 155;
|
||||
int length = 3962;
|
||||
double width = 45.0;
|
||||
double displ_thresh = 79;
|
||||
double stopway = 0;
|
||||
FGRunway runway = FGRunway( FGPositioned::RUNWAY,
|
||||
aAirport,
|
||||
"16R",
|
||||
aGeod,
|
||||
heading,
|
||||
length,
|
||||
width,
|
||||
displ_thresh,
|
||||
stopway,
|
||||
1);
|
||||
int calculated = SGGeodesy::distanceM(runway.begin(), runway.end());
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Distance between the runway endpoints should be runway length", length, calculated, 1);
|
||||
calculated = SGGeodesy::distanceM(runway.begin(), runway.pointOnCenterline(-length));
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Distance between the runway start and point on centerline should be runway length", length, calculated, 1);
|
||||
}
|
||||
51
test_suite/unit_tests/Airports/test_runway.hxx
Normal file
51
test_suite/unit_tests/Airports/test_runway.hxx
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Keith Paterson
|
||||
*
|
||||
* 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;
|
||||
|
||||
class FGAIAircraft;
|
||||
|
||||
// The flight plan unit tests.
|
||||
class RunwayTests : public CppUnit::TestFixture
|
||||
{
|
||||
// Set up the test suite.
|
||||
CPPUNIT_TEST_SUITE(RunwayTests);
|
||||
CPPUNIT_TEST(testRunway);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
|
||||
public:
|
||||
// Set up function for each test.
|
||||
void setUp();
|
||||
|
||||
// Clean up after each test.
|
||||
void tearDown();
|
||||
|
||||
// The tests.
|
||||
void testRunway();
|
||||
};
|
||||
18
test_suite/unit_tests/Autopilot/CMakeLists.txt
Normal file
18
test_suite/unit_tests/Autopilot/CMakeLists.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
set(TESTSUITE_SOURCES
|
||||
${TESTSUITE_SOURCES}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/TestSuite.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/testDigitalFilter.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/testPidController.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/testPidControllerData.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/testInputValue.cxx
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
set(TESTSUITE_HEADERS
|
||||
${TESTSUITE_HEADERS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/testDigitalFilter.hxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/testPidController.hxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/testPidControllerData.hxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/testInputValue.hxx
|
||||
PARENT_SCOPE
|
||||
)
|
||||
28
test_suite/unit_tests/Autopilot/TestSuite.cxx
Normal file
28
test_suite/unit_tests/Autopilot/TestSuite.cxx
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Edward d'Auvergne
|
||||
*
|
||||
* 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 "testDigitalFilter.hxx"
|
||||
#include "testPidController.hxx"
|
||||
#include "testInputValue.hxx"
|
||||
|
||||
// Set up the unit tests.
|
||||
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(DigitalFilterTests, "Unit tests");
|
||||
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(PidControllerTests, "Unit tests");
|
||||
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(InputValueTests, "Unit tests");
|
||||
|
||||
81
test_suite/unit_tests/Autopilot/testDigitalFilter.cxx
Normal file
81
test_suite/unit_tests/Autopilot/testDigitalFilter.cxx
Normal file
@@ -0,0 +1,81 @@
|
||||
#include "testDigitalFilter.hxx"
|
||||
|
||||
#include <strstream>
|
||||
|
||||
#include "test_suite/FGTestApi/testGlobals.hxx"
|
||||
|
||||
|
||||
#include <Autopilot/autopilot.hxx>
|
||||
#include <Autopilot/digitalfilter.hxx>
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <Main/globals.hxx>
|
||||
|
||||
|
||||
#include <simgear/math/sg_random.hxx>
|
||||
#include <simgear/props/props_io.hxx>
|
||||
|
||||
// Set up function for each test.
|
||||
void DigitalFilterTests::setUp()
|
||||
{
|
||||
FGTestApi::setUp::initTestGlobals("ap-digitialfilter");
|
||||
}
|
||||
|
||||
|
||||
// Clean up after each test.
|
||||
void DigitalFilterTests::tearDown()
|
||||
{
|
||||
FGTestApi::tearDown::shutdownTestGlobals();
|
||||
}
|
||||
|
||||
|
||||
SGPropertyNode_ptr DigitalFilterTests::configFromString(const std::string& s)
|
||||
{
|
||||
SGPropertyNode_ptr config = new SGPropertyNode;
|
||||
|
||||
std::istringstream iss(s);
|
||||
readProperties(iss, config);
|
||||
return config;
|
||||
}
|
||||
|
||||
void DigitalFilterTests::testNoise()
|
||||
{
|
||||
sg_srandom(999);
|
||||
|
||||
auto config = configFromString(R"(<?xml version="1.0" encoding="UTF-8"?>
|
||||
<PropertyList>
|
||||
<filter>
|
||||
<input>/test/a</input>
|
||||
<output>/test/b</output>
|
||||
<type>coherent-noise</type>
|
||||
<amplitude>3.0</amplitude>
|
||||
<absolute type="bool">true</absolute>
|
||||
<discrete-resolution>1024</discrete-resolution>
|
||||
</filter>
|
||||
</PropertyList>
|
||||
)");
|
||||
|
||||
auto ap = new FGXMLAutopilot::Autopilot(globals->get_props(), config);
|
||||
|
||||
globals->add_subsystem("ap", ap, SGSubsystemMgr::FDM);
|
||||
ap->bind();
|
||||
ap->init();
|
||||
|
||||
//
|
||||
// for (double x=0.0; x < 5.0; x+=0.01) {
|
||||
// fgSetDouble("/test/a", x);
|
||||
// ap->update(0.1);
|
||||
// std::cerr << fgGetDouble("/test/b") << std::endl;
|
||||
// }
|
||||
|
||||
fgSetDouble("/test/a", 0.5);
|
||||
ap->update(0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.029, fgGetDouble("/test/b"), 0.001);
|
||||
|
||||
fgSetDouble("/test/a", 0.8);
|
||||
ap->update(0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.808, fgGetDouble("/test/b"), 0.001);
|
||||
|
||||
fgSetDouble("/test/a", 0.3);
|
||||
ap->update(0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.478, fgGetDouble("/test/b"), 0.001);
|
||||
}
|
||||
50
test_suite/unit_tests/Autopilot/testDigitalFilter.hxx
Normal file
50
test_suite/unit_tests/Autopilot/testDigitalFilter.hxx
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Edward d'Auvergne
|
||||
*
|
||||
* 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 <simgear/props/props.hxx>
|
||||
|
||||
|
||||
// The system tests.
|
||||
class DigitalFilterTests : public CppUnit::TestFixture
|
||||
{
|
||||
// Set up the test suite.
|
||||
CPPUNIT_TEST_SUITE(DigitalFilterTests);
|
||||
CPPUNIT_TEST(testNoise);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
SGPropertyNode_ptr configFromString(const std::string& s);
|
||||
|
||||
public:
|
||||
// Set up function for each test.
|
||||
void setUp();
|
||||
|
||||
// Clean up after each test.
|
||||
void tearDown();
|
||||
|
||||
|
||||
// The tests.
|
||||
void testNoise();
|
||||
};
|
||||
115
test_suite/unit_tests/Autopilot/testInputValue.cxx
Normal file
115
test_suite/unit_tests/Autopilot/testInputValue.cxx
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
SPDX-Copyright: James Turner
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "testInputValue.hxx"
|
||||
|
||||
#include <strstream>
|
||||
|
||||
#include "test_suite/FGTestApi/testGlobals.hxx"
|
||||
|
||||
|
||||
#include <Autopilot/autopilot.hxx>
|
||||
#include <Autopilot/inputvalue.hxx>
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <Main/globals.hxx>
|
||||
|
||||
|
||||
#include <simgear/math/sg_random.hxx>
|
||||
#include <simgear/props/props_io.hxx>
|
||||
|
||||
using namespace FGXMLAutopilot;
|
||||
|
||||
// Set up function for each test.
|
||||
void InputValueTests::setUp()
|
||||
{
|
||||
FGTestApi::setUp::initTestGlobals("ap-inputvalue");
|
||||
}
|
||||
|
||||
|
||||
// Clean up after each test.
|
||||
void InputValueTests::tearDown()
|
||||
{
|
||||
FGTestApi::tearDown::shutdownTestGlobals();
|
||||
}
|
||||
|
||||
|
||||
SGPropertyNode_ptr InputValueTests::configFromString(const std::string& s)
|
||||
{
|
||||
SGPropertyNode_ptr config = new SGPropertyNode;
|
||||
|
||||
std::istringstream iss(s);
|
||||
readProperties(iss, config);
|
||||
return config;
|
||||
}
|
||||
|
||||
void InputValueTests::testPropertyPath()
|
||||
{
|
||||
|
||||
sg_srandom(999);
|
||||
|
||||
auto config = configFromString(R"(<?xml version="1.0" encoding="UTF-8"?>
|
||||
<PropertyList>
|
||||
<property-path>/test/altitude-ft-node-path</property-path>
|
||||
<value>1.23</value>
|
||||
</PropertyList>
|
||||
)");
|
||||
|
||||
fgSetString("/test/altitude-ft-node-path", "/test/a");
|
||||
fgSetDouble("/test/a", 0.5);
|
||||
|
||||
InputValue_ptr valueA = new InputValue(*globals->get_props(), *config);
|
||||
|
||||
CPPUNIT_ASSERT(valueA->is_enabled());
|
||||
// check value is not written back
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.5, valueA->get_value(), 0.001);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.5, fgGetDouble("/test/a"), 0.001);
|
||||
|
||||
fgSetDouble("/test/a", 2.34);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(2.34, valueA->get_value(), 0.001);
|
||||
|
||||
fgSetString("/test/altitude-ft-node-path", "blah");
|
||||
CPPUNIT_ASSERT(!valueA->is_enabled());
|
||||
// <value> is used
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.23, valueA->get_value(), 0.001);
|
||||
|
||||
|
||||
fgSetDouble("/foo/bpath", 99);
|
||||
fgSetString("/test/altitude-ft-node-path", "/foo/bpath");
|
||||
CPPUNIT_ASSERT(valueA->is_enabled());
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(99.0, valueA->get_value(), 0.001);
|
||||
|
||||
fgSetDouble("/foo/bpath", -45.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(-45.1, valueA->get_value(), 0.001);
|
||||
|
||||
// start with different config
|
||||
auto config2 = configFromString(R"(<?xml version="1.0" encoding="UTF-8"?>
|
||||
<PropertyList>
|
||||
<property-path>/test/indicated-knots-node-path</property-path>
|
||||
</PropertyList>
|
||||
)");
|
||||
|
||||
InputValue_ptr valueB = new InputValue(*globals->get_props(), *config2);
|
||||
CPPUNIT_ASSERT(!valueB->is_enabled());
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, valueB->get_value(), 0.001);
|
||||
|
||||
fgSetString("/test/indicated-knots-node-path", "/instruments/airspeed/output/knots");
|
||||
CPPUNIT_ASSERT(!valueB->is_enabled());
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, valueB->get_value(), 0.001);
|
||||
|
||||
// create the property, but this does not trigger the change listener, so
|
||||
// stays invalid
|
||||
fgSetDouble("/instruments/airspeed/output/knots", 415);
|
||||
CPPUNIT_ASSERT(!valueB->is_enabled());
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, valueB->get_value(), 0.001);
|
||||
|
||||
// set the path again (with some whitespace, which is trimmed)
|
||||
fgSetString("/test/indicated-knots-node-path", " /instruments/airspeed/output/knots ");
|
||||
CPPUNIT_ASSERT(valueB->is_enabled());
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(415.0, valueB->get_value(), 0.001);
|
||||
|
||||
fgSetString("/test/indicated-knots-node-path", "");
|
||||
CPPUNIT_ASSERT(!valueB->is_enabled());
|
||||
|
||||
}
|
||||
35
test_suite/unit_tests/Autopilot/testInputValue.hxx
Normal file
35
test_suite/unit_tests/Autopilot/testInputValue.hxx
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
SPDX-Copyright: James Turner
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cppunit/TestFixture.h>
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
|
||||
#include <simgear/props/props.hxx>
|
||||
|
||||
|
||||
// The system tests.
|
||||
class InputValueTests : public CppUnit::TestFixture
|
||||
{
|
||||
// Set up the test suite.
|
||||
CPPUNIT_TEST_SUITE(InputValueTests);
|
||||
CPPUNIT_TEST(testPropertyPath);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
SGPropertyNode_ptr configFromString(const std::string& s);
|
||||
|
||||
public:
|
||||
// Set up function for each test.
|
||||
void setUp();
|
||||
|
||||
// Clean up after each test.
|
||||
void tearDown();
|
||||
|
||||
|
||||
// The tests.
|
||||
void testPropertyPath();
|
||||
};
|
||||
127
test_suite/unit_tests/Autopilot/testPidController.cxx
Normal file
127
test_suite/unit_tests/Autopilot/testPidController.cxx
Normal file
@@ -0,0 +1,127 @@
|
||||
#include "testPidController.hxx"
|
||||
#include "testPidControllerData.hxx"
|
||||
|
||||
#include "test_suite/FGTestApi/testGlobals.hxx"
|
||||
|
||||
|
||||
#include <Autopilot/autopilot.hxx>
|
||||
#include <Autopilot/pidcontroller.hxx>
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <Main/globals.hxx>
|
||||
|
||||
|
||||
#include <simgear/math/sg_random.hxx>
|
||||
#include <simgear/props/props_io.hxx>
|
||||
|
||||
// Set up function for each test.
|
||||
void PidControllerTests::setUp()
|
||||
{
|
||||
FGTestApi::setUp::initTestGlobals("ap-pidcontroller");
|
||||
}
|
||||
|
||||
|
||||
// Clean up after each test.
|
||||
void PidControllerTests::tearDown()
|
||||
{
|
||||
FGTestApi::tearDown::shutdownTestGlobals();
|
||||
}
|
||||
|
||||
|
||||
SGPropertyNode_ptr PidControllerTests::configFromString(const std::string& s)
|
||||
{
|
||||
SGPropertyNode_ptr config = new SGPropertyNode;
|
||||
|
||||
std::istringstream iss(s);
|
||||
readProperties(iss, config);
|
||||
return config;
|
||||
}
|
||||
|
||||
void PidControllerTests::test0()
|
||||
{
|
||||
test(false /*startup_current*/);
|
||||
}
|
||||
|
||||
void PidControllerTests::test1()
|
||||
{
|
||||
test(true /*startup_current*/);
|
||||
}
|
||||
|
||||
void PidControllerTests::test(bool startup_current)
|
||||
{
|
||||
sg_srandom(999);
|
||||
|
||||
// Define vertical-hold pid-controller (based on Harrier-GR3).
|
||||
//
|
||||
std::string config_text0 =
|
||||
R"(<?xml version="1.0" encoding="UTF-8"?>
|
||||
<PropertyList>
|
||||
<pid-controller>
|
||||
<name>Vertical Speed Hold</name>
|
||||
<debug>false</debug>
|
||||
<startup-current></startup-current>
|
||||
<enable>true</enable>
|
||||
<input>
|
||||
<prop>/velocities/vertical-speed-fps</prop>
|
||||
</input>
|
||||
<reference>
|
||||
<prop>/autopilot/settings/vertical-speed-fpm</prop>
|
||||
<scale>0.01667</scale>
|
||||
</reference>
|
||||
<output>
|
||||
<prop>/controls/flight/elevator</prop>
|
||||
</output>
|
||||
<config>
|
||||
<Kp>-0.025</Kp> <!-- proportional gain -->
|
||||
<beta>1.0</beta> <!-- input value weighing factor -->
|
||||
<alpha>0.1</alpha> <!-- low pass filter weighing factor -->
|
||||
<gamma>0.0</gamma> <!-- input value weighing factor for -->
|
||||
<!-- unfiltered derivative error -->
|
||||
<Ti>5</Ti> <!-- integrator time -->
|
||||
<Td>0.01</Td> <!-- derivator time -->
|
||||
|
||||
<!-- Restrict elevator values to +/-0.35 at high speed, but allow full
|
||||
+/-1 range at low speed. -->
|
||||
<u_min>-1</u_min>
|
||||
<u_max>1</u_max>
|
||||
|
||||
</config>
|
||||
</pid-controller>
|
||||
</PropertyList>
|
||||
)";
|
||||
|
||||
// Set the <startup-current> element.
|
||||
std::string from = "<startup-current></startup-current>";
|
||||
std::string config_text = config_text0;
|
||||
config_text.replace(config_text.find(from), from.size(),
|
||||
(startup_current) ? "<startup-current>true</startup-current>" : "<startup-current>false</startup-current>"
|
||||
);
|
||||
assert(config_text != config_text0);
|
||||
std::cout << "config_text is:\n" << config_text << "\n";
|
||||
|
||||
SGPropertyNode_ptr config = configFromString(config_text);
|
||||
|
||||
auto ap = new FGXMLAutopilot::Autopilot(globals->get_props(), config);
|
||||
|
||||
globals->add_subsystem("ap", ap, SGSubsystemMgr::FDM);
|
||||
ap->bind();
|
||||
ap->init();
|
||||
|
||||
const std::vector<PidControllerOutput>& outputs = (startup_current) ? pidControllerOutputs1 : pidControllerOutputs0;
|
||||
assert(pidControllerInputs.size() == outputs.size());
|
||||
|
||||
fgSetDouble("/controls/flight/elevator", 0);
|
||||
|
||||
for (unsigned i=0; i<pidControllerInputs.size(); ++i) {
|
||||
const PidControllerInput& input = pidControllerInputs[i];
|
||||
const PidControllerOutput& output = outputs[i];
|
||||
fgSetDouble("/velocities/vertical-speed-fps", input.vspeed_fps);
|
||||
fgSetDouble("/autopilot/settings/vertical-speed-fpm", input.reference / 0.01667);
|
||||
ap->update(0.00833333 /*dt*/);
|
||||
double elevator = fgGetDouble("/controls/flight/elevator");
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(output.output, elevator, 0.0001);
|
||||
if (0) {
|
||||
// This generates C++ text for setting the <outputs> vector above.
|
||||
std::cout << "{ " << elevator << "},\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
51
test_suite/unit_tests/Autopilot/testPidController.hxx
Normal file
51
test_suite/unit_tests/Autopilot/testPidController.hxx
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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 <simgear/props/props.hxx>
|
||||
|
||||
|
||||
// The system tests.
|
||||
struct PidControllerTests : public CppUnit::TestFixture
|
||||
{
|
||||
// Set up function for each test.
|
||||
void setUp();
|
||||
|
||||
// Clean up after each test.
|
||||
void tearDown();
|
||||
|
||||
// The tests.
|
||||
void test0();
|
||||
void test1();
|
||||
|
||||
private:
|
||||
|
||||
// Set up the test suite.
|
||||
CPPUNIT_TEST_SUITE(PidControllerTests);
|
||||
CPPUNIT_TEST(test0);
|
||||
CPPUNIT_TEST(test1);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
SGPropertyNode_ptr configFromString(const std::string& s);
|
||||
void test(bool startup_zeros);
|
||||
};
|
||||
7444
test_suite/unit_tests/Autopilot/testPidControllerData.cxx
Normal file
7444
test_suite/unit_tests/Autopilot/testPidControllerData.cxx
Normal file
File diff suppressed because it is too large
Load Diff
30
test_suite/unit_tests/Autopilot/testPidControllerData.hxx
Normal file
30
test_suite/unit_tests/Autopilot/testPidControllerData.hxx
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
struct PidControllerInput
|
||||
{
|
||||
double vspeed_fps;
|
||||
double reference;
|
||||
};
|
||||
|
||||
struct PidControllerOutput
|
||||
{
|
||||
double output;
|
||||
};
|
||||
|
||||
// Define sequence of input values. These are the values actually used with
|
||||
// Harrier-GR3 when startup_current=true, but we use the same inputs for
|
||||
// startup_current=false also.
|
||||
//
|
||||
extern std::vector<PidControllerInput> pidControllerInputs;
|
||||
|
||||
// Expected output when startup_current is false (the default old behaviour).
|
||||
//
|
||||
// Note the large transient at the start which messes up the initial behaviour.
|
||||
//
|
||||
extern std::vector<PidControllerOutput> pidControllerOutputs0;
|
||||
|
||||
// Expected output when startup_current is true (the new improved behaviour).
|
||||
//
|
||||
extern std::vector<PidControllerOutput> pidControllerOutputs1;
|
||||
31
test_suite/unit_tests/CMakeLists.txt
Normal file
31
test_suite/unit_tests/CMakeLists.txt
Normal file
@@ -0,0 +1,31 @@
|
||||
# Add each unit test category.
|
||||
foreach( unit_test_category
|
||||
Add-ons
|
||||
general
|
||||
FDM
|
||||
Input
|
||||
Main
|
||||
Navaids
|
||||
Network
|
||||
Instrumentation
|
||||
Scripting
|
||||
AI
|
||||
Airports
|
||||
Autopilot
|
||||
)
|
||||
|
||||
add_subdirectory(${unit_test_category})
|
||||
|
||||
endforeach( unit_test_category )
|
||||
|
||||
|
||||
set(TESTSUITE_SOURCES
|
||||
${TESTSUITE_SOURCES}
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
|
||||
set(TESTSUITE_HEADERS
|
||||
${TESTSUITE_HEADERS}
|
||||
PARENT_SCOPE
|
||||
)
|
||||
18
test_suite/unit_tests/FDM/CMakeLists.txt
Normal file
18
test_suite/unit_tests/FDM/CMakeLists.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
set(TESTSUITE_SOURCES
|
||||
${TESTSUITE_SOURCES}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/TestSuite.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_ls_matrix.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/testAeroElement.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/testYASimAtmosphere.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/testYASimGear.cxx
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
set(TESTSUITE_HEADERS
|
||||
${TESTSUITE_HEADERS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_ls_matrix.hxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/testAeroElement.hxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/testYASimAtmosphere.hxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/testYASimGear.hxx
|
||||
PARENT_SCOPE
|
||||
)
|
||||
30
test_suite/unit_tests/FDM/TestSuite.cxx
Normal file
30
test_suite/unit_tests/FDM/TestSuite.cxx
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Edward d'Auvergne
|
||||
*
|
||||
* 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_ls_matrix.hxx"
|
||||
#include "testAeroElement.hxx"
|
||||
#include "testYASimAtmosphere.hxx"
|
||||
#include "testYASimGear.hxx"
|
||||
|
||||
|
||||
// Set up the unit tests.
|
||||
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(AeroElementTests, "Unit tests");
|
||||
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(LaRCSimMatrixTests, "Unit tests");
|
||||
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(YASimAtmosphereTests, "Unit tests");
|
||||
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(YASimGearTests, "Unit tests");
|
||||
134
test_suite/unit_tests/FDM/testAeroElement.cxx
Normal file
134
test_suite/unit_tests/FDM/testAeroElement.cxx
Normal file
@@ -0,0 +1,134 @@
|
||||
#include <simgear/constants.h>
|
||||
#include <simgear/structure/SGSharedPtr.hxx>
|
||||
#include <simgear/math/SGVec3.hxx>
|
||||
|
||||
#include "FDM/AIWake/AeroElement.hxx"
|
||||
|
||||
#include "testAeroElement.hxx"
|
||||
|
||||
|
||||
void AeroElementTests::testNormal()
|
||||
{
|
||||
AeroElement_ptr el = new AeroElement(SGVec3d(-1., -0.5, 0.),
|
||||
SGVec3d(0., -0.5, 0.),
|
||||
SGVec3d(0., 0.5, 0.),
|
||||
SGVec3d(-1., 0.5, 0.));
|
||||
SGVec3d n = el->getNormal();
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(n[0], 0.0, 1e-9);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(n[1], 0.0, 1e-9);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(n[2], -1.0, 1e-9);
|
||||
}
|
||||
|
||||
void AeroElementTests::testCollocationPoint()
|
||||
{
|
||||
AeroElement_ptr el = new AeroElement(SGVec3d(-1., -0.5, 0.),
|
||||
SGVec3d(0., -0.5, 0.),
|
||||
SGVec3d(0., 0.5, 0.),
|
||||
SGVec3d(-1., 0.5, 0.));
|
||||
SGVec3d cp = el->getCollocationPoint();
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(cp[0], -0.75, 1e-9);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(cp[1], 0.0, 1e-9);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(cp[2], 0.0, 1e-9);
|
||||
}
|
||||
|
||||
void AeroElementTests::testBoundVortexMidPoint()
|
||||
{
|
||||
AeroElement_ptr el = new AeroElement(SGVec3d(-1., -0.5, 0.),
|
||||
SGVec3d(0., -0.5, 0.),
|
||||
SGVec3d(0., 0.5, 0.),
|
||||
SGVec3d(-1., 0.5, 0.));
|
||||
SGVec3d mp = el->getBoundVortexMidPoint();
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(mp[0], -0.25, 1e-9);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(mp[1], 0.0, 1e-9);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(mp[2], 0.0, 1e-9);
|
||||
}
|
||||
|
||||
void AeroElementTests::testBoundVortex()
|
||||
{
|
||||
AeroElement_ptr el = new AeroElement(SGVec3d(-1., -0.5, 0.),
|
||||
SGVec3d(0., -0.5, 0.),
|
||||
SGVec3d(0., 0.5, 0.),
|
||||
SGVec3d(-1., 0.5, 0.));
|
||||
SGVec3d v = el->getBoundVortex();
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(v[0], 0.0, 1e-9);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(v[1], 1.0, 1e-9);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(v[2], 0.0, 1e-9);
|
||||
}
|
||||
|
||||
void AeroElementTests::testInducedVelocityOnBoundVortex()
|
||||
{
|
||||
AeroElement_ptr el = new AeroElement(SGVec3d(-1., -0.5, 0.),
|
||||
SGVec3d(0., -0.5, 0.),
|
||||
SGVec3d(0., 0.5, 0.),
|
||||
SGVec3d(-1., 0.5, 0.));
|
||||
SGVec3d mp = el->getBoundVortexMidPoint();
|
||||
SGVec3d v = el->getInducedVelocity(mp);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(v[0], 0.0, 1e-9);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(v[1], 0.0, 1e-9);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(v[2], 1.0/M_PI, 1e-9);
|
||||
}
|
||||
|
||||
void AeroElementTests::testInducedVelocityOnCollocationPoint()
|
||||
{
|
||||
AeroElement_ptr el = new AeroElement(SGVec3d(-1., -0.5, 0.),
|
||||
SGVec3d(0., -0.5, 0.),
|
||||
SGVec3d(0., 0.5, 0.),
|
||||
SGVec3d(-1., 0.5, 0.));
|
||||
SGVec3d cp = el->getCollocationPoint();
|
||||
SGVec3d v = el->getInducedVelocity(cp);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(v[0], 0.0, 1e-9);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(v[1], 0.0, 1e-9);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(v[2], (1.0+sqrt(2.0)/M_PI), 1e-9);
|
||||
}
|
||||
|
||||
void AeroElementTests::testInducedVelocityAtFarField()
|
||||
{
|
||||
AeroElement_ptr el = new AeroElement(SGVec3d(-1., -0.5, 0.),
|
||||
SGVec3d(0., -0.5, 0.),
|
||||
SGVec3d(0., 0.5, 0.),
|
||||
SGVec3d(-1., 0.5, 0.));
|
||||
SGVec3d mp = el->getBoundVortexMidPoint();
|
||||
SGVec3d v = el->getInducedVelocity(mp+SGVec3d(-1000.,0.,0.));
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(v[0], 0.0, 1e-9);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(v[1], 0.0, 1e-9);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(v[2], 2.0/M_PI, 1e-7);
|
||||
}
|
||||
|
||||
void AeroElementTests::testInducedVelocityAbove()
|
||||
{
|
||||
AeroElement_ptr el = new AeroElement(SGVec3d(-1., -0.5, 0.),
|
||||
SGVec3d(0., -0.5, 0.),
|
||||
SGVec3d(0., 0.5, 0.),
|
||||
SGVec3d(-1., 0.5, 0.));
|
||||
SGVec3d mp = el->getBoundVortexMidPoint();
|
||||
SGVec3d v = el->getInducedVelocity(mp+SGVec3d(0.,0.,-0.5));
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(v[0], -1.0/(sqrt(2.0)*M_PI), 1e-9);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(v[1], 0.0, 1e-9);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(v[2], 0.5/M_PI, 1e-9);
|
||||
}
|
||||
|
||||
void AeroElementTests::testInducedVelocityAboveWithOffset()
|
||||
{
|
||||
AeroElement_ptr el = new AeroElement(SGVec3d(-1., -0.5, 0.),
|
||||
SGVec3d(0., -0.5, 0.),
|
||||
SGVec3d(0., 0.5, 0.),
|
||||
SGVec3d(-1., 0.5, 0.));
|
||||
SGVec3d mp = el->getBoundVortexMidPoint();
|
||||
SGVec3d v = el->getInducedVelocity(mp+SGVec3d(0.0, 0.5, -1.0));
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(v[0], -1.0/(4.0*M_PI*sqrt(2.0)), 1e-9);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(v[1], -0.125/M_PI, 1e-9);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(v[2], 0.125/M_PI, 1e-9);
|
||||
}
|
||||
|
||||
void AeroElementTests::testInducedVelocityUpstream()
|
||||
{
|
||||
AeroElement_ptr el = new AeroElement(SGVec3d(-1., -0.5, 0.),
|
||||
SGVec3d(0., -0.5, 0.),
|
||||
SGVec3d(0., 0.5, 0.),
|
||||
SGVec3d(-1., 0.5, 0.));
|
||||
SGVec3d mp = el->getBoundVortexMidPoint();
|
||||
SGVec3d v = el->getInducedVelocity(mp+SGVec3d(0.5, 0.0, 0.0));
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(v[0], 0.0, 1e-9);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(v[1], 0.0, 1e-9);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(v[2], (1.0-sqrt(2.0))/M_PI, 1e-9);
|
||||
}
|
||||
66
test_suite/unit_tests/FDM/testAeroElement.hxx
Normal file
66
test_suite/unit_tests/FDM/testAeroElement.hxx
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Edward d'Auvergne
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _FG_AERO_ELEMENT_UNIT_TESTS_HXX
|
||||
#define _FG_AERO_ELEMENT_UNIT_TESTS_HXX
|
||||
|
||||
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
#include <cppunit/TestFixture.h>
|
||||
|
||||
|
||||
// The unit tests.
|
||||
class AeroElementTests : public CppUnit::TestFixture
|
||||
{
|
||||
// Set up the test suite.
|
||||
CPPUNIT_TEST_SUITE(AeroElementTests);
|
||||
CPPUNIT_TEST(testBoundVortex);
|
||||
CPPUNIT_TEST(testBoundVortexMidPoint);
|
||||
CPPUNIT_TEST(testCollocationPoint);
|
||||
CPPUNIT_TEST(testInducedVelocityAbove);
|
||||
CPPUNIT_TEST(testInducedVelocityAboveWithOffset);
|
||||
CPPUNIT_TEST(testInducedVelocityAtFarField);
|
||||
CPPUNIT_TEST(testInducedVelocityOnBoundVortex);
|
||||
//CPPUNIT_TEST(testInducedVelocityOnCollocationPoint); // Not run in the original ctest.
|
||||
CPPUNIT_TEST(testInducedVelocityUpstream);
|
||||
CPPUNIT_TEST(testNormal);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
// Set up function for each test.
|
||||
void setUp() {}
|
||||
|
||||
// Clean up after each test.
|
||||
void tearDown() {}
|
||||
|
||||
// The tests.
|
||||
void testBoundVortex();
|
||||
void testBoundVortexMidPoint();
|
||||
void testCollocationPoint();
|
||||
void testInducedVelocityAbove();
|
||||
void testInducedVelocityAboveWithOffset();
|
||||
void testInducedVelocityAtFarField();
|
||||
void testInducedVelocityOnBoundVortex();
|
||||
void testInducedVelocityOnCollocationPoint();
|
||||
void testInducedVelocityUpstream();
|
||||
void testNormal();
|
||||
};
|
||||
|
||||
#endif // _FG_AERO_ELEMENT_UNIT_TESTS_HXX
|
||||
44
test_suite/unit_tests/FDM/testYASimAtmosphere.cxx
Normal file
44
test_suite/unit_tests/FDM/testYASimAtmosphere.cxx
Normal file
@@ -0,0 +1,44 @@
|
||||
#include "test_suite/FGTestApi/PrivateAccessorFDM.hxx"
|
||||
|
||||
#include "testYASimAtmosphere.hxx"
|
||||
|
||||
#include <FDM/YASim/Math.hpp>
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
|
||||
|
||||
using namespace yasim;
|
||||
|
||||
void YASimAtmosphereTests::setUp()
|
||||
{
|
||||
a.reset(new Atmosphere());
|
||||
}
|
||||
|
||||
|
||||
void YASimAtmosphereTests::tearDown()
|
||||
{
|
||||
a.reset();
|
||||
}
|
||||
|
||||
|
||||
void YASimAtmosphereTests::testAtmosphere()
|
||||
{
|
||||
auto accessor = FGTestApi::PrivateAccessor::FDM::Accessor();
|
||||
|
||||
int numColumns = accessor.read_FDM_YASim_Atmosphere_numColumns(a);
|
||||
int maxTableIndex = a->maxTableIndex();
|
||||
int rows = maxTableIndex + 1;
|
||||
const float maxDeviation = 0.0002f;
|
||||
|
||||
SG_LOG(SG_GENERAL, SG_INFO, "Columns = " << numColumns);
|
||||
SG_LOG(SG_GENERAL, SG_INFO, "Rows = " << rows);
|
||||
|
||||
for (int alt = 0; alt <= maxTableIndex; alt++) {
|
||||
float density = a->calcStdDensity(accessor.read_FDM_YASim_Atmosphere_data(a, alt, a->PRESSURE), accessor.read_FDM_YASim_Atmosphere_data(a, alt, a->TEMPERATURE));
|
||||
float delta = accessor.read_FDM_YASim_Atmosphere_data(a, alt, a->DENSITY) - density;
|
||||
SG_LOG(SG_GENERAL, SG_INFO, "alt: " << alt << ", delta: " << delta);
|
||||
if (Math::abs(delta) > maxDeviation)
|
||||
CPPUNIT_FAIL("Deviation above limit of 0.0002");
|
||||
}
|
||||
SG_LOG(SG_GENERAL, SG_INFO, "Deviation below " << maxDeviation << " for all rows.");
|
||||
}
|
||||
52
test_suite/unit_tests/FDM/testYASimAtmosphere.hxx
Normal file
52
test_suite/unit_tests/FDM/testYASimAtmosphere.hxx
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Edward d'Auvergne
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _FG_YASIM_ATMOSPHERE_UNIT_TESTS_HXX
|
||||
#define _FG_YASIM_ATMOSPHERE_UNIT_TESTS_HXX
|
||||
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
#include <cppunit/TestFixture.h>
|
||||
|
||||
#include <src/FDM/YASim/Atmosphere.hpp>
|
||||
|
||||
|
||||
// The unit tests.
|
||||
class YASimAtmosphereTests : public CppUnit::TestFixture
|
||||
{
|
||||
// Set up the test suite.
|
||||
CPPUNIT_TEST_SUITE(YASimAtmosphereTests);
|
||||
CPPUNIT_TEST(testAtmosphere);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
// Set up function for each test.
|
||||
void setUp();
|
||||
|
||||
// Clean up after each test.
|
||||
void tearDown();
|
||||
|
||||
// The tests.
|
||||
void testAtmosphere();
|
||||
|
||||
// Data.
|
||||
std::unique_ptr<yasim::Atmosphere> a;
|
||||
};
|
||||
|
||||
#endif // _FG_YASIM_ATMOSPHERE_UNIT_TESTS_HXX
|
||||
128
test_suite/unit_tests/FDM/testYASimGear.cxx
Normal file
128
test_suite/unit_tests/FDM/testYASimGear.cxx
Normal file
@@ -0,0 +1,128 @@
|
||||
#include "testYASimGear.hxx"
|
||||
|
||||
#include "test_suite/FGTestApi/testGlobals.hxx"
|
||||
|
||||
#include "FDM/YASim/Gear.hpp"
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
|
||||
void YASimGearTests::setUp()
|
||||
{
|
||||
FGTestApi::setUp::initTestGlobals("");
|
||||
}
|
||||
|
||||
void YASimGearTests::tearDown()
|
||||
{
|
||||
FGTestApi::tearDown::shutdownTestGlobals();
|
||||
}
|
||||
|
||||
|
||||
void YASimGearTests::test()
|
||||
{
|
||||
/* Check we get expected values for a particular set of inputs. */
|
||||
|
||||
float ground[4] = { -0.22097, 0.00507429, -0.975263, 2.35943};
|
||||
float wheel_pos[3] = { -0.953044, 2.20823, -2.00883};
|
||||
yasim::GearVector wheel_axle( -0, 0.16383, -0.114715);
|
||||
float wheel_radius = 0.261257;
|
||||
float tyre_radius = 0.130629;
|
||||
yasim::GearVector compression( -0.139389, -0, 0.480178);
|
||||
|
||||
float contact[3];
|
||||
float compress_distance_vertical;
|
||||
float compress_norm;
|
||||
|
||||
bool on_ground = yasim::gearCompression(
|
||||
ground,
|
||||
compression,
|
||||
wheel_pos,
|
||||
wheel_axle,
|
||||
wheel_radius,
|
||||
tyre_radius,
|
||||
[] () { return 0; },
|
||||
|
||||
/* output params: */
|
||||
contact,
|
||||
compress_distance_vertical,
|
||||
compress_norm
|
||||
);
|
||||
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "on_ground=" << on_ground);
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "contact=(" << contact[0] << ", " << contact[1] << ", " << contact[1] << ")");
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "compress_distance_vertical=" << compress_distance_vertical);
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "compress_norm=" << compress_norm);
|
||||
|
||||
double e = 0.0001;
|
||||
CPPUNIT_ASSERT( on_ground);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL( -1.1053, contact[0], e);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL( 2.0645, contact[1], e);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL( -2.1581, contact[2], e);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL( 0.167955, compress_distance_vertical, e);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL( 0.383887, compress_norm, e);
|
||||
|
||||
|
||||
/* Now check we get same results as old point-contact algorithm, when using
|
||||
wheel_radius=0 and tyre_radius=0. */
|
||||
|
||||
/* For this test we set gear contact point to bottom of what was the wheel,
|
||||
so that it will be approximately in same position as earlier contact point,
|
||||
and so slightly underground. */
|
||||
wheel_pos[2] -= wheel_radius + tyre_radius;
|
||||
|
||||
float bump_altitude_override = 0.1;
|
||||
|
||||
on_ground = yasim::gearCompression(
|
||||
ground,
|
||||
compression,
|
||||
wheel_pos /* contact point. */,
|
||||
wheel_axle /* values don't matter. */,
|
||||
0 /*wheel_radius*/,
|
||||
0 /*tyre_radius*/,
|
||||
[bump_altitude_override] () { return bump_altitude_override; },
|
||||
|
||||
/* output params: */
|
||||
contact,
|
||||
compress_distance_vertical,
|
||||
compress_norm
|
||||
);
|
||||
|
||||
float contact_old[3];
|
||||
float compress_distance_vertical_old;
|
||||
float compress_norm_old;
|
||||
|
||||
bool on_ground_old = yasim::gearCompressionOld(
|
||||
ground,
|
||||
compression,
|
||||
wheel_pos /* contact point. */,
|
||||
[bump_altitude_override] () { return bump_altitude_override; },
|
||||
|
||||
/* output params: */
|
||||
contact_old,
|
||||
compress_distance_vertical_old,
|
||||
compress_norm_old
|
||||
);
|
||||
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "comparing with old algorithm.");
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "old:");
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, " on_ground_old=" << on_ground_old);
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, " contact=(" << contact_old[0] << ", " << contact_old[1] << ", " << contact_old[1] << ")");
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, " compress_distance_vertical=" << compress_distance_vertical_old);
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, " compress_norm=" << compress_norm_old);
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "new:");
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, " on_ground=" << on_ground);
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, " contact=(" << contact[0] << ", " << contact[1] << ", " << contact[1] << ")");
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, " compress_distance_vertical=" << compress_distance_vertical);
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, " compress_norm=" << compress_norm);
|
||||
|
||||
CPPUNIT_ASSERT( on_ground);
|
||||
CPPUNIT_ASSERT_EQUAL( on_ground_old, on_ground);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL( contact_old[0], contact[0], e);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL( contact_old[1], contact[1], e);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL( contact_old[2], contact[2], e);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL( compress_distance_vertical_old, compress_distance_vertical, e);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL( compress_norm_old, compress_norm, e);
|
||||
}
|
||||
|
||||
15
test_suite/unit_tests/FDM/testYASimGear.hxx
Normal file
15
test_suite/unit_tests/FDM/testYASimGear.hxx
Normal file
@@ -0,0 +1,15 @@
|
||||
#include <cppunit/TestFixture.h>
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
|
||||
struct YASimGearTests : CppUnit::TestFixture
|
||||
{
|
||||
void setUp();
|
||||
|
||||
void tearDown();
|
||||
|
||||
void test();
|
||||
|
||||
CPPUNIT_TEST_SUITE(YASimGearTests);
|
||||
CPPUNIT_TEST(test);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
};
|
||||
155
test_suite/unit_tests/FDM/test_ls_matrix.cxx
Normal file
155
test_suite/unit_tests/FDM/test_ls_matrix.cxx
Normal file
@@ -0,0 +1,155 @@
|
||||
#include "test_ls_matrix.hxx"
|
||||
|
||||
#include <simgear/constants.h>
|
||||
#include <simgear/misc/test_macros.hxx>
|
||||
extern "C" {
|
||||
#include "src/FDM/LaRCsim/ls_matrix.h"
|
||||
}
|
||||
|
||||
|
||||
void LaRCSimMatrixTests::testCopyMatrix()
|
||||
{
|
||||
int nelm = 20;
|
||||
double **src = nr_matrix(1, nelm, 1, nelm);
|
||||
double **dest = nr_matrix(1, nelm, 1, nelm);
|
||||
double invmaxlong = 1.0/(double)RAND_MAX;
|
||||
|
||||
for (int i=1; i<=nelm; ++i)
|
||||
for (int j=1; j<=nelm; ++j)
|
||||
src[i][j] = 2.0 - 4.0*invmaxlong*(double) rand();
|
||||
|
||||
nr_copymat(src, nelm, dest);
|
||||
|
||||
for (int i=1; i<=nelm; ++i)
|
||||
for (int j=1; j<=nelm; ++j)
|
||||
CPPUNIT_ASSERT_EQUAL(src[i][j], dest[i][j]);
|
||||
|
||||
nr_free_matrix(src, 1, nelm, 1, nelm);
|
||||
nr_free_matrix(dest, 1, nelm, 1, nelm);
|
||||
}
|
||||
|
||||
void LaRCSimMatrixTests::testIdentityMatrix()
|
||||
{
|
||||
int nelm = 10;
|
||||
double **id = nr_matrix(1, nelm, 1, nelm);
|
||||
|
||||
for (int i=1; i<=nelm; ++i)
|
||||
for (int j=1; j<=nelm; ++j)
|
||||
id[i][j] = i == j ? 1.0 : 0.0;
|
||||
|
||||
nr_gaussj(id, nelm, 0, 0);
|
||||
|
||||
for (int i=1; i<=nelm; ++i)
|
||||
for (int j=1; j<=nelm; ++j)
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(id[i][j], i == j ? 1.0 : 0.0, 1e-9);
|
||||
|
||||
nr_free_matrix(id, 1, nelm, 1, nelm);
|
||||
}
|
||||
|
||||
void LaRCSimMatrixTests::testOrthogonalMatrix()
|
||||
{
|
||||
int nelm = 3;
|
||||
double **m = nr_matrix(1, nelm, 1, nelm);
|
||||
double **inv = nr_matrix(1, nelm, 1, nelm);
|
||||
double angle = M_PI/3.0;
|
||||
|
||||
m[1][1] = cos(angle);
|
||||
m[1][2] = sin(angle);
|
||||
m[1][3] = 0.0;
|
||||
m[2][1] = -m[1][2];
|
||||
m[2][2] = m[1][1];
|
||||
m[2][3] = 0.0;
|
||||
m[3][1] = 0.0;
|
||||
m[3][2] = 0.0;
|
||||
m[3][3] = 1.0;
|
||||
|
||||
nr_copymat(m, nelm, inv);
|
||||
nr_gaussj(inv, nelm, 0, 0);
|
||||
|
||||
for (int i=1; i<=nelm; ++i)
|
||||
for (int j=1; j<=nelm; ++j)
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(m[i][j], inv[j][i], 1e-9);
|
||||
|
||||
nr_free_matrix(m, 1, nelm, 1, nelm);
|
||||
nr_free_matrix(inv, 1, nelm, 1, nelm);
|
||||
}
|
||||
|
||||
void LaRCSimMatrixTests::testRandomMatrix()
|
||||
{
|
||||
int nelm = 20;
|
||||
double **src = nr_matrix(1, nelm, 1, nelm);
|
||||
double **inv = nr_matrix(1, nelm, 1, nelm);
|
||||
double **id = nr_matrix(1, nelm, 1, nelm);
|
||||
double invmaxlong = 1.0/(double)RAND_MAX;
|
||||
|
||||
for (int i=1; i<=nelm; ++i)
|
||||
for (int j=1; j<=nelm; ++j)
|
||||
src[i][j] = 2.0 - 4.0*invmaxlong*(double) rand();
|
||||
|
||||
nr_copymat(src, nelm, inv);
|
||||
nr_gaussj(inv, nelm, 0, 0);
|
||||
|
||||
for (int i=1; i<=nelm; ++i)
|
||||
for (int j=1; j<=nelm; ++j) {
|
||||
id[i][j] = 0.0;
|
||||
for (int k=1; k<=nelm; ++k)
|
||||
id[i][j] += src[i][k]*inv[k][j];
|
||||
}
|
||||
|
||||
for (int i=1; i<=nelm; ++i)
|
||||
for (int j=1; j<=nelm; ++j)
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(id[i][j], i == j ? 1.0 : 0.0, 1e-9);
|
||||
|
||||
nr_free_matrix(src, 1, nelm, 1, nelm);
|
||||
nr_free_matrix(inv, 1, nelm, 1, nelm);
|
||||
nr_free_matrix(id, 1, nelm, 1, nelm);
|
||||
}
|
||||
|
||||
void LaRCSimMatrixTests::testSolveLinearSystem()
|
||||
{
|
||||
int nelm = 20;
|
||||
double **src = nr_matrix(1, nelm, 1, nelm);
|
||||
double **inv = nr_matrix(1, nelm, 1, nelm);
|
||||
double **rhs = nr_matrix(1, nelm, 1, 1);
|
||||
double **sol = nr_matrix(1, nelm, 1, 1);
|
||||
double **check = nr_matrix(1, nelm, 1, nelm);
|
||||
double invmaxlong = 1.0/(double)RAND_MAX;
|
||||
|
||||
for (int i=1; i<=nelm; ++i)
|
||||
for (int j=1; j<=nelm; ++j)
|
||||
src[i][j] = 2.0 - 4.0*invmaxlong*(double) rand();
|
||||
|
||||
for (int i=1; i<=nelm; ++i) {
|
||||
rhs[i][1] = 2.0+cos(i*M_PI/nelm);
|
||||
sol[i][1] = rhs[i][1];
|
||||
}
|
||||
|
||||
nr_copymat(src, nelm, inv);
|
||||
nr_gaussj(inv, nelm, sol, 1);
|
||||
|
||||
for (int i=1; i<=nelm; ++i)
|
||||
for (int j=1; j<=nelm; ++j) {
|
||||
check[i][j] = 0.0;
|
||||
for (int k=1; k<=nelm; ++k)
|
||||
check[i][j] += src[i][k]*inv[k][j];
|
||||
}
|
||||
|
||||
for (int i=1; i<=nelm; ++i)
|
||||
for (int j=1; j<=nelm; ++j)
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(check[i][j], i == j ? 1.0 : 0.0, 1e-9);
|
||||
|
||||
for (int i=1; i<=nelm; ++i) {
|
||||
check[i][1] = 0.0;
|
||||
for (int j=1; j<=nelm; ++j)
|
||||
check[i][1] += src[i][j]*sol[j][1];
|
||||
}
|
||||
|
||||
for (int i=1; i<=nelm; ++i)
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(check[i][1], rhs[i][1], 1e-9);
|
||||
|
||||
nr_free_matrix(src, 1, nelm, 1, nelm);
|
||||
nr_free_matrix(inv, 1, nelm, 1, nelm);
|
||||
nr_free_matrix(check, 1, nelm, 1, nelm);
|
||||
nr_free_matrix(rhs, 1, nelm, 1, 1);
|
||||
nr_free_matrix(sol, 1, nelm, 1, 1);
|
||||
}
|
||||
56
test_suite/unit_tests/FDM/test_ls_matrix.hxx
Normal file
56
test_suite/unit_tests/FDM/test_ls_matrix.hxx
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Edward d'Auvergne
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _FG_LARCSIM_MATRIX_UNIT_TESTS_HXX
|
||||
#define _FG_LARCSIM_MATRIX_UNIT_TESTS_HXX
|
||||
|
||||
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
#include <cppunit/TestFixture.h>
|
||||
|
||||
|
||||
// The LaRCSim matrix unit tests.
|
||||
class LaRCSimMatrixTests : public CppUnit::TestFixture
|
||||
{
|
||||
// Set up the test suite.
|
||||
CPPUNIT_TEST_SUITE(LaRCSimMatrixTests);
|
||||
CPPUNIT_TEST(testCopyMatrix);
|
||||
CPPUNIT_TEST(testIdentityMatrix);
|
||||
CPPUNIT_TEST(testOrthogonalMatrix);
|
||||
CPPUNIT_TEST(testRandomMatrix);
|
||||
CPPUNIT_TEST(testSolveLinearSystem);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
// Set up function for each test.
|
||||
void setUp() {}
|
||||
|
||||
// Clean up after each test.
|
||||
void tearDown() {}
|
||||
|
||||
// The tests.
|
||||
void testCopyMatrix();
|
||||
void testIdentityMatrix();
|
||||
void testOrthogonalMatrix();
|
||||
void testRandomMatrix();
|
||||
void testSolveLinearSystem();
|
||||
};
|
||||
|
||||
#endif // _FG_LARCSIM_MATRIX_UNIT_TESTS_HXX
|
||||
17
test_suite/unit_tests/Input/CMakeLists.txt
Normal file
17
test_suite/unit_tests/Input/CMakeLists.txt
Normal file
@@ -0,0 +1,17 @@
|
||||
if(ENABLE_HID_INPUT)
|
||||
set(HID_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/test_hidinput.cxx)
|
||||
set(HID_HEADER ${CMAKE_CURRENT_SOURCE_DIR}/test_hidinput.hxx)
|
||||
endif()
|
||||
|
||||
set(TESTSUITE_SOURCES
|
||||
${TESTSUITE_SOURCES}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/TestSuite.cxx
|
||||
${HID_SOURCE}
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
set(TESTSUITE_HEADERS
|
||||
${TESTSUITE_HEADERS}
|
||||
${HID_HEADER}
|
||||
PARENT_SCOPE
|
||||
)
|
||||
28
test_suite/unit_tests/Input/TestSuite.cxx
Normal file
28
test_suite/unit_tests/Input/TestSuite.cxx
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Edward d'Auvergne
|
||||
*
|
||||
* 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 <config.h>
|
||||
|
||||
#include "test_hidinput.hxx"
|
||||
|
||||
|
||||
// Set up the unit tests.
|
||||
#ifdef ENABLE_HID_INPUT
|
||||
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(HIDInputTests, "Unit tests");
|
||||
#endif
|
||||
73
test_suite/unit_tests/Input/test_hidinput.cxx
Normal file
73
test_suite/unit_tests/Input/test_hidinput.cxx
Normal file
@@ -0,0 +1,73 @@
|
||||
// Written by James Turner, started 2017.
|
||||
//
|
||||
// Copyright (C) 2017 James Turner
|
||||
//
|
||||
// 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, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "test_hidinput.hxx"
|
||||
|
||||
#include "test_suite/FGTestApi/testGlobals.hxx"
|
||||
|
||||
#include <simgear/misc/test_macros.hxx>
|
||||
|
||||
#include <Input/FGHIDEventInput.hxx>
|
||||
|
||||
void HIDInputTests::testValueExtract()
|
||||
{
|
||||
uint8_t testDataFromSpec[4] = {0, 0xf4, 0x1 | (0x7 << 2), 0x03};
|
||||
CPPUNIT_ASSERT(extractBits(testDataFromSpec, 4, 8, 10) == 500);
|
||||
CPPUNIT_ASSERT(extractBits(testDataFromSpec, 4, 18, 10) == 199);
|
||||
|
||||
uint8_t testData2[4] = {0x01 << 6 | 0x0f,
|
||||
0x17 | (1 << 6),
|
||||
0x3 | (0x11 << 2),
|
||||
0x3d | (1 << 6) };
|
||||
|
||||
CPPUNIT_ASSERT(extractBits(testData2, 4, 0, 6) == 15);
|
||||
CPPUNIT_ASSERT(extractBits(testData2, 4, 6, 12) == 3421);
|
||||
CPPUNIT_ASSERT(extractBits(testData2, 4, 18, 12) == 3921);
|
||||
CPPUNIT_ASSERT(extractBits(testData2, 4, 30, 1) == 1);
|
||||
CPPUNIT_ASSERT(extractBits(testData2, 4, 31, 1) == 0);
|
||||
}
|
||||
|
||||
// void writeBits(uint8_t* bytes, size_t bitOffset, size_t bitSize, int value)
|
||||
|
||||
void HIDInputTests::testValueInsert()
|
||||
{
|
||||
uint8_t buf[8];
|
||||
memset(buf, 0, 8);
|
||||
|
||||
int a = 3421;
|
||||
int b = 3921;
|
||||
writeBits(buf, 6, 12, a);
|
||||
writeBits(buf, 18, 12, b);
|
||||
|
||||
CPPUNIT_ASSERT(buf[0] == 0x40);
|
||||
CPPUNIT_ASSERT(buf[1] == 0x57);
|
||||
CPPUNIT_ASSERT(buf[2] == (0x03 | 0x44));
|
||||
CPPUNIT_ASSERT(buf[3] == 0x3d);
|
||||
}
|
||||
|
||||
void HIDInputTests::testSignExtension()
|
||||
{
|
||||
CPPUNIT_ASSERT(signExtend(0x80, 8) == -128);
|
||||
CPPUNIT_ASSERT(signExtend(0xff, 8) == -1);
|
||||
CPPUNIT_ASSERT(signExtend(0x7f, 8) == 127);
|
||||
|
||||
CPPUNIT_ASSERT(signExtend(0x831, 12) == -1999);
|
||||
CPPUNIT_ASSERT(signExtend(0x7dd, 12) == 2013);
|
||||
}
|
||||
52
test_suite/unit_tests/Input/test_hidinput.hxx
Normal file
52
test_suite/unit_tests/Input/test_hidinput.hxx
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Edward d'Auvergne
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _FG_HIDINPUT_UNIT_TESTS_HXX
|
||||
#define _FG_HIDINPUT_UNIT_TESTS_HXX
|
||||
|
||||
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
#include <cppunit/TestFixture.h>
|
||||
|
||||
|
||||
// The unit tests.
|
||||
class HIDInputTests : public CppUnit::TestFixture
|
||||
{
|
||||
// Set up the test suite.
|
||||
CPPUNIT_TEST_SUITE(HIDInputTests);
|
||||
CPPUNIT_TEST(testValueExtract);
|
||||
CPPUNIT_TEST(testValueInsert);
|
||||
CPPUNIT_TEST(testSignExtension);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
// Set up function for each test.
|
||||
void setUp() {}
|
||||
|
||||
// Clean up after each test.
|
||||
void tearDown() {}
|
||||
|
||||
// The tests.
|
||||
void testValueExtract();
|
||||
void testValueInsert();
|
||||
void testSignExtension();
|
||||
};
|
||||
|
||||
#endif // _FG_HIDINPUT_UNIT_TESTS_HXX
|
||||
24
test_suite/unit_tests/Instrumentation/CMakeLists.txt
Normal file
24
test_suite/unit_tests/Instrumentation/CMakeLists.txt
Normal file
@@ -0,0 +1,24 @@
|
||||
set(TESTSUITE_SOURCES
|
||||
${TESTSUITE_SOURCES}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/TestSuite.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_navRadio.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_gps.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_hold_controller.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_rnav_procedures.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_dme.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_commRadio.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_transponder.cxx
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
set(TESTSUITE_HEADERS
|
||||
${TESTSUITE_HEADERS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_navRadio.hxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_gps.hxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_hold_controller.hxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_rnav_procedures.hxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_dme.hxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_commRadio.hxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_transponder.hxx
|
||||
PARENT_SCOPE
|
||||
)
|
||||
35
test_suite/unit_tests/Instrumentation/TestSuite.cxx
Normal file
35
test_suite/unit_tests/Instrumentation/TestSuite.cxx
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Edward d'Auvergne
|
||||
*
|
||||
* 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_commRadio.hxx"
|
||||
#include "test_dme.hxx"
|
||||
#include "test_gps.hxx"
|
||||
#include "test_hold_controller.hxx"
|
||||
#include "test_navRadio.hxx"
|
||||
#include "test_rnav_procedures.hxx"
|
||||
#include "test_transponder.hxx"
|
||||
|
||||
// Set up the unit tests.
|
||||
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(NavRadioTests, "Unit tests");
|
||||
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(GPSTests, "Unit tests");
|
||||
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(HoldControllerTests, "Unit tests");
|
||||
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(RNAVProcedureTests, "Unit tests");
|
||||
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(DMEReceiverTests, "Unit tests");
|
||||
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(CommRadioTests, "Unit tests");
|
||||
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(TransponderTests, "Unit tests");
|
||||
207
test_suite/unit_tests/Instrumentation/test_commRadio.cxx
Normal file
207
test_suite/unit_tests/Instrumentation/test_commRadio.cxx
Normal file
@@ -0,0 +1,207 @@
|
||||
#include "test_commRadio.hxx"
|
||||
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
|
||||
#include "test_suite/FGTestApi/NavDataCache.hxx"
|
||||
#include "test_suite/FGTestApi/testGlobals.hxx"
|
||||
|
||||
#include <Airports/airport.hxx>
|
||||
#include <Navaids/NavDataCache.hxx>
|
||||
|
||||
#include <Instrumentation/commradio.hxx>
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <Main/locale.hxx>
|
||||
|
||||
// Set up function for each test.
|
||||
void CommRadioTests::setUp()
|
||||
{
|
||||
FGTestApi::setUp::initTestGlobals("commradio");
|
||||
FGTestApi::setUp::initNavDataCache();
|
||||
|
||||
// otherwise ATCSPeech will call locale functions and assert
|
||||
globals->get_locale()->selectLanguage({});
|
||||
}
|
||||
|
||||
|
||||
// Clean up after each test.
|
||||
void CommRadioTests::tearDown()
|
||||
{
|
||||
FGTestApi::tearDown::shutdownTestGlobals();
|
||||
}
|
||||
|
||||
|
||||
// std::string NavRadioTests::formatFrequency(double f)
|
||||
// {
|
||||
// char buf[16];
|
||||
// ::snprintf(buf, 16, "%3.2f", f);
|
||||
// return buf;
|
||||
// }
|
||||
|
||||
SGSubsystemRef CommRadioTests::setupStandardRadio(const std::string& name, int index, bool enable833)
|
||||
{
|
||||
SGPropertyNode_ptr configNode(new SGPropertyNode);
|
||||
configNode->setStringValue("name", name);
|
||||
configNode->setIntValue("number", index);
|
||||
configNode->setBoolValue("eight-point-three", enable833);
|
||||
auto r = Instrumentation::CommRadio::createInstance(configNode);
|
||||
|
||||
fgSetBool("/sim/atis/enabled", false);
|
||||
|
||||
r->bind();
|
||||
r->init();
|
||||
|
||||
globals->add_subsystem("comm-radio", r, SGSubsystemMgr::GENERAL);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void CommRadioTests::testBasic()
|
||||
{
|
||||
auto r = setupStandardRadio("commtest", 2, false);
|
||||
|
||||
FGAirportRef apt = FGAirport::getByIdent("EDDM");
|
||||
FGTestApi::setPositionAndStabilise(apt->geod());
|
||||
|
||||
SGPropertyNode_ptr n = globals->get_props()->getNode("instrumentation/commtest[2]");
|
||||
// EDDM ATIS
|
||||
n->setDoubleValue("frequencies/selected-mhz", 123.125);
|
||||
r->update(1.0);
|
||||
|
||||
// CPPUNIT_ASSERT_DOUBLES_EQUAL(25, n->getDoubleValue("frequencies/selected-channel-width-khz"), 1e-3);
|
||||
CPPUNIT_ASSERT_EQUAL("123.12"s, string{n->getStringValue("frequencies/selected-mhz-fmt")});
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL("EDDM"s, string{n->getStringValue("airport-id")});
|
||||
CPPUNIT_ASSERT_EQUAL("ATIS"s, string{n->getStringValue("station-name")});
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, n->getDoubleValue("slant-distance-m"), 1e-6);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, n->getDoubleValue("signal-quality-norm"), 1e-6);
|
||||
|
||||
|
||||
n->setDoubleValue("frequencies/selected-mhz", 121.72);
|
||||
r->update(1.0);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL("121.72"s, string{n->getStringValue("frequencies/selected-mhz-fmt")});
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL("EDDM"s, string{n->getStringValue("airport-id")});
|
||||
CPPUNIT_ASSERT_EQUAL("CLNC DEL"s, string{n->getStringValue("station-name")});
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, n->getDoubleValue("slant-distance-m"), 1e-6);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, n->getDoubleValue("signal-quality-norm"), 1e-6);
|
||||
}
|
||||
|
||||
void CommRadioTests::testEightPointThree()
|
||||
{
|
||||
auto r = setupStandardRadio("commtest", 2, true);
|
||||
|
||||
|
||||
FGAirportRef apt = FGAirport::getByIdent("EGKK");
|
||||
FGTestApi::setPositionAndStabilise(apt->geod());
|
||||
|
||||
SGPropertyNode_ptr n = globals->get_props()->getNode("instrumentation/commtest[2]");
|
||||
|
||||
// EGKK ATIS
|
||||
n->setDoubleValue("frequencies/selected-mhz", 136.525);
|
||||
|
||||
r->update(1.0);
|
||||
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(25, n->getDoubleValue("frequencies/selected-channel-width-khz"), 1e-3);
|
||||
CPPUNIT_ASSERT_EQUAL("136.525"s, string{n->getStringValue("frequencies/selected-mhz-fmt")});
|
||||
|
||||
// random 8.3Khz station
|
||||
|
||||
n->setDoubleValue("frequencies/selected-mhz", 120.11);
|
||||
r->update(1.0);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(8.33, n->getDoubleValue("frequencies/selected-channel-width-khz"), 1e-3);
|
||||
CPPUNIT_ASSERT_EQUAL("120.110"s, string{n->getStringValue("frequencies/selected-mhz-fmt")});
|
||||
CPPUNIT_ASSERT_EQUAL(338, n->getIntValue("frequencies/selected-channel"));
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(120.10833, n->getDoubleValue("frequencies/selected-real-frequency-mhz"), 1e-6);
|
||||
|
||||
// select station by channel, on 8.3khz boundary
|
||||
n->setIntValue("frequencies/selected-channel", 2561);
|
||||
r->update(1.0);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(8.33, n->getDoubleValue("frequencies/selected-channel-width-khz"), 1e-3);
|
||||
CPPUNIT_ASSERT_EQUAL("134.005"s, string{n->getStringValue("frequencies/selected-mhz-fmt")});
|
||||
CPPUNIT_ASSERT_EQUAL(2561, n->getIntValue("frequencies/selected-channel"));
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(134.000, n->getDoubleValue("frequencies/selected-real-frequency-mhz"), 1e-6);
|
||||
|
||||
// select station by channel, on 25Khz boundary
|
||||
n->setIntValue("frequencies/selected-channel", 2560);
|
||||
r->update(1.0);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(25, n->getDoubleValue("frequencies/selected-channel-width-khz"), 1e-3);
|
||||
CPPUNIT_ASSERT_EQUAL("134.000"s, string{n->getStringValue("frequencies/selected-mhz-fmt")});
|
||||
CPPUNIT_ASSERT_EQUAL(2560, n->getIntValue("frequencies/selected-channel"));
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(134.000, n->getDoubleValue("frequencies/selected-real-frequency-mhz"), 1e-6);
|
||||
|
||||
// select by frequency
|
||||
n->setDoubleValue("frequencies/selected-mhz", 120.035);
|
||||
r->update(1.0);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(8.33, n->getDoubleValue("frequencies/selected-channel-width-khz"), 1e-3);
|
||||
CPPUNIT_ASSERT_EQUAL("120.035"s, string{n->getStringValue("frequencies/selected-mhz-fmt")});
|
||||
CPPUNIT_ASSERT_EQUAL(326, n->getIntValue("frequencies/selected-channel"));
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(120.03333, n->getDoubleValue("frequencies/selected-real-frequency-mhz"), 1e-6);
|
||||
|
||||
// under-run the permitted frequency range
|
||||
n->setDoubleValue("frequencies/selected-mhz", 117.99);
|
||||
r->update(1.0);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(25.0, n->getDoubleValue("frequencies/selected-channel-width-khz"), 1e-3);
|
||||
CPPUNIT_ASSERT_EQUAL(0, n->getIntValue("frequencies/selected-channel"));
|
||||
|
||||
n->setDoubleValue("frequencies/selected-mhz", 118.705);
|
||||
r->update(1.0);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(8.33, n->getDoubleValue("frequencies/selected-channel-width-khz"), 1e-3);
|
||||
CPPUNIT_ASSERT_EQUAL("118.705"s, string{n->getStringValue("frequencies/selected-mhz-fmt")});
|
||||
CPPUNIT_ASSERT_EQUAL(113, n->getIntValue("frequencies/selected-channel"));
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(118.700, n->getDoubleValue("frequencies/selected-real-frequency-mhz"), 1e-6);
|
||||
|
||||
// over-run the frequency range
|
||||
n->setDoubleValue("frequencies/selected-mhz", 137.000);
|
||||
r->update(1.0);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(8.33, n->getDoubleValue("frequencies/selected-channel-width-khz"), 1e-3);
|
||||
CPPUNIT_ASSERT_EQUAL("136.990"s, string{n->getStringValue("frequencies/selected-mhz-fmt")});
|
||||
CPPUNIT_ASSERT_EQUAL(3039, n->getIntValue("frequencies/selected-channel"));
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(136.99166, n->getDoubleValue("frequencies/selected-real-frequency-mhz"), 1e-6);
|
||||
}
|
||||
|
||||
void CommRadioTests::testEPLLTuning833()
|
||||
{
|
||||
// this test is disabled until data entry for EPLL is fixed
|
||||
return;
|
||||
|
||||
auto r = setupStandardRadio("commtest", 2, true);
|
||||
|
||||
FGAirportRef apt = FGAirport::getByIdent("EPLL");
|
||||
FGTestApi::setPositionAndStabilise(apt->geod());
|
||||
|
||||
SGPropertyNode_ptr n = globals->get_props()->getNode("instrumentation/commtest[2]");
|
||||
// should be EPLL TWR
|
||||
n->setDoubleValue("frequencies/selected-mhz", 124.225);
|
||||
r->update(1.0);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL("EPLL"s, string{n->getStringValue("airport-id")});
|
||||
CPPUNIT_ASSERT_EQUAL("Lodz TOWER"s, string{n->getStringValue("station-name")});
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, n->getDoubleValue("slant-distance-m"), 1e-6);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, n->getDoubleValue("signal-quality-norm"), 1e-6);
|
||||
}
|
||||
|
||||
void CommRadioTests::testEPLLTuning25()
|
||||
{
|
||||
auto r = setupStandardRadio("commtest", 2, false);
|
||||
|
||||
FGAirportRef apt = FGAirport::getByIdent("EPLL");
|
||||
FGTestApi::setPositionAndStabilise(apt->geod());
|
||||
|
||||
SGPropertyNode_ptr n = globals->get_props()->getNode("instrumentation/commtest[2]");
|
||||
// should be EPLL TWR
|
||||
n->setDoubleValue("frequencies/selected-mhz", 124.23);
|
||||
r->update(1.0);
|
||||
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(124.23, n->getDoubleValue("frequencies/selected-mhz"), 1e-6);
|
||||
CPPUNIT_ASSERT_EQUAL("124.22"s, string{n->getStringValue("frequencies/selected-mhz-fmt")});
|
||||
|
||||
// fail for now
|
||||
#if 0
|
||||
CPPUNIT_ASSERT_EQUAL("EPLL"s, string{n->getStringValue("airport-id")});
|
||||
CPPUNIT_ASSERT_EQUAL("Lodz TOWER"s, string{n->getStringValue("station-name")});
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, n->getDoubleValue("slant-distance-m"), 1e-6);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, n->getDoubleValue("signal-quality-norm"), 1e-6);
|
||||
#endif
|
||||
}
|
||||
61
test_suite/unit_tests/Instrumentation/test_commRadio.hxx
Normal file
61
test_suite/unit_tests/Instrumentation/test_commRadio.hxx
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Edward d'Auvergne
|
||||
*
|
||||
* 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 <simgear/structure/subsystem_mgr.hxx>
|
||||
|
||||
class FGNavRadio;
|
||||
class SGGeod;
|
||||
|
||||
// The flight plan unit tests.
|
||||
class CommRadioTests : public CppUnit::TestFixture
|
||||
{
|
||||
// Set up the test suite.
|
||||
CPPUNIT_TEST_SUITE(CommRadioTests);
|
||||
|
||||
CPPUNIT_TEST(testBasic);
|
||||
CPPUNIT_TEST(testEightPointThree);
|
||||
CPPUNIT_TEST(testEPLLTuning833);
|
||||
CPPUNIT_TEST(testEPLLTuning25);
|
||||
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
SGSubsystemRef setupStandardRadio(const std::string& name, int index, bool enable833);
|
||||
|
||||
public:
|
||||
// Set up function for each test.
|
||||
void setUp();
|
||||
|
||||
// Clean up after each test.
|
||||
void tearDown();
|
||||
|
||||
// std::string formatFrequency(double f);
|
||||
|
||||
// The tests.
|
||||
void testBasic();
|
||||
void testEightPointThree();
|
||||
void testEPLLTuning833();
|
||||
void testEPLLTuning25();
|
||||
};
|
||||
120
test_suite/unit_tests/Instrumentation/test_dme.cxx
Normal file
120
test_suite/unit_tests/Instrumentation/test_dme.cxx
Normal file
@@ -0,0 +1,120 @@
|
||||
#include "test_dme.hxx"
|
||||
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
|
||||
#include "test_suite/FGTestApi/NavDataCache.hxx"
|
||||
#include "test_suite/FGTestApi/TestPilot.hxx"
|
||||
#include "test_suite/FGTestApi/testGlobals.hxx"
|
||||
|
||||
#include <Airports/airport.hxx>
|
||||
#include <Navaids/NavDataCache.hxx>
|
||||
#include <Navaids/navlist.hxx>
|
||||
#include <Navaids/navrecord.hxx>
|
||||
|
||||
#include <Instrumentation/dme.hxx>
|
||||
|
||||
#include <Main/fg_props.hxx>
|
||||
|
||||
// Set up function for each test.
|
||||
void DMEReceiverTests::setUp()
|
||||
{
|
||||
FGTestApi::setUp::initTestGlobals("navradio");
|
||||
FGTestApi::setUp::initNavDataCache();
|
||||
}
|
||||
|
||||
|
||||
// Clean up after each test.
|
||||
void DMEReceiverTests::tearDown()
|
||||
{
|
||||
FGTestApi::tearDown::shutdownTestGlobals();
|
||||
}
|
||||
|
||||
void DMEReceiverTests::setPositionAndStabilise(DME* r, const SGGeod& g)
|
||||
{
|
||||
FGTestApi::setPosition(g);
|
||||
for (int i = 0; i < 60; ++i) {
|
||||
r->update(0.1);
|
||||
}
|
||||
}
|
||||
|
||||
SGSharedPtr<DME> DMEReceiverTests::setupStandardDME()
|
||||
{
|
||||
SGPropertyNode_ptr configNode(new SGPropertyNode);
|
||||
configNode->setStringValue("name", "dmetest");
|
||||
configNode->setIntValue("number", 2);
|
||||
|
||||
return new DME(configNode);
|
||||
}
|
||||
|
||||
void DMEReceiverTests::testBasic()
|
||||
{
|
||||
SGSharedPtr<DME> r = setupStandardDME();
|
||||
|
||||
// set a source string pointing at a fictious nav-receiver
|
||||
fgSetString("/instrumentation/dmetest[2]/frequencies/source",
|
||||
"/instrumentation/nav[0]/frequencies/selected-mhz");
|
||||
|
||||
r->bind();
|
||||
r->init();
|
||||
globals->get_subsystem_mgr()->add("dme", r.get());
|
||||
|
||||
auto arlanda = fgFindAirportID("ESSA");
|
||||
|
||||
// set the nav frequency
|
||||
fgSetDouble("/instrumentation/nav[0]/frequencies/selected-mhz", 113.30);
|
||||
|
||||
|
||||
SGPropertyNode_ptr node = globals->get_props()->getNode("instrumentation/dmetest[2]");
|
||||
node->setBoolValue("serviceable", true);
|
||||
|
||||
fgSetDouble("systems/electrical/outputs/dme", 12.0);
|
||||
|
||||
setPositionAndStabilise(r.get(), arlanda->geod());
|
||||
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(true, node->getBoolValue("in-range"));
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(4.4, node->getDoubleValue("indicated-distance-nm"), 0.1);
|
||||
|
||||
// fly towards the station at constant speed
|
||||
|
||||
auto pilot = SGSharedPtr<FGTestApi::TestPilot>(new FGTestApi::TestPilot);
|
||||
pilot->setSpeedKts(150);
|
||||
|
||||
|
||||
FGPositioned::TypeFilter f{FGPositioned::DME};
|
||||
FGNavRecordRef arlandaDME = fgpositioned_cast<FGNavRecord>(
|
||||
FGPositioned::findClosestWithIdent("ANE", arlanda->geod(), &f));
|
||||
|
||||
const double trueCourseToANE = SGGeodesy::courseDeg(arlanda->geod(), arlandaDME->geod());
|
||||
pilot->setCourseTrue(trueCourseToANE);
|
||||
FGTestApi::runForTime(30.0);
|
||||
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(150, node->getDoubleValue("indicated-ground-speed-kt"), 0.5);
|
||||
// should have travelled (150 / 3600 * 30 ) = 1.25nm
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(3.15, node->getDoubleValue("indicated-distance-nm"), 0.1);
|
||||
}
|
||||
|
||||
void DMEReceiverTests::testRCFN_04DME()
|
||||
{
|
||||
// disabled pending discussion about the data for this one
|
||||
return;
|
||||
|
||||
auto rcfn = fgFindAirportID("RCFN");
|
||||
|
||||
auto dmeReceiver = setupStandardDME();
|
||||
|
||||
FGRunwayRef rwy04 = rcfn->getRunwayByIdent("04");
|
||||
|
||||
FGPositioned::TypeFilter filter(FGPositioned::DME);
|
||||
auto matches = FGPositioned::findAllWithIdent("IFNN", &filter);
|
||||
FGPositioned::sortByRange(matches, rcfn->geod());
|
||||
|
||||
CPPUNIT_ASSERT(!matches.empty());
|
||||
// should be size two, really
|
||||
|
||||
auto station = fgpositioned_cast<FGNavRecord>(matches.front());
|
||||
CPPUNIT_ASSERT(station);
|
||||
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(110.9, station->get_freq(), 0.01);
|
||||
}
|
||||
53
test_suite/unit_tests/Instrumentation/test_dme.hxx
Normal file
53
test_suite/unit_tests/Instrumentation/test_dme.hxx
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2021 James Turner <james@flightgear.org>
|
||||
*
|
||||
* 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 <simgear/structure/SGSharedPtr.hxx>
|
||||
|
||||
class DME;
|
||||
class SGGeod;
|
||||
|
||||
// The DME unit tests.
|
||||
class DMEReceiverTests : public CppUnit::TestFixture
|
||||
{
|
||||
// Set up the test suite.
|
||||
CPPUNIT_TEST_SUITE(DMEReceiverTests);
|
||||
CPPUNIT_TEST(testBasic);
|
||||
CPPUNIT_TEST(testRCFN_04DME);
|
||||
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
void setPositionAndStabilise(DME* r, const SGGeod& g);
|
||||
|
||||
SGSharedPtr<DME> setupStandardDME();
|
||||
|
||||
public:
|
||||
// Set up function for each test.
|
||||
void setUp();
|
||||
|
||||
// Clean up after each test.
|
||||
void tearDown();
|
||||
|
||||
// The tests.
|
||||
void testBasic();
|
||||
void testRCFN_04DME();
|
||||
};
|
||||
1847
test_suite/unit_tests/Instrumentation/test_gps.cxx
Normal file
1847
test_suite/unit_tests/Instrumentation/test_gps.cxx
Normal file
File diff suppressed because it is too large
Load Diff
103
test_suite/unit_tests/Instrumentation/test_gps.hxx
Normal file
103
test_suite/unit_tests/Instrumentation/test_gps.hxx
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _FG_GPS_UNIT_TESTS_HXX
|
||||
#define _FG_GPS_UNIT_TESTS_HXX
|
||||
|
||||
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
#include <cppunit/TestFixture.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <simgear/props/props.hxx>
|
||||
|
||||
class SGGeod;
|
||||
class GPS;
|
||||
|
||||
// The flight plan unit tests.
|
||||
class GPSTests : public CppUnit::TestFixture
|
||||
{
|
||||
// Set up the test suite.
|
||||
CPPUNIT_TEST_SUITE(GPSTests);
|
||||
CPPUNIT_TEST(testBasic);
|
||||
CPPUNIT_TEST(testNavRadioSlave);
|
||||
CPPUNIT_TEST(testTurnAnticipation);
|
||||
CPPUNIT_TEST(testOBSMode);
|
||||
CPPUNIT_TEST(testDirectTo);
|
||||
CPPUNIT_TEST(testLegMode);
|
||||
CPPUNIT_TEST(testDirectToLegOnFlightplan);
|
||||
CPPUNIT_TEST(testLongLeg);
|
||||
CPPUNIT_TEST(testLongLegWestbound);
|
||||
CPPUNIT_TEST(testOffsetFlight);
|
||||
CPPUNIT_TEST(testOverflightSequencing);
|
||||
CPPUNIT_TEST(testOffcourseSequencing);
|
||||
CPPUNIT_TEST(testLegIntercept);
|
||||
CPPUNIT_TEST(testDirectToLegOnFlightplanAndResumeBuiltin);
|
||||
CPPUNIT_TEST(testBuiltinRevertToOBSAtEnd);
|
||||
CPPUNIT_TEST(testRadialIntercept);
|
||||
CPPUNIT_TEST(testSWIFT8);
|
||||
CPPUNIT_TEST(testDMEIntercept);
|
||||
CPPUNIT_TEST(testFinalLegCourse);
|
||||
CPPUNIT_TEST(testCourseLegIntermediateWaypoint);
|
||||
CPPUNIT_TEST(testExceedFlyByMaxAngleTurn);
|
||||
CPPUNIT_TEST(testFlyOverMaxInterceptAngle);
|
||||
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
void setPositionAndStabilise(GPS* gps, const SGGeod& g);
|
||||
|
||||
GPS* setupStandardGPS(SGPropertyNode_ptr config = {},
|
||||
const std::string name = "gps", const int index = 0);
|
||||
void setupRouteManager();
|
||||
|
||||
public:
|
||||
// Set up function for each test.
|
||||
void setUp();
|
||||
|
||||
// Clean up after each test.
|
||||
void tearDown();
|
||||
|
||||
// The tests.
|
||||
void testBasic();
|
||||
void testNavRadioSlave();
|
||||
void testTurnAnticipation();
|
||||
void testOBSMode();
|
||||
void testDirectTo();
|
||||
void testLegMode();
|
||||
void testDirectToLegOnFlightplan();
|
||||
void testLongLeg();
|
||||
void testLongLegWestbound();
|
||||
void testOffsetFlight();
|
||||
void testOffcourseSequencing();
|
||||
void testOverflightSequencing();
|
||||
void testLegIntercept();
|
||||
void testDirectToLegOnFlightplanAndResumeBuiltin();
|
||||
void testBuiltinRevertToOBSAtEnd();
|
||||
void testRadialIntercept();
|
||||
void testSWIFT8();
|
||||
void testDMEIntercept();
|
||||
void testFinalLegCourse();
|
||||
void testCourseLegIntermediateWaypoint();
|
||||
void testExceedFlyByMaxAngleTurn();
|
||||
void testFlyOverMaxInterceptAngle();
|
||||
};
|
||||
|
||||
#endif // _FG_GPS_UNIT_TESTS_HXX
|
||||
1006
test_suite/unit_tests/Instrumentation/test_hold_controller.cxx
Normal file
1006
test_suite/unit_tests/Instrumentation/test_hold_controller.cxx
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _FG_HOLD_CTL_UNIT_TESTS_HXX
|
||||
#define _FG_HOLD_CTL_UNIT_TESTS_HXX
|
||||
|
||||
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
#include <cppunit/TestFixture.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <simgear/props/props.hxx>
|
||||
|
||||
class SGGeod;
|
||||
class GPS;
|
||||
|
||||
// The flight plan unit tests.
|
||||
class HoldControllerTests : public CppUnit::TestFixture
|
||||
{
|
||||
// Set up the test suite.
|
||||
CPPUNIT_TEST_SUITE(HoldControllerTests);
|
||||
CPPUNIT_TEST(testHoldEntryDirect);
|
||||
CPPUNIT_TEST(testHoldEntryTeardrop);
|
||||
CPPUNIT_TEST(testHoldEntryParallel);
|
||||
CPPUNIT_TEST(testLeftHoldEntryDirect);
|
||||
CPPUNIT_TEST(testLeftHoldEntryTeardrop);
|
||||
CPPUNIT_TEST(testLeftHoldEntryParallel);
|
||||
CPPUNIT_TEST(testHoldNotEntered);
|
||||
CPPUNIT_TEST(testHoldEntryOffCourse);
|
||||
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
void setPositionAndStabilise(const SGGeod& g);
|
||||
|
||||
GPS* setupStandardGPS(SGPropertyNode_ptr config = {},
|
||||
const std::string name = "gps", const int index = 0);
|
||||
void setupRouteManager();
|
||||
|
||||
public:
|
||||
// Set up function for each test.
|
||||
void setUp();
|
||||
|
||||
// Clean up after each test.
|
||||
void tearDown();
|
||||
|
||||
// The tests.
|
||||
void testHoldEntryDirect();
|
||||
void testHoldEntryTeardrop();
|
||||
void testHoldEntryParallel();
|
||||
void testLeftHoldEntryDirect();
|
||||
void testLeftHoldEntryTeardrop();
|
||||
void testLeftHoldEntryParallel();
|
||||
void testHoldNotEntered();
|
||||
void testHoldEntryOffCourse();
|
||||
private:
|
||||
GPS* m_gps = nullptr;
|
||||
SGPropertyNode_ptr m_gpsNode;
|
||||
};
|
||||
|
||||
#endif // _FG_HOLD_CTL_UNIT_TESTS_HXX
|
||||
840
test_suite/unit_tests/Instrumentation/test_navRadio.cxx
Normal file
840
test_suite/unit_tests/Instrumentation/test_navRadio.cxx
Normal file
@@ -0,0 +1,840 @@
|
||||
#include "test_navRadio.hxx"
|
||||
|
||||
#include <memory>
|
||||
#include <cstring>
|
||||
|
||||
#include "test_suite/FGTestApi/testGlobals.hxx"
|
||||
#include "test_suite/FGTestApi/NavDataCache.hxx"
|
||||
|
||||
#include <Navaids/NavDataCache.hxx>
|
||||
#include <Navaids/navrecord.hxx>
|
||||
#include <Navaids/navlist.hxx>
|
||||
|
||||
#include <Instrumentation/navradio.hxx>
|
||||
|
||||
// Set up function for each test.
|
||||
void NavRadioTests::setUp()
|
||||
{
|
||||
FGTestApi::setUp::initTestGlobals("navradio");
|
||||
FGTestApi::setUp::initNavDataCache();
|
||||
}
|
||||
|
||||
|
||||
// Clean up after each test.
|
||||
void NavRadioTests::tearDown()
|
||||
{
|
||||
FGTestApi::tearDown::shutdownTestGlobals();
|
||||
}
|
||||
|
||||
void NavRadioTests::setPositionAndStabilise(FGNavRadio* r, const SGGeod& g)
|
||||
{
|
||||
FGTestApi::setPosition(g);
|
||||
for (int i=0; i<60; ++i) {
|
||||
r->update(0.1);
|
||||
}
|
||||
}
|
||||
|
||||
std::string NavRadioTests::formatFrequency(double f)
|
||||
{
|
||||
char buf[16];
|
||||
::snprintf(buf, 16, "%3.2f", f);
|
||||
return buf;
|
||||
}
|
||||
|
||||
void NavRadioTests::testBasic()
|
||||
{
|
||||
SGPropertyNode_ptr configNode(new SGPropertyNode);
|
||||
configNode->setStringValue("name", "navtest");
|
||||
configNode->setIntValue("number", 2);
|
||||
std::unique_ptr<FGNavRadio> r(new FGNavRadio(configNode));
|
||||
|
||||
r->bind();
|
||||
r->init();
|
||||
|
||||
SGPropertyNode_ptr node = globals->get_props()->getNode("instrumentation/navtest[2]");
|
||||
node->setBoolValue("serviceable", true);
|
||||
// needed for the radio to power up
|
||||
globals->get_props()->setDoubleValue("systems/electrical/outputs/nav", 6.0);
|
||||
node->setDoubleValue("frequencies/selected-mhz", 113.8);
|
||||
|
||||
SGGeod pos = SGGeod::fromDegFt(-3.352780, 55.499199, 20000);
|
||||
setPositionAndStabilise(r.get(), pos);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(true, node->getBoolValue("operable"));
|
||||
CPPUNIT_ASSERT(node->getStringValue("nav-id") == "TLA");
|
||||
CPPUNIT_ASSERT_EQUAL(true, node->getBoolValue("in-range"));
|
||||
}
|
||||
|
||||
static const struct {
|
||||
int nvType;
|
||||
double nvLat, nvLon, nvAlt, nvFreq, nvRnge, nvTwst;
|
||||
const string& nvIden;
|
||||
double onRose, atDstNM, atAltFt, atHdg, rSele;
|
||||
bool vOpnl, vToFlag;
|
||||
double vSigNorm, vSigTolr, vHdgDefl, vDeflTolr, vHdgNorm, vDefnTolr, xtkTolr;
|
||||
const string& tDesc;
|
||||
} CDITestRoll[] = {
|
||||
//
|
||||
// Test Items: Add test cases here:
|
||||
// nv<= fields are copied direct from nav dat => <= Rx pos wrt navaid, Radial =><= v- Values expected / tested => <= Line / Desc for Mesg =>
|
||||
//Type Lat Lon Alt Freq Rnge Twist Iden] onRose atNm atAlt atHdg rSele Op To sigN - Tolr Defl - Tolr DNrm -Tolr xtkTolr
|
||||
//
|
||||
{ 3, 53.3, -2.26, 282, 113.55, 130, -5.0, "MCT", 25, 10.0, 4000, 200, 25, 1, 0, 1.0, 0.01, 0.0, 9.01, 0.0, 0.01, 50.0, "1: MCT EGCC On Radial" },
|
||||
{ 3, 53.3, -2.26, 282, 113.55, 130, -5.0, "MCT", 25, 10.0, 4000, 200, 25, 1, 0, 1.0, 0.01, 0.0, 9.01, 0.0, 0.01, 50.0, "2: MCT EGCC On Again " },
|
||||
{ 3, 53.3, -2.26, 282, 113.55, 130, -5.0, "MCT", 20, 20.0, 12000, 20, 25, 1, 0, 1.0, 0.01, 5.0, 0.1, 0.5, 0.01, 50.0, "3: MCT 5deg Off radial" },
|
||||
{ 3, 53.3, -2.26, 282, 113.55, 130, -5.0, "MCT", 33, 30.0, 16000, 100, 25, 1, 0, 1.0, 0.01, -8.0, 0.1, -0.8, 0.01, 50.0, "4: MCT 8deg Off radial" },
|
||||
{ 3, 53.3, -2.26, 282, 113.55, 130, -5.0, "MCT", 38, 40.0, 16000, 280, 25, 1, 0, 1.0, 0.01, -10.0, 0.1, -1.0, 0.01, 50.0, "5: MCT >10 Off radial" },
|
||||
{ 3, -31.9, 115.95, 87, 113.70, 130, -2.0, "PH", 222, 20.0, 12000, 220, 42, 1, 1, 1.0, 0.01, 0.0, 0.01, 0.0, 0.01, 50.0, "6: PH Perth W.Aus On Radial"},
|
||||
{ 3, -31.9, 115.95, 87, 113.70, 130, -2.0, "PH", 225, 20.0, 18000, 220, 42, 1, 1, 1.0, 0.01, 3.0, 0.01, 0.3, 0.01, 50.0, "7: PH +3deg Off radial" }
|
||||
};
|
||||
|
||||
void NavRadioTests::callNavRadioCDI() {
|
||||
//
|
||||
//2021Ja15 set flag for newnavradio
|
||||
//
|
||||
fgSetBool("/instrumentation/use-new-navradio", true);
|
||||
// setup
|
||||
SGPropertyNode_ptr configNode(new SGPropertyNode);
|
||||
configNode->setStringValue("name", "navtest");
|
||||
configNode->setIntValue("number", 2);
|
||||
std::unique_ptr<FGNavRadio> r(new FGNavRadio(configNode));
|
||||
r->bind();
|
||||
r->init();
|
||||
SGPropertyNode_ptr node = globals->get_props()->getNode("instrumentation/navtest[2]");
|
||||
node->setBoolValue("serviceable", true);
|
||||
// needed for the radio to power up
|
||||
globals->get_props()->setDoubleValue("systems/electrical/outputs/nav", 6.0);
|
||||
//
|
||||
int tale = sizeof(CDITestRoll) / sizeof(CDITestRoll[0]);
|
||||
for (int i = 0; (i < tale); i++) {
|
||||
// prep error message
|
||||
const string& itemDesc = " navradioCDI Item " + CDITestRoll[i].tDesc + " @ ";
|
||||
// Txmitting navaid
|
||||
node->setDoubleValue("frequencies/selected-mhz", CDITestRoll[i].nvFreq);
|
||||
node->setDoubleValue("radials/selected-deg", CDITestRoll[i].rSele);
|
||||
// tbd Filter on type as defined in nav dat
|
||||
//FGPositioned::TypeFilter f{FGPositioned::VOR};
|
||||
FGPositioned::TypeFilter f{{FGPositioned::VOR, FGPositioned::ILS, FGPositioned::LOC}};
|
||||
FGNavRecordRef nav = fgpositioned_cast<FGNavRecord>(FGPositioned::findClosestWithIdent(CDITestRoll[i].nvIden,
|
||||
SGGeod::fromDeg(CDITestRoll[i].nvLon, CDITestRoll[i].nvLat), &f));
|
||||
//
|
||||
// For VOR nav dat field 7: 'Twist' == Easterly rotation of Txmitter's 360 wrt True North c.f for Compass: 'Deviation West Rose is Best'
|
||||
// Rx posn is specified according to navaid's radials as printed on chart: True Bng = ( Radial on Rose + Twist ( Deviation ))
|
||||
// ( ftr: Both MCT -5 and PH -2 Are Negative Twists )
|
||||
SGGeod posWrtRadial = SGGeodesy::direct(nav->geod(), (CDITestRoll[i].onRose + CDITestRoll[i].nvTwst),
|
||||
(CDITestRoll[i].atDstNM * SG_NM_TO_METER));
|
||||
posWrtRadial.setElevationFt(CDITestRoll[i].atAltFt);
|
||||
setPositionAndStabilise(r.get(), posWrtRadial);
|
||||
// heading-deg property below means bearing to txmitter; calc copied from navradio.cxx !!!
|
||||
double bngToNavaid, az2, s;
|
||||
SGGeodesy::inverse(posWrtRadial, (nav->geod()), bngToNavaid, az2, s);
|
||||
// calc XTrack error
|
||||
double xtkE = sin((CDITestRoll[i].rSele - CDITestRoll[i].onRose) * SG_DEGREES_TO_RADIANS) * (CDITestRoll[i].atDstNM * SG_NM_TO_METER);
|
||||
// Verify expected vs Result
|
||||
string tMesg = itemDesc + "VOR type";
|
||||
CPPUNIT_ASSERT_MESSAGE(tMesg, nav->type() == FGPositioned::VOR);
|
||||
tMesg = itemDesc + "Operable";
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE(tMesg, CDITestRoll[i].vOpnl, node->getBoolValue("operable"));
|
||||
tMesg = itemDesc + "TO Flag";
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE(tMesg, CDITestRoll[i].vToFlag, node->getBoolValue("to-flag"));
|
||||
tMesg = itemDesc + "FROM Flag";
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE(tMesg, CDITestRoll[i].vToFlag, !node->getBoolValue("from-flag"));
|
||||
//
|
||||
tMesg = itemDesc + "nav-id";
|
||||
CPPUNIT_ASSERT_MESSAGE(tMesg, node->getStringValue("nav-id") == CDITestRoll[i].nvIden);
|
||||
//tbd VOR seems to not set selected-mhz-fmt
|
||||
// Converting nvFreq to string results in trailing zeros
|
||||
tMesg = itemDesc + "selected-mhz-fmt";
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE(tMesg, formatFrequency(CDITestRoll[i].nvFreq), string{node->getStringValue("frequencies/selected-mhz-fmt")});
|
||||
// actual-deg means: bearing seen on intstrument's dial: actual == onRose
|
||||
tMesg = itemDesc + "actual-deg";
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(tMesg, CDITestRoll[i].onRose, node->getDoubleValue("radials/actual-deg"), CDITestRoll[i].vDefnTolr);
|
||||
// heading-deg means true bearing to navaid, not affected by plane's heading
|
||||
tMesg = itemDesc + "heading-deg";
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(tMesg, bngToNavaid, node->getDoubleValue("heading-deg"), 1);
|
||||
//
|
||||
tMesg = itemDesc + "Sig Norm";
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(tMesg, CDITestRoll[i].vSigNorm, node->getDoubleValue("signal-quality-norm"), CDITestRoll[i].vSigTolr);
|
||||
tMesg = itemDesc + "needle defl";
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(tMesg, CDITestRoll[i].vHdgDefl, node->getDoubleValue("heading-needle-deflection"), CDITestRoll[i].vDeflTolr);
|
||||
tMesg = itemDesc + "defl norm";
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(tMesg, CDITestRoll[i].vHdgNorm, node->getDoubleValue("heading-needle-deflection-norm"), CDITestRoll[i].vDefnTolr);
|
||||
tMesg = itemDesc + "xTrack error";
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(tMesg, xtkE, node->getDoubleValue("crosstrack-error-m"), CDITestRoll[i].xtkTolr);
|
||||
}
|
||||
}
|
||||
|
||||
void NavRadioTests::callNewNavRadioCDI()
|
||||
{
|
||||
//
|
||||
//2021Ja15 set flag for newnavradio
|
||||
//
|
||||
fgSetBool("/instrumentation/use-new-navradio", true);
|
||||
// setup
|
||||
SGPropertyNode_ptr configNode(new SGPropertyNode);
|
||||
configNode->setStringValue("name", "navtest");
|
||||
configNode->setIntValue("number", 2);
|
||||
std::unique_ptr<FGNavRadio> r(new FGNavRadio(configNode));
|
||||
r->bind();
|
||||
r->init();
|
||||
SGPropertyNode_ptr node = globals->get_props()->getNode("instrumentation/navtest[2]");
|
||||
node->setBoolValue("serviceable", true);
|
||||
// needed for the radio to power up
|
||||
globals->get_props()->setDoubleValue("systems/electrical/outputs/nav", 6.0);
|
||||
//
|
||||
int tale = sizeof(CDITestRoll) / sizeof(CDITestRoll[0]);
|
||||
for (int i = 0; (i < tale); i++) {
|
||||
// prep error message
|
||||
const string& itemDesc = " navradioCDI Item " + CDITestRoll[i].tDesc + " @ ";
|
||||
// Txmitting navaid
|
||||
node->setDoubleValue("frequencies/selected-mhz", CDITestRoll[i].nvFreq);
|
||||
node->setDoubleValue("radials/selected-deg", CDITestRoll[i].rSele);
|
||||
// tbd Filter on type as defined in nav dat
|
||||
//FGPositioned::TypeFilter f{FGPositioned::VOR};
|
||||
FGPositioned::TypeFilter f{{FGPositioned::VOR, FGPositioned::ILS, FGPositioned::LOC}};
|
||||
FGNavRecordRef nav = fgpositioned_cast<FGNavRecord>(FGPositioned::findClosestWithIdent(CDITestRoll[i].nvIden,
|
||||
SGGeod::fromDeg(CDITestRoll[i].nvLon, CDITestRoll[i].nvLat), &f));
|
||||
//
|
||||
// For VOR nav dat field 7: 'Twist' == Easterly rotation of Txmitter's 360 wrt True North c.f for Compass: 'Deviation West Rose is Best'
|
||||
// Rx posn is specified according to navaid's radials as printed on chart: True Bng = ( Radial on Rose + Twist ( Deviation ))
|
||||
// ( ftr: Both MCT -5 and PH -2 Are Negative Twists )
|
||||
SGGeod posWrtRadial = SGGeodesy::direct(nav->geod(), (CDITestRoll[i].onRose + CDITestRoll[i].nvTwst),
|
||||
(CDITestRoll[i].atDstNM * SG_NM_TO_METER));
|
||||
posWrtRadial.setElevationFt(CDITestRoll[i].atAltFt);
|
||||
setPositionAndStabilise(r.get(), posWrtRadial);
|
||||
// heading-deg property below means bearing to txmitter; calc copied from navradio.cxx !!!
|
||||
double bngToNavaid, az2, s;
|
||||
SGGeodesy::inverse(posWrtRadial, (nav->geod()), bngToNavaid, az2, s);
|
||||
// calc XTrack error
|
||||
double xtkE = sin((CDITestRoll[i].rSele - CDITestRoll[i].onRose) * SG_DEGREES_TO_RADIANS) * (CDITestRoll[i].atDstNM * SG_NM_TO_METER);
|
||||
// Verify expected vs Result
|
||||
string tMesg = itemDesc + "VOR type";
|
||||
CPPUNIT_ASSERT_MESSAGE(tMesg, nav->type() == FGPositioned::VOR);
|
||||
tMesg = itemDesc + "Operable";
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE(tMesg, CDITestRoll[i].vOpnl, node->getBoolValue("operable"));
|
||||
tMesg = itemDesc + "TO Flag";
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE(tMesg, CDITestRoll[i].vToFlag, node->getBoolValue("to-flag"));
|
||||
tMesg = itemDesc + "FROM Flag";
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE(tMesg, CDITestRoll[i].vToFlag, !node->getBoolValue("from-flag"));
|
||||
//
|
||||
tMesg = itemDesc + "nav-id";
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE(tMesg, CDITestRoll[i].nvIden, string{node->getStringValue("nav-id")});
|
||||
|
||||
// Converting nvFreq to string results in trailing zeros
|
||||
tMesg = itemDesc + "selected-mhz-fmt";
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE(tMesg, formatFrequency(CDITestRoll[i].nvFreq), string{node->getStringValue("frequencies/selected-mhz-fmt")});
|
||||
|
||||
// actual-deg means: bearing seen on intstrument's dial: actual == onRose
|
||||
tMesg = itemDesc + "actual-deg";
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(tMesg, CDITestRoll[i].onRose, node->getDoubleValue("radials/actual-deg"), CDITestRoll[i].vDefnTolr);
|
||||
// heading-deg means true bearing to navaid, not affected by plane's heading
|
||||
tMesg = itemDesc + "heading-deg";
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(tMesg, bngToNavaid, node->getDoubleValue("heading-deg"), 1);
|
||||
//
|
||||
tMesg = itemDesc + "Sig Norm";
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(tMesg, CDITestRoll[i].vSigNorm, node->getDoubleValue("signal-quality-norm"), CDITestRoll[i].vSigTolr);
|
||||
tMesg = itemDesc + "needle defl";
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(tMesg, CDITestRoll[i].vHdgDefl, node->getDoubleValue("heading-needle-deflection"), CDITestRoll[i].vDeflTolr);
|
||||
tMesg = itemDesc + "defl norm";
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(tMesg, CDITestRoll[i].vHdgNorm, node->getDoubleValue("heading-needle-deflection-norm"), CDITestRoll[i].vDefnTolr);
|
||||
tMesg = itemDesc + "xTrack error";
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(tMesg, xtkE, node->getDoubleValue("crosstrack-error-m"), CDITestRoll[i].xtkTolr);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct {
|
||||
int nvType;
|
||||
double nvLat, nvLon, nvAlt, nvFreq, nvRnge, nvTwst;
|
||||
const string& nvIden;
|
||||
double onRose, atDstNM, atAltFt, atHdg, rSele;
|
||||
bool vOpnl, vToFlag;
|
||||
double vSigNorm, vSigTolr, vHdgDefl, vDeflTolr, vHdgNorm, vDefnTolr, xtkTolr;
|
||||
const string& tDesc;
|
||||
|
||||
} ILSTestRoll[] = {
|
||||
//
|
||||
// Ref pilotscafe.com ILS width: 700ft wide at thrsh. WIthin range, sensed at +-35dg @ 10NM +-10dg @ 18NM
|
||||
//
|
||||
// Test Items: Add test cases here:
|
||||
// nv<= fields are copied direct from nav dat => <= Rx pos wrt navaid, Radial => <= v- Values expected / tested => <= Item - Desc =>
|
||||
//Typ Lat Lon Alt Freq Rnge TruHdng Iden] onRose atNm atAlt atHdg rSele Op To sigN - Tolr Defl - Tolr DNrm -Tolr xtkTol ]
|
||||
//
|
||||
{4, 37.626, -122.394, 8, 109.55, 18, 297.932, "ISFO", 117.932, 2.5, 2500, 27, 297.932, 1, 1, 1.0, 0.01, 0.0, 0.01, 0.0, 0.01, 50.0, "1: ISFO On LOC"}, //
|
||||
{4, 37.626, -122.394, 8, 109.55, 18, 297.932, "ISFO", 116.932, 6.0, 1500, 27, 297.932, 1, 1, 1.0, 0.01, -1.0, 0.10, -0.1, 0.01, 50.0, "2: ISFO -1 Deg"}, //
|
||||
{4, 37.626, -122.394, 8, 109.55, 18, 297.932, "ISFO", 118.932, 6.0, 1500, 27, 297.932, 1, 1, 1.0, 0.01, 1.0, 0.01, -0.1, 0.01, 50.0, "3: ISFO +1 Deg"}, //
|
||||
{4, 37.626, -122.394, 8, 109.55, 18, 297.932, "ISFO", 113.932, 3.0, 600, 27, 297.932, 1, 1, 1.0, 0.01, -3.0, 0.10, -0.1, 0.01, 50.0, "4: ISFO < MinDefl"}, //
|
||||
{4, 37.626, -122.394, 8, 109.55, 18, 297.932, "ISFO", 121.932, 3.0, 600, 27, 297.932, 1, 1, 1.0, 0.01, 3.0, 0.01, -0.1, 0.01, 50.0, "5: ISFO > MzxDefl"}, //
|
||||
{4, 37.626, -122.394, 8, 109.55, 18, 297.932, "ISFO", 297.932, 4.0, 1500, 27, 297.932, 1, 1, 1.0, 0.01, 0.0, 0.01, 0.0, 0.01, 50.0, "6: ISFO BC On LOC"}, //
|
||||
{4, 37.626, -122.394, 8, 109.55, 18, 297.932, "ISFO", 296.932, 4.0, 1500, 27, 297.932, 1, 1, 1.0, 0.01, 1.0, 0.10, -0.1, 0.01, 50.0, "7: ISFO BC -1 Deg"}, //
|
||||
{4, 37.626, -122.394, 8, 109.55, 18, 297.932, "ISFO", 298.932, 4.0, 1500, 27, 297.932, 1, 1, 1.0, 0.01, -1.0, 0.01, -0.1, 0.01, 50.0, "8: ISFO BC +1 Deg"}, //
|
||||
{4, 37.626, -122.394, 8, 109.55, 18, 297.932, "ISFO", 293.932, 4.0, 1500, 27, 297.932, 1, 1, 1.0, 0.01, 3.0, 0.10, -0.1, 0.01, 50.0, "9: ISFO BC > MaxD"}, //
|
||||
{4, 37.626, -122.394, 8, 109.55, 18, 297.932, "ISFO", 301.932, 4.0, 1500, 27, 297.932, 1, 1, 1.0, 0.01, -3.0, 0.01, -0.1, 0.01, 50.0, "10: ISFO BC < MinD"} //
|
||||
};
|
||||
|
||||
void NavRadioTests::callNavRadioILS()
|
||||
{
|
||||
// set flag for newnavradio
|
||||
fgSetBool("/instrumentation/use-new-navradio", false);
|
||||
// setup
|
||||
SGPropertyNode_ptr configNode(new SGPropertyNode);
|
||||
configNode->setStringValue("name", "navtest");
|
||||
configNode->setIntValue("number", 2);
|
||||
std::unique_ptr<FGNavRadio> r(new FGNavRadio(configNode));
|
||||
r->bind();
|
||||
r->init();
|
||||
SGPropertyNode_ptr node = globals->get_props()->getNode("instrumentation/navtest[2]");
|
||||
node->setBoolValue("serviceable", true);
|
||||
// needed for the radio to power up
|
||||
globals->get_props()->setDoubleValue("systems/electrical/outputs/nav", 6.0);
|
||||
//
|
||||
int tale = sizeof(ILSTestRoll) / sizeof(ILSTestRoll[0]);
|
||||
for (int i = 0; (i < tale); i++) {
|
||||
// prep error message
|
||||
const string& itemDesc = " navRadioILS Item " + ILSTestRoll[i].tDesc + " @ ";
|
||||
// Txmitting navaid
|
||||
node->setDoubleValue("frequencies/selected-mhz", ILSTestRoll[i].nvFreq);
|
||||
node->setDoubleValue("radials/selected-deg", ILSTestRoll[i].rSele);
|
||||
FGPositioned::TypeFilter f{{FGPositioned::VOR, FGPositioned::ILS, FGPositioned::LOC}};
|
||||
FGNavRecordRef nav = fgpositioned_cast<FGNavRecord>(FGPositioned::findClosestWithIdent(ILSTestRoll[i].nvIden,
|
||||
SGGeod::fromDeg(ILSTestRoll[i].nvLon, ILSTestRoll[i].nvLat), &f));
|
||||
SGGeod posWrtRadial = SGGeodesy::direct(nav->geod(), (ILSTestRoll[i].onRose), (ILSTestRoll[i].atDstNM * SG_NM_TO_METER));
|
||||
posWrtRadial.setElevationFt(ILSTestRoll[i].atAltFt);
|
||||
setPositionAndStabilise(r.get(), posWrtRadial);
|
||||
// heading-deg property below means bearing to txmitter; calc copied from navradio.cxx !!!
|
||||
double bngToNavaid, az2, s;
|
||||
SGGeodesy::inverse(posWrtRadial, (nav->geod()), bngToNavaid, az2, s);
|
||||
double xtkE = sin((ILSTestRoll[i].rSele - ILSTestRoll[i].onRose) * SG_DEGREES_TO_RADIANS) * (ILSTestRoll[i].atDstNM * SG_NM_TO_METER);
|
||||
//
|
||||
const double locWidth = nav->localizerWidth();
|
||||
// Expected Defl / Scaling is hokey because ILS width varies ??
|
||||
const double deflectionScale = 20.0 / locWidth; // 20 degrees is full VOR swing (-10 to +10 degrees)
|
||||
double xpecDefl = (ILSTestRoll[i].vHdgDefl * deflectionScale);
|
||||
xpecDefl = (xpecDefl > 10) ? 10 : xpecDefl;
|
||||
xpecDefl = (xpecDefl < -10) ? -10 : xpecDefl;
|
||||
//
|
||||
// Verify expected: Operational and To flags
|
||||
string tMesg = itemDesc + "ILS type";
|
||||
CPPUNIT_ASSERT_MESSAGE(tMesg, nav->type() == FGPositioned::ILS);
|
||||
tMesg = itemDesc + "Operable";
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE(tMesg, ILSTestRoll[i].vOpnl, node->getBoolValue("operable"));
|
||||
tMesg = itemDesc + "TO Flag";
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE(tMesg, ILSTestRoll[i].vToFlag, node->getBoolValue("to-flag"));
|
||||
tMesg = itemDesc + "FROM Flag";
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE(tMesg, ILSTestRoll[i].vToFlag, !node->getBoolValue("from-flag"));
|
||||
//
|
||||
tMesg = itemDesc + "heading-deg";
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(tMesg, bngToNavaid, node->getDoubleValue("heading-deg"), 1);
|
||||
tMesg = itemDesc + "nav-id";
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE(tMesg, ILSTestRoll[i].nvIden, string{node->getStringValue("nav-id")});
|
||||
// Converting nvFreq to string results in trailing zeros
|
||||
tMesg = itemDesc + "selected-mhz-fmt";
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE(tMesg, formatFrequency(ILSTestRoll[i].nvFreq), string{node->getStringValue("frequencies/selected-mhz-fmt")});
|
||||
|
||||
// actual-deg means: bearing seen on intstrument's dial: actual == onRose
|
||||
tMesg = itemDesc + "actual-deg";
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(tMesg, ILSTestRoll[i].onRose, node->getDoubleValue("radials/actual-deg"), ILSTestRoll[i].vDefnTolr);
|
||||
tMesg = itemDesc + "Sig Norm";
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(tMesg, ILSTestRoll[i].vSigNorm, node->getDoubleValue("signal-quality-norm"), ILSTestRoll[i].vSigTolr);
|
||||
tMesg = itemDesc + "needle defl";
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(tMesg, xpecDefl, node->getDoubleValue("heading-needle-deflection"), ILSTestRoll[i].vDeflTolr);
|
||||
tMesg = itemDesc + "defl norm";
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(tMesg, (xpecDefl * 0.1), node->getDoubleValue("heading-needle-deflection-norm"), ILSTestRoll[i].vDefnTolr);
|
||||
tMesg = itemDesc + "xTrack error";
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(tMesg, xtkE, node->getDoubleValue("crosstrack-error-m"), ILSTestRoll[i].xtkTolr);
|
||||
}
|
||||
}
|
||||
|
||||
void NavRadioTests::callNewNavRadioILS()
|
||||
{
|
||||
// set flag for newnavradio
|
||||
fgSetBool("/instrumentation/use-new-navradio", true);
|
||||
// setup
|
||||
SGPropertyNode_ptr configNode(new SGPropertyNode);
|
||||
configNode->setStringValue("name", "navtest");
|
||||
configNode->setIntValue("number", 2);
|
||||
std::unique_ptr<FGNavRadio> r(new FGNavRadio(configNode));
|
||||
r->bind();
|
||||
r->init();
|
||||
SGPropertyNode_ptr node = globals->get_props()->getNode("instrumentation/navtest[2]");
|
||||
node->setBoolValue("serviceable", true);
|
||||
// needed for the radio to power up
|
||||
globals->get_props()->setDoubleValue("systems/electrical/outputs/nav", 6.0);
|
||||
//
|
||||
int tale = sizeof(ILSTestRoll) / sizeof(ILSTestRoll[0]);
|
||||
for (int i = 0; (i < tale); i++) {
|
||||
// prep error message
|
||||
const string & itemDesc = "newNavRadioILS Item " + ILSTestRoll[i].tDesc + " @ ";
|
||||
// Txmitting navaid
|
||||
node->setDoubleValue("frequencies/selected-mhz", ILSTestRoll[i].nvFreq);
|
||||
node->setDoubleValue("radials/selected-deg", ILSTestRoll[i].rSele);
|
||||
FGPositioned::TypeFilter f{{FGPositioned::VOR, FGPositioned::ILS, FGPositioned::LOC}};
|
||||
FGNavRecordRef nav = fgpositioned_cast<FGNavRecord>(FGPositioned::findClosestWithIdent(ILSTestRoll[i].nvIden, \
|
||||
SGGeod::fromDeg( ILSTestRoll[i].nvLon, ILSTestRoll[i].nvLat), &f));
|
||||
SGGeod posWrtRadial = SGGeodesy::direct(nav->geod(), (ILSTestRoll[i].onRose ), (ILSTestRoll[i].atDstNM * SG_NM_TO_METER));
|
||||
posWrtRadial.setElevationFt(ILSTestRoll[i].atAltFt);
|
||||
setPositionAndStabilise(r.get(), posWrtRadial);
|
||||
// heading-deg property below means bearing to txmitter; calc copied from navradio.cxx !!!
|
||||
double bngToNavaid, az2, s;
|
||||
SGGeodesy::inverse(posWrtRadial, (nav->geod()), bngToNavaid, az2, s);
|
||||
double xtkE = sin( (ILSTestRoll[i].rSele - ILSTestRoll[i].onRose) * SG_DEGREES_TO_RADIANS) \
|
||||
* ( ILSTestRoll[i].atDstNM * SG_NM_TO_METER ) ;
|
||||
//
|
||||
const double locWidth = nav->localizerWidth();
|
||||
// Expected Defl / Scaling is hokey because ILS width varies ??
|
||||
const double deflectionScale = 20.0 / locWidth; // 20 degrees is full VOR swing (-10 to +10 degrees)
|
||||
double xpecDefl = (ILSTestRoll[i].vHdgDefl * deflectionScale);
|
||||
xpecDefl = ( xpecDefl > 10 ) ? 10 : xpecDefl;
|
||||
xpecDefl = ( xpecDefl < -10 ) ? -10 : xpecDefl;
|
||||
//
|
||||
// Verify expected: Operational and To flags
|
||||
string tMesg = itemDesc + "ILS type";
|
||||
CPPUNIT_ASSERT_MESSAGE( tMesg, nav->type() == FGPositioned::ILS);
|
||||
tMesg = itemDesc + "Operable";
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE( tMesg, ILSTestRoll[i].vOpnl, node->getBoolValue("operable"));
|
||||
tMesg = itemDesc + "TO Flag";
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE( tMesg, ILSTestRoll[i].vToFlag, node->getBoolValue("to-flag"));
|
||||
tMesg = itemDesc + "FROM Flag";
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE( tMesg, ILSTestRoll[i].vToFlag, !node->getBoolValue("from-flag"));
|
||||
//
|
||||
tMesg = itemDesc + "heading-deg";
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE( tMesg, bngToNavaid, node->getDoubleValue("heading-deg"), 1);
|
||||
tMesg = itemDesc + "nav-id";
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE( tMesg, ILSTestRoll[i].nvIden, string{node->getStringValue("nav-id")});
|
||||
// Converting nvFreq to string results in trailing zeros
|
||||
tMesg = itemDesc + "selected-mhz-fmt";
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE(tMesg, formatFrequency(ILSTestRoll[i].nvFreq), string{node->getStringValue("frequencies/selected-mhz-fmt")});
|
||||
// actual-deg means: bearing seen on intstrument's dial: actual == onRose
|
||||
tMesg = itemDesc + "actual-deg";
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE( tMesg, ILSTestRoll[i].onRose, node->getDoubleValue("radials/actual-deg"), ILSTestRoll[i].vDefnTolr);
|
||||
tMesg = itemDesc + "Sig Norm";
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE( tMesg, ILSTestRoll[i].vSigNorm, node->getDoubleValue("signal-quality-norm"), ILSTestRoll[i].vSigTolr);
|
||||
tMesg = itemDesc + "needle defl";
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE( tMesg, xpecDefl, node->getDoubleValue("heading-needle-deflection"), ILSTestRoll[i].vDeflTolr);
|
||||
tMesg = itemDesc + "defl norm";
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE( tMesg, (xpecDefl * 0.1 ), node->getDoubleValue("heading-needle-deflection-norm"), ILSTestRoll[i].vDefnTolr);
|
||||
tMesg = itemDesc + "xTrack error";
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE( tMesg, xtkE, node->getDoubleValue("crosstrack-error-m"), ILSTestRoll[i].xtkTolr);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct {
|
||||
int nvType;
|
||||
double nvLat, nvLon, nvAlt, nvFreq, nvRnge, nvAzim;
|
||||
const string& nvIden;
|
||||
double onRose, atDstNM, atAltFt, plusDeg, atHdg, rTruDeg;
|
||||
bool vInRnge, vFalse;
|
||||
double vSigNorm, vSigTolr, vGSDefl, vDeflTolr, vGSDefn, vDefnTolr;
|
||||
const string& tDesc;
|
||||
} GSTestRoll[] = {
|
||||
//
|
||||
// Test Items: Add test cases here:
|
||||
// nv<= fields are copied direct from nav dat => <= Rx pos wrt navaid, Radial => <= v- Values expected / tested => <= Item - Desc =>
|
||||
//Typ Lat Lon Alt Freq Rnge GSAzim Iden] onRose atNm atAlt or Deg atHdg TruDeg Rng Fls SgN - Tolr GSDefl-Tolr GSDefn-Tolr ]
|
||||
//
|
||||
{ 4, 52.563, 13.305, 101, 110.10, 10, 3.000, "ITLW", 117.932, 8.0, 0, 0, 80.828, 260.857, 1, 0, 1.0, 0.01, 0.0, 0.1, 0.0, 0.01, "1: EDDT 26R +0 deg" }, //
|
||||
{ 4, 52.563, 13.305, 101, 110.10, 10, 3.000, "ITLW", 117.932, 4.0, 0, 0.50, 80.828, 260.857, 1, 0, 1.0, 0.01, 0.0, 0.1, 0.0, 0.01, "2: EDDT 26R +0.5 d" }, //
|
||||
{ 4, 52.563, 13.305, 101, 110.10, 10, 3.000, "ITLW", 117.932, 2.0, 0, -1.00, 80.828, 260.857, 1, 0, 1.0, 0.01, 0.0, 0.1, 0.0, 0.01, "3: EDDT 26R -1 deg" }, //
|
||||
{ 4, 52.563, 13.305, 101, 110.10, 10, 3.000, "ITLW", 117.932, 5.0, 0, 3.00, 80.828, 260.857, 1, 1, 1.0, 0.01, 0.0, 0.1, 0.0, 0.01, "4: EDDT 26R +3.0 Fls"}, //
|
||||
{ 4, 52.563, 13.305, 101, 110.10, 10, 3.000, "ITLW", 117.932, 3.0, 0, +2.65, 80.828, 260.857, 1, 1, 1.0, 0.01,-1.75, 0.1, -0.5, 0.01, "5: EDDT 26R +3.5 Fls"}, //
|
||||
// { 4, 51.464, -0.439, 50, 109.50, 10, 3.000, "ILL", 89.690, 7.5, 2500, 0, 80.828, 269.690, 1, 0, 1.0, 0.01, 0.0, 0.1, 0.0, 0.01, "6: EGLL 27L 2K5 7M5"}, // Fail: Rx finds IBB
|
||||
// { 4, 51.464, -0.439, 50, 109.50, 10, 3.000, "ILL", 89.690, 9.0, 3000, 0, 80.828, 269.690, 1, 0, 1.0, 0.01, 0.0, 0.1, 0.0, 0.01, "7: EGLL 27L 3K0 9M0"}, //
|
||||
// { 4, 51.464, -0.439, 50, 109.50, 10, 3.000, "ILL", 89.690,17.5, 4000, 0, 80.828, 269.690, 1, 0, 1.0, 0.01, 0.0, 0.1, 0.0, 0.01, "8: EGLL 27L 4K 17M5"}, //
|
||||
// { 4, 51.464, -0.439, 50, 109.50, 10, 3.000, "ILL", 89.690,25.0, 4000, 0, 80.828, 269.690, 1, 0, 1.0, 0.01, 0.0, 0.1, 0.0, 0.01, "9: EGLL 27L 4K 25M0"} //
|
||||
};
|
||||
|
||||
void NavRadioTests::callNavRadioGS() {
|
||||
//set flag for newnavradio
|
||||
fgSetBool("/instrumentation/use-new-navradio", false);
|
||||
// setup
|
||||
SGPropertyNode_ptr configNode(new SGPropertyNode);
|
||||
configNode->setStringValue("name", "navtest");
|
||||
configNode->setIntValue("number", 2);
|
||||
std::unique_ptr<FGNavRadio> r(new FGNavRadio(configNode));
|
||||
r->bind();
|
||||
r->init();
|
||||
SGPropertyNode_ptr node = globals->get_props()->getNode("instrumentation/navtest[2]");
|
||||
node->setBoolValue("serviceable", true);
|
||||
// needed for the radio to power up
|
||||
globals->get_props()->setDoubleValue("systems/electrical/outputs/nav", 6.0);
|
||||
//
|
||||
// GS beam depth +-0.7deg; needle deflection -+3.5; gs-direct: Rxvr elevation from GS Txmitter level
|
||||
//
|
||||
const double halfBeam = 0.700;
|
||||
const double deflFact = 3.500;
|
||||
//
|
||||
int tale = sizeof(GSTestRoll) / sizeof(GSTestRoll[0]);
|
||||
for (int i = 0; (i < tale); i++) {
|
||||
// prep error message
|
||||
const string& itemDesc = " navRadioGS Item " + GSTestRoll[i].tDesc + " @ ";
|
||||
// Txmitting navaid
|
||||
node->setDoubleValue("frequencies/selected-mhz", GSTestRoll[i].nvFreq);
|
||||
node->setDoubleValue("radials/selected-deg", GSTestRoll[i].rTruDeg);
|
||||
FGPositioned::TypeFilter f{{FGPositioned::VOR, FGPositioned::GS, FGPositioned::LOC}};
|
||||
FGNavRecordRef nav = fgpositioned_cast<FGNavRecord>(FGPositioned::findClosestWithIdent(GSTestRoll[i].nvIden,
|
||||
SGGeod::fromDeg(GSTestRoll[i].nvLon, GSTestRoll[i].nvLat), &f));
|
||||
|
||||
// Check for proper nav type befor doing GS things
|
||||
string tMesg = itemDesc + "GS Type ?";
|
||||
CPPUNIT_ASSERT_MESSAGE(tMesg, nav->type() == FGPositioned::GS);
|
||||
/////////////
|
||||
// derive the GS geometry in cartesian vectors, to match what navradio.cxx does
|
||||
SGGeod aboveGS = nav->geod();
|
||||
aboveGS.setElevationM(nav->geod().getElevationM() + 100);
|
||||
SGVec3d gsVerticalAxis = SGVec3d::fromGeod(aboveGS) - nav->cart();
|
||||
// intentionally different approach to what navradio uses
|
||||
gsVerticalAxis *= 0.01; // make it per meter, since we used 100m above
|
||||
// derive the baseline
|
||||
SGQuatd baseLineRot = SGQuatd::fromLonLat(nav->geod()) * SGQuatd::fromHeadAttBankDeg(GSTestRoll[i].atHdg, 0, 0);
|
||||
SGVec3d gsAltAxis = baseLineRot.backTransform(SGVec3d(1.0, 0.0, 0.0));
|
||||
const SGVec3d gsCart = nav->cart();
|
||||
//////////////////
|
||||
// expected deflection is calculated here if atAltFt is non-zero
|
||||
double xpecAzim, xpecDefl, xpecDefn;
|
||||
double bngToNavaid, az2, s;
|
||||
if (GSTestRoll[i].atAltFt == 0) {
|
||||
// Line item atAlt is zero so use degrees off GlideSlope for Rx position
|
||||
double gsAngleRad = (nav->glideSlopeAngleDeg() + GSTestRoll[i].plusDeg) * SG_DEGREES_TO_RADIANS;
|
||||
SGVec3d radioPos = gsCart;
|
||||
radioPos += (gsVerticalAxis * tan(gsAngleRad) * GSTestRoll[i].atDstNM * SG_NM_TO_METER);
|
||||
radioPos += (gsAltAxis * GSTestRoll[i].atDstNM * SG_NM_TO_METER);
|
||||
setPositionAndStabilise(r.get(), SGGeod::fromCart(radioPos));
|
||||
xpecAzim = (GSTestRoll[i].nvAzim + GSTestRoll[i].plusDeg);
|
||||
} else {
|
||||
// Line item atAlt is non zero so use altitude for Rx position
|
||||
SGGeod p = SGGeodesy::direct(nav->geod(), GSTestRoll[i].atAltFt, GSTestRoll[i].atDstNM * SG_NM_TO_METER);
|
||||
p.setElevationFt(GSTestRoll[i].atAltFt);
|
||||
setPositionAndStabilise(r.get(), p);
|
||||
//tbd calc Rx Azim from Tx for altitude case
|
||||
SGGeodesy::inverse(p, nav->geod(), bngToNavaid, az2, s);
|
||||
xpecAzim = SG_RADIANS_TO_DEGREES * (atan((GSTestRoll[i].atAltFt * SG_FEET_TO_METER) / s));
|
||||
}
|
||||
//
|
||||
if (GSTestRoll[i].vFalse) {
|
||||
// rxFlse indicates false signal, use deflections manually entered in Item
|
||||
xpecDefl = GSTestRoll[i].vGSDefl;
|
||||
xpecDefn = GSTestRoll[i].vGSDefn;
|
||||
} else {
|
||||
if (GSTestRoll[i].atAltFt == 0) {
|
||||
// not rxFlse and atAltFt is zero : calculate needle deflections from Rx posn degrees wrt beam
|
||||
xpecDefn = 0 - GSTestRoll[i].plusDeg / halfBeam; // Plane high: needle below
|
||||
} else {
|
||||
// not rxFlse and atAltFt non zero : calculate needle deflections from Rx posn azimuth wrt beam
|
||||
xpecDefn = (GSTestRoll[i].nvAzim - az2) / halfBeam; // Plane high: needle below;
|
||||
}
|
||||
xpecDefn = (xpecDefn > 1) ? 1 : xpecDefn;
|
||||
xpecDefn = (xpecDefn < -1) ? -1 : xpecDefn;
|
||||
xpecDefl = xpecDefn * deflFact;
|
||||
}
|
||||
//
|
||||
// Verify expected:
|
||||
tMesg = itemDesc + "Operable";
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE(tMesg, GSTestRoll[i].vInRnge, node->getBoolValue("operable"));
|
||||
tMesg = itemDesc + "nav-id";
|
||||
string dddbug = node->getStringValue("nav-id");
|
||||
CPPUNIT_ASSERT_MESSAGE(tMesg, node->getStringValue("nav-id") == GSTestRoll[i].nvIden);
|
||||
tMesg = itemDesc + "Sig Norm";
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(tMesg, GSTestRoll[i].vSigNorm, node->getDoubleValue("signal-quality-norm"), GSTestRoll[i].vSigTolr);
|
||||
tMesg = itemDesc + "gs-direct";
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(tMesg, xpecAzim, node->getDoubleValue("gs-direct-deg"), 1);
|
||||
tMesg = itemDesc + "needle defl";
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(tMesg, xpecDefl, node->getDoubleValue("gs-needle-deflection"), GSTestRoll[i].vDeflTolr);
|
||||
tMesg = itemDesc + "defl norm";
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(tMesg, xpecDefn, node->getDoubleValue("gs-needle-deflection-norm"), GSTestRoll[i].vDefnTolr);
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
void NavRadioTests::callNewNavRadioGS()
|
||||
{
|
||||
//set flag for newnavradio
|
||||
fgSetBool("/instrumentation/use-new-navradio", true);
|
||||
// setup
|
||||
SGPropertyNode_ptr configNode(new SGPropertyNode);
|
||||
configNode->setStringValue("name", "navtest");
|
||||
configNode->setIntValue("number", 2);
|
||||
std::unique_ptr<FGNavRadio> r(new FGNavRadio(configNode));
|
||||
r->bind();
|
||||
r->init();
|
||||
SGPropertyNode_ptr node = globals->get_props()->getNode("instrumentation/navtest[2]");
|
||||
node->setBoolValue("serviceable", true);
|
||||
// needed for the radio to power up
|
||||
globals->get_props()->setDoubleValue("systems/electrical/outputs/nav", 6.0);
|
||||
//
|
||||
// GS beam depth +-0.7deg; needle deflection -+3.5; gs-direct: Rxvr elevation from GS Txmitter level
|
||||
//
|
||||
const double halfBeam = 0.700;
|
||||
const double deflFact = 3.500;
|
||||
//
|
||||
int tale = sizeof(GSTestRoll) / sizeof(GSTestRoll[0]);
|
||||
for (int i = 0; (i < tale); i++) {
|
||||
// prep error message
|
||||
const string& itemDesc = "newnavRadioGS Item " + GSTestRoll[i].tDesc + " @ ";
|
||||
// Txmitting navaid
|
||||
node->setDoubleValue("frequencies/selected-mhz", GSTestRoll[i].nvFreq);
|
||||
node->setDoubleValue("radials/selected-deg", GSTestRoll[i].rTruDeg);
|
||||
FGPositioned::TypeFilter f{{FGPositioned::VOR, FGPositioned::GS, FGPositioned::LOC}};
|
||||
FGNavRecordRef nav = fgpositioned_cast<FGNavRecord>(FGPositioned::findClosestWithIdent(GSTestRoll[i].nvIden,
|
||||
SGGeod::fromDeg(GSTestRoll[i].nvLon, GSTestRoll[i].nvLat), &f));
|
||||
|
||||
// Check for proper nav type befor doing GS things
|
||||
string tMesg = itemDesc + "GS Type ?";
|
||||
CPPUNIT_ASSERT_MESSAGE(tMesg, nav->type() == FGPositioned::GS);
|
||||
/////////////
|
||||
// derive the GS geometry in cartesian vectors, to match what navradio.cxx does
|
||||
SGGeod aboveGS = nav->geod();
|
||||
aboveGS.setElevationM(nav->geod().getElevationM() + 100);
|
||||
SGVec3d gsVerticalAxis = SGVec3d::fromGeod(aboveGS) - nav->cart();
|
||||
// intentionally different approach to what navradio uses
|
||||
gsVerticalAxis *= 0.01; // make it per meter, since we used 100m above
|
||||
// derive the baseline
|
||||
SGQuatd baseLineRot = SGQuatd::fromLonLat(nav->geod()) * SGQuatd::fromHeadAttBankDeg(GSTestRoll[i].atHdg, 0, 0);
|
||||
SGVec3d gsAltAxis = baseLineRot.backTransform(SGVec3d(1.0, 0.0, 0.0));
|
||||
const SGVec3d gsCart = nav->cart();
|
||||
//////////////////
|
||||
// expected deflection is calculated here if atAltFt is non-zero
|
||||
double xpecAzim, xpecDefl, xpecDefn;
|
||||
double bngToNavaid, az2, s;
|
||||
if (GSTestRoll[i].atAltFt == 0) {
|
||||
// Line item atAlt is zero so use degrees off GlideSlope for Rx position
|
||||
double gsAngleRad = (nav->glideSlopeAngleDeg() + GSTestRoll[i].plusDeg) * SG_DEGREES_TO_RADIANS;
|
||||
SGVec3d radioPos = gsCart;
|
||||
radioPos += (gsVerticalAxis * tan(gsAngleRad) * GSTestRoll[i].atDstNM * SG_NM_TO_METER);
|
||||
radioPos += (gsAltAxis * GSTestRoll[i].atDstNM * SG_NM_TO_METER);
|
||||
setPositionAndStabilise(r.get(), SGGeod::fromCart(radioPos));
|
||||
xpecAzim = (GSTestRoll[i].nvAzim + GSTestRoll[i].plusDeg);
|
||||
} else {
|
||||
// Line item atAlt is non zero so use altitude for Rx position
|
||||
SGGeod p = SGGeodesy::direct(nav->geod(), GSTestRoll[i].atAltFt, GSTestRoll[i].atDstNM * SG_NM_TO_METER);
|
||||
p.setElevationFt(GSTestRoll[i].atAltFt);
|
||||
setPositionAndStabilise(r.get(), p);
|
||||
//tbd calc Rx Azim from Tx for altitude case
|
||||
SGGeodesy::inverse(p, nav->geod(), bngToNavaid, az2, s);
|
||||
xpecAzim = SG_RADIANS_TO_DEGREES * (atan((GSTestRoll[i].atAltFt * SG_FEET_TO_METER) / s));
|
||||
}
|
||||
//
|
||||
if (GSTestRoll[i].vFalse) {
|
||||
// rxFlse indicates false signal, use deflections manually entered in Item
|
||||
xpecDefl = GSTestRoll[i].vGSDefl;
|
||||
xpecDefn = GSTestRoll[i].vGSDefn;
|
||||
} else {
|
||||
if (GSTestRoll[i].atAltFt == 0) {
|
||||
// not rxFlse and atAltFt is zero : calculate needle deflections from Rx posn degrees wrt beam
|
||||
xpecDefn = 0 - GSTestRoll[i].plusDeg / halfBeam; // Plane high: needle below
|
||||
} else {
|
||||
// not rxFlse and atAltFt non zero : calculate needle deflections from Rx posn azimuth wrt beam
|
||||
xpecDefn = (GSTestRoll[i].nvAzim - az2) / halfBeam; // Plane high: needle below;
|
||||
}
|
||||
xpecDefn = (xpecDefn > 1) ? 1 : xpecDefn;
|
||||
xpecDefn = (xpecDefn < -1) ? -1 : xpecDefn;
|
||||
xpecDefl = xpecDefn * deflFact;
|
||||
}
|
||||
//
|
||||
// Verify expected:
|
||||
tMesg = itemDesc + "Operable";
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE(tMesg, GSTestRoll[i].vInRnge, node->getBoolValue("operable"));
|
||||
tMesg = itemDesc + "nav-id";
|
||||
string dddbug = node->getStringValue("nav-id");
|
||||
CPPUNIT_ASSERT_MESSAGE(tMesg, node->getStringValue("nav-id") == GSTestRoll[i].nvIden);
|
||||
tMesg = itemDesc + "Sig Norm";
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(tMesg, GSTestRoll[i].vSigNorm, node->getDoubleValue("signal-quality-norm"), GSTestRoll[i].vSigTolr);
|
||||
tMesg = itemDesc + "gs-direct";
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(tMesg, xpecAzim, node->getDoubleValue("gs-direct-deg"), 1);
|
||||
tMesg = itemDesc + "needle defl";
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(tMesg, xpecDefl, node->getDoubleValue("gs-needle-deflection"), GSTestRoll[i].vDeflTolr);
|
||||
tMesg = itemDesc + "defl norm";
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(tMesg, xpecDefn, node->getDoubleValue("gs-needle-deflection-norm"), GSTestRoll[i].vDefnTolr);
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
void NavRadioTests::testGS()
|
||||
{
|
||||
// radio setup
|
||||
SGPropertyNode_ptr configNode(new SGPropertyNode);
|
||||
configNode->setStringValue("name", "navtest");
|
||||
configNode->setIntValue("number", 2);
|
||||
std::unique_ptr<FGNavRadio> r(new FGNavRadio(configNode));
|
||||
r->bind();
|
||||
r->init();
|
||||
|
||||
SGPropertyNode_ptr node = globals->get_props()->getNode("instrumentation/navtest[2]");
|
||||
node->setBoolValue("serviceable", true);
|
||||
globals->get_props()->setDoubleValue("systems/electrical/outputs/nav", 6.0);
|
||||
|
||||
// EDDT 28R
|
||||
FGPositioned::TypeFilter f{FGPositioned::GS};
|
||||
FGNavRecordRef gs = fgpositioned_cast<FGNavRecord>(
|
||||
FGPositioned::findClosestWithIdent("ITLW", SGGeod::fromDeg(13, 52), &f));
|
||||
CPPUNIT_ASSERT(gs->type() == FGPositioned::GS);
|
||||
node->setDoubleValue("frequencies/selected-mhz", 110.10);
|
||||
CPPUNIT_ASSERT(node->getStringValue("frequencies/selected-mhz-fmt") == "110.10");
|
||||
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(gs->glideSlopeAngleDeg(), 3.0, 0.001);
|
||||
double gsAngleRad = gs->glideSlopeAngleDeg() * SG_DEGREES_TO_RADIANS;
|
||||
|
||||
/////////////
|
||||
// derive the GS geometry in cartesian vectors, to match what
|
||||
// navradio.cxx does
|
||||
SGGeod aboveGS = gs->geod();
|
||||
aboveGS.setElevationM(gs->geod().getElevationM() + 100.0);
|
||||
SGVec3d gsVerticalAxis = SGVec3d::fromGeod(aboveGS) - gs->cart();
|
||||
// intentionally different approach to what navradio uses
|
||||
|
||||
gsVerticalAxis *= 0.01; // make it per meter, since we used 100m above
|
||||
|
||||
// dervice the baseline
|
||||
SGQuatd baseLineRot = SGQuatd::fromLonLat(gs->geod()) * SGQuatd::fromHeadAttBankDeg(80.828, 0, 0);
|
||||
SGVec3d gsAltAxis = baseLineRot.backTransform(SGVec3d(1.0, 0.0, 0.0));
|
||||
|
||||
const SGVec3d gsCart = gs->cart();
|
||||
|
||||
//////////////////
|
||||
|
||||
SGVec3d radioPos = gsCart;
|
||||
radioPos += (gsVerticalAxis * tan(gsAngleRad) * 8 * SG_NM_TO_METER);
|
||||
radioPos += (gsAltAxis * 8 * SG_NM_TO_METER);
|
||||
|
||||
setPositionAndStabilise(r.get(), SGGeod::fromCart(radioPos));
|
||||
|
||||
CPPUNIT_ASSERT(node->getStringValue("nav-id") == "ITLW");
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, node->getDoubleValue("signal-quality-norm"), 0.01);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(3.0, node->getDoubleValue("gs-direct-deg"), 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, node->getDoubleValue("gs-needle-deflection"), 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, node->getDoubleValue("gs-needle-deflection-norm"), 0.01);
|
||||
CPPUNIT_ASSERT(node->getBoolValue("gs-in-range"));
|
||||
|
||||
// 0.5 degree offset above
|
||||
gsAngleRad = (gs->glideSlopeAngleDeg() + 0.5) * SG_DEGREES_TO_RADIANS;
|
||||
radioPos = gsCart;
|
||||
radioPos += (gsVerticalAxis * tan(gsAngleRad) * 4 * SG_NM_TO_METER);
|
||||
radioPos += (gsAltAxis * 4 * SG_NM_TO_METER);
|
||||
|
||||
setPositionAndStabilise(r.get(), SGGeod::fromCart(radioPos));
|
||||
|
||||
CPPUNIT_ASSERT(node->getStringValue("nav-id") == "ITLW");
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, node->getDoubleValue("signal-quality-norm"), 0.01);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(3.5, node->getDoubleValue("gs-direct-deg"), 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(-2.5, node->getDoubleValue("gs-needle-deflection"), 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(-0.714, node->getDoubleValue("gs-needle-deflection-norm"), 0.01);
|
||||
CPPUNIT_ASSERT(node->getBoolValue("gs-in-range"));
|
||||
|
||||
// 1 degree below (danger!)
|
||||
gsAngleRad = (gs->glideSlopeAngleDeg() - 1.0) * SG_DEGREES_TO_RADIANS;
|
||||
radioPos = gsCart;
|
||||
radioPos += (gsVerticalAxis * tan(gsAngleRad) * 2 * SG_NM_TO_METER);
|
||||
radioPos += (gsAltAxis * 2 * SG_NM_TO_METER);
|
||||
|
||||
setPositionAndStabilise(r.get(), SGGeod::fromCart(radioPos));
|
||||
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, node->getDoubleValue("signal-quality-norm"), 0.01);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(2.0, node->getDoubleValue("gs-direct-deg"), 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(3.5, node->getDoubleValue("gs-needle-deflection"), 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, node->getDoubleValue("gs-needle-deflection-norm"), 0.01);
|
||||
CPPUNIT_ASSERT(node->getBoolValue("gs-in-range"));
|
||||
|
||||
// false course above, reversed
|
||||
gsAngleRad = (gs->glideSlopeAngleDeg() + 3.0) * SG_DEGREES_TO_RADIANS;
|
||||
radioPos = gsCart;
|
||||
radioPos += (gsVerticalAxis * tan(gsAngleRad) * 5 * SG_NM_TO_METER);
|
||||
radioPos += (gsAltAxis * 5 * SG_NM_TO_METER);
|
||||
|
||||
setPositionAndStabilise(r.get(), SGGeod::fromCart(radioPos));
|
||||
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, node->getDoubleValue("signal-quality-norm"), 0.01);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(6.0, node->getDoubleValue("gs-direct-deg"), 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, node->getDoubleValue("gs-needle-deflection"), 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, node->getDoubleValue("gs-needle-deflection-norm"), 0.01);
|
||||
CPPUNIT_ASSERT(node->getBoolValue("gs-in-range"));
|
||||
|
||||
// false course above, reversed, 0.35 offset below
|
||||
gsAngleRad = (gs->glideSlopeAngleDeg() + 2.65) * SG_DEGREES_TO_RADIANS;
|
||||
radioPos = gsCart;
|
||||
radioPos += (gsVerticalAxis * tan(gsAngleRad) * 3 * SG_NM_TO_METER);
|
||||
radioPos += (gsAltAxis * 3 * SG_NM_TO_METER);
|
||||
|
||||
setPositionAndStabilise(r.get(), SGGeod::fromCart(radioPos));
|
||||
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, node->getDoubleValue("signal-quality-norm"), 0.01);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(5.65, node->getDoubleValue("gs-direct-deg"), 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(-1.75, node->getDoubleValue("gs-needle-deflection"), 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(-0.5, node->getDoubleValue("gs-needle-deflection-norm"), 0.01);
|
||||
CPPUNIT_ASSERT(node->getBoolValue("gs-in-range"));
|
||||
}
|
||||
|
||||
void NavRadioTests::testILSFalseCourse()
|
||||
{
|
||||
|
||||
// also GS false lobes
|
||||
}
|
||||
|
||||
void NavRadioTests::testILSPaired()
|
||||
{
|
||||
// EGPH and countless more
|
||||
}
|
||||
|
||||
void NavRadioTests::testILSAdjacentPaired()
|
||||
{
|
||||
// eg KJFK
|
||||
}
|
||||
|
||||
void NavRadioTests::testGlideslopeLongDistance()
|
||||
{
|
||||
// radio setup
|
||||
SGPropertyNode_ptr configNode(new SGPropertyNode);
|
||||
configNode->setStringValue("name", "navtest");
|
||||
configNode->setIntValue("number", 2);
|
||||
std::unique_ptr<FGNavRadio> r(new FGNavRadio(configNode));
|
||||
r->bind();
|
||||
r->init();
|
||||
|
||||
SGPropertyNode_ptr node = globals->get_props()->getNode("instrumentation/navtest[2]");
|
||||
node->setBoolValue("serviceable", true);
|
||||
globals->get_props()->setDoubleValue("systems/electrical/outputs/nav", 6.0);
|
||||
|
||||
// EGLL 27L
|
||||
FGPositioned::TypeFilter f{FGPositioned::GS};
|
||||
FGNavRecordRef gs = fgpositioned_cast<FGNavRecord>(
|
||||
FGPositioned::findClosestWithIdent("ILL", SGGeod::fromDeg(0, 51), &f));
|
||||
CPPUNIT_ASSERT(gs->type() == FGPositioned::GS);
|
||||
node->setDoubleValue("frequencies/selected-mhz", 109.50);
|
||||
CPPUNIT_ASSERT(node->getStringValue("frequencies/selected-mhz-fmt") == "109.50");
|
||||
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(gs->glideSlopeAngleDeg(), 3.0, 0.001);
|
||||
double gsAngleRad = gs->glideSlopeAngleDeg() * SG_DEGREES_TO_RADIANS;
|
||||
|
||||
// standard approach (per charts)
|
||||
SGGeod p = SGGeodesy::direct(gs->geod(), 90, 7.5 * SG_NM_TO_METER);
|
||||
p.setElevationFt(2500);
|
||||
setPositionAndStabilise(r.get(), p);
|
||||
CPPUNIT_ASSERT_EQUAL(true, node->getBoolValue("gs-in-range"));
|
||||
|
||||
// normal approach
|
||||
p = SGGeodesy::direct(gs->geod(), 90, 9 * SG_NM_TO_METER);
|
||||
p.setElevationFt(3000);
|
||||
setPositionAndStabilise(r.get(), p);
|
||||
CPPUNIT_ASSERT_EQUAL(true, node->getBoolValue("gs-in-range"));
|
||||
|
||||
// in our current nav data, the GS range is defined as 10nm, so the gs-in-range
|
||||
// is false for these
|
||||
|
||||
// 4000 feet intercept
|
||||
p = SGGeodesy::direct(gs->geod(), 90, 12 * SG_NM_TO_METER);
|
||||
p.setElevationFt(4000);
|
||||
setPositionAndStabilise(r.get(), p);
|
||||
CPPUNIT_ASSERT_EQUAL(false, node->getBoolValue("gs-in-range"));
|
||||
CPPUNIT_ASSERT_EQUAL(true, node->getBoolValue("in-range"));
|
||||
|
||||
// further back
|
||||
p = SGGeodesy::direct(gs->geod(), 90, 17.5 * SG_NM_TO_METER);
|
||||
p.setElevationFt(4000);
|
||||
setPositionAndStabilise(r.get(), p);
|
||||
CPPUNIT_ASSERT_EQUAL(false, node->getBoolValue("gs-in-range"));
|
||||
CPPUNIT_ASSERT_EQUAL(true, node->getBoolValue("in-range"));
|
||||
|
||||
// really pushing it
|
||||
p = SGGeodesy::direct(gs->geod(), 90, 25 * SG_NM_TO_METER);
|
||||
p.setElevationFt(4000);
|
||||
setPositionAndStabilise(r.get(), p);
|
||||
CPPUNIT_ASSERT_EQUAL(false, node->getBoolValue("gs-in-range"));
|
||||
CPPUNIT_ASSERT_EQUAL(true, node->getBoolValue("in-range"));
|
||||
}
|
||||
82
test_suite/unit_tests/Instrumentation/test_navRadio.hxx
Normal file
82
test_suite/unit_tests/Instrumentation/test_navRadio.hxx
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Edward d'Auvergne
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _FG_NAVRADIO_UNIT_TESTS_HXX
|
||||
#define _FG_NAVRADIO_UNIT_TESTS_HXX
|
||||
|
||||
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
#include <cppunit/TestFixture.h>
|
||||
|
||||
class FGNavRadio;
|
||||
class SGGeod;
|
||||
|
||||
// The flight plan unit tests.
|
||||
class NavRadioTests : public CppUnit::TestFixture
|
||||
{
|
||||
// Set up the test suite.
|
||||
CPPUNIT_TEST_SUITE(NavRadioTests);
|
||||
CPPUNIT_TEST(testBasic);
|
||||
|
||||
CPPUNIT_TEST(callNavRadioCDI);
|
||||
CPPUNIT_TEST(callNewNavRadioCDI);
|
||||
CPPUNIT_TEST(callNavRadioILS);
|
||||
CPPUNIT_TEST(callNewNavRadioILS);
|
||||
CPPUNIT_TEST(callNavRadioGS);
|
||||
CPPUNIT_TEST(callNewNavRadioGS);
|
||||
|
||||
CPPUNIT_TEST(testGS);
|
||||
CPPUNIT_TEST(testILSFalseCourse);
|
||||
CPPUNIT_TEST(testILSPaired);
|
||||
CPPUNIT_TEST(testILSAdjacentPaired);
|
||||
CPPUNIT_TEST(testGlideslopeLongDistance);
|
||||
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
void setPositionAndStabilise(FGNavRadio* r, const SGGeod& g);
|
||||
|
||||
public:
|
||||
// Set up function for each test.
|
||||
void setUp();
|
||||
|
||||
// Clean up after each test.
|
||||
void tearDown();
|
||||
|
||||
std::string formatFrequency(double f);
|
||||
|
||||
// The tests.
|
||||
void testBasic();
|
||||
|
||||
void callNavRadioCDI();
|
||||
void callNewNavRadioCDI();
|
||||
void callNavRadioILS();
|
||||
void callNewNavRadioILS();
|
||||
void callNavRadioGS();
|
||||
void callNewNavRadioGS();
|
||||
|
||||
void testGS();
|
||||
void testILSFalseCourse();
|
||||
void testILSPaired();
|
||||
void testILSAdjacentPaired();
|
||||
void testGlideslopeLongDistance();
|
||||
};
|
||||
|
||||
|
||||
#endif // _FG_NAVRADIO_UNIT_TESTS_HXX
|
||||
702
test_suite/unit_tests/Instrumentation/test_rnav_procedures.cxx
Normal file
702
test_suite/unit_tests/Instrumentation/test_rnav_procedures.cxx
Normal file
@@ -0,0 +1,702 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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_rnav_procedures.hxx"
|
||||
|
||||
#include <memory>
|
||||
#include <cstring>
|
||||
|
||||
#include "test_suite/FGTestApi/testGlobals.hxx"
|
||||
#include "test_suite/FGTestApi/NavDataCache.hxx"
|
||||
#include "test_suite/FGTestApi/TestPilot.hxx"
|
||||
|
||||
#include <simgear/structure/exception.hxx>
|
||||
|
||||
#include <Airports/airport.hxx>
|
||||
#include <Navaids/NavDataCache.hxx>
|
||||
#include <Navaids/navrecord.hxx>
|
||||
#include <Navaids/navlist.hxx>
|
||||
#include <Navaids/FlightPlan.hxx>
|
||||
|
||||
#include <Instrumentation/gps.hxx>
|
||||
|
||||
#include <Autopilot/route_mgr.hxx>
|
||||
|
||||
using namespace flightgear;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace {
|
||||
class TestFPDelegate : public FlightPlan::Delegate
|
||||
{
|
||||
public:
|
||||
FlightPlanRef thePlan;
|
||||
int sequenceCount = 0;
|
||||
|
||||
virtual ~TestFPDelegate()
|
||||
{
|
||||
}
|
||||
|
||||
void sequence() override
|
||||
{
|
||||
|
||||
++sequenceCount;
|
||||
int newIndex = thePlan->currentIndex() + 1;
|
||||
if (newIndex >= thePlan->numLegs()) {
|
||||
thePlan->finish();
|
||||
return;
|
||||
}
|
||||
|
||||
thePlan->setCurrentIndex(newIndex);
|
||||
}
|
||||
|
||||
void currentWaypointChanged() override
|
||||
{
|
||||
}
|
||||
|
||||
void departureChanged() override
|
||||
{
|
||||
// mimic the default delegate, inserting the SID waypoints
|
||||
|
||||
// clear anything existing
|
||||
thePlan->clearWayptsWithFlag(WPT_DEPARTURE);
|
||||
|
||||
// insert waypt for the dpearture runway
|
||||
auto dr = new RunwayWaypt(thePlan->departureRunway(), thePlan);
|
||||
dr->setFlag(WPT_DEPARTURE);
|
||||
dr->setFlag(WPT_GENERATED);
|
||||
thePlan->insertWayptAtIndex(dr, 0);
|
||||
|
||||
if (thePlan->sid()) {
|
||||
WayptVec sidRoute;
|
||||
bool ok = thePlan->sid()->route(thePlan->departureRunway(), thePlan->sidTransition(), sidRoute);
|
||||
if (!ok)
|
||||
throw sg_exception("failed to route via SID");
|
||||
int insertIndex = 1;
|
||||
for (auto w : sidRoute) {
|
||||
w->setFlag(WPT_DEPARTURE);
|
||||
w->setFlag(WPT_GENERATED);
|
||||
thePlan->insertWayptAtIndex(w, insertIndex++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void arrivalChanged() override
|
||||
{
|
||||
// mimic the default delegate, inserting the STAR waypoints
|
||||
|
||||
// clear anything existing
|
||||
thePlan->clearWayptsWithFlag(WPT_ARRIVAL);
|
||||
|
||||
// insert waypt for the destination runway
|
||||
auto dr = new RunwayWaypt(thePlan->destinationRunway(), thePlan);
|
||||
dr->setFlag(WPT_ARRIVAL);
|
||||
dr->setFlag(WPT_GENERATED);
|
||||
auto leg = thePlan->insertWayptAtIndex(dr, -1);
|
||||
|
||||
if (thePlan->star()) {
|
||||
WayptVec starRoute;
|
||||
bool ok = thePlan->star()->route(thePlan->destinationRunway(), thePlan->starTransition(), starRoute);
|
||||
if (!ok)
|
||||
throw sg_exception("failed to route via STAR");
|
||||
int insertIndex = leg->index();
|
||||
for (auto w : starRoute) {
|
||||
w->setFlag(WPT_ARRIVAL);
|
||||
w->setFlag(WPT_GENERATED);
|
||||
thePlan->insertWayptAtIndex(w, insertIndex++);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // of anonymous namespace
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Set up function for each test.
|
||||
void RNAVProcedureTests::setUp()
|
||||
{
|
||||
FGTestApi::setUp::initTestGlobals("rnav-procedures");
|
||||
FGTestApi::setUp::initNavDataCache();
|
||||
|
||||
globals->get_subsystem_mgr()->bind();
|
||||
globals->get_subsystem_mgr()->init();
|
||||
|
||||
SGPath proceduresPath = SGPath::fromEnv("FG_PROCEDURES_PATH");
|
||||
if (proceduresPath.exists()) {
|
||||
globals->append_fg_scenery(proceduresPath);
|
||||
}
|
||||
|
||||
setupRouteManager();
|
||||
}
|
||||
|
||||
// Clean up after each test.
|
||||
void RNAVProcedureTests::tearDown()
|
||||
{
|
||||
FGTestApi::tearDown::shutdownTestGlobals();
|
||||
}
|
||||
|
||||
GPS* RNAVProcedureTests::setupStandardGPS(SGPropertyNode_ptr config,
|
||||
const std::string name, const int index)
|
||||
{
|
||||
SGPropertyNode_ptr configNode(config.valid() ? config
|
||||
: SGPropertyNode_ptr{new SGPropertyNode});
|
||||
configNode->setStringValue("name", name);
|
||||
configNode->setIntValue("number", index);
|
||||
|
||||
GPS* gps(new GPS(configNode));
|
||||
m_gps = gps;
|
||||
|
||||
m_gpsNode = globals->get_props()->getNode("instrumentation", true)->getChild(name, index, true);
|
||||
m_gpsNode->setBoolValue("serviceable", true);
|
||||
globals->get_props()->setDoubleValue("systems/electrical/outputs/gps", 6.0);
|
||||
|
||||
gps->bind();
|
||||
gps->init();
|
||||
|
||||
globals->add_subsystem("gps", gps, SGSubsystemMgr::POST_FDM);
|
||||
return gps;
|
||||
}
|
||||
|
||||
void RNAVProcedureTests::setupRouteManager()
|
||||
{
|
||||
auto rm = globals->add_new_subsystem<FGRouteMgr>(SGSubsystemMgr::GENERAL);
|
||||
rm->bind();
|
||||
rm->init();
|
||||
rm->postinit();
|
||||
}
|
||||
|
||||
void RNAVProcedureTests::setPositionAndStabilise(const SGGeod& g)
|
||||
{
|
||||
FGTestApi::setPosition(g);
|
||||
for (int i=0; i<60; ++i) {
|
||||
m_gps->update(0.015);
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if 0
|
||||
void RNAVProcedureTests::testBasic()
|
||||
{
|
||||
setupStandardGPS();
|
||||
|
||||
FGPositioned::TypeFilter f{FGPositioned::VOR};
|
||||
auto bodrumVOR = fgpositioned_cast<FGNavRecord>(FGPositioned::findClosestWithIdent("BDR", SGGeod::fromDeg(27.6, 37), &f));
|
||||
SGGeod p1 = SGGeodesy::direct(bodrumVOR->geod(), 45.0, 5.0 * SG_NM_TO_METER);
|
||||
|
||||
FGTestApi::setPositionAndStabilise(p1);
|
||||
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
void RNAVProcedureTests::testHeadingToAlt()
|
||||
{
|
||||
auto vhhh = FGAirport::findByIdent("VHHH");
|
||||
// FGTestApi::setUp::logPositionToKML("heading_to_alt");
|
||||
|
||||
auto rm = globals->get_subsystem<FGRouteMgr>();
|
||||
auto fp = FlightPlan::create();
|
||||
|
||||
auto testDelegate = new TestFPDelegate;
|
||||
testDelegate->thePlan = fp;
|
||||
fp->addDelegate(testDelegate);
|
||||
|
||||
rm->setFlightPlan(fp);
|
||||
|
||||
// we don't have Nasal, but our delegate does the same work
|
||||
FGTestApi::setUp::populateFPWithNasal(fp, "VHHH", "25R", "EGLL", "27R", "HAZEL");
|
||||
|
||||
auto wp = new HeadingToAltitude(fp, "TO_4000", 270);
|
||||
wp->setAltitude(4000, RESTRICT_ABOVE);
|
||||
fp->insertWayptAtIndex(wp, 1); // between the runway WP and HAZEL
|
||||
|
||||
// FGTestApi::writeFlightPlanToKML(fp);
|
||||
|
||||
auto depRwy = fp->departureRunway();
|
||||
|
||||
setupStandardGPS();
|
||||
setPositionAndStabilise(fp->departureRunway()->pointOnCenterline(0.0));
|
||||
|
||||
fp->activate();
|
||||
m_gpsNode->setStringValue("command", "leg");
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(fp->currentIndex(), 0);
|
||||
|
||||
auto pilot = SGSharedPtr<FGTestApi::TestPilot>(new FGTestApi::TestPilot);
|
||||
pilot->resetAtPosition(depRwy->pointOnCenterline(0.0));
|
||||
pilot->setCourseTrue(depRwy->headingDeg());
|
||||
pilot->setSpeedKts(200);
|
||||
pilot->flyGPSCourse(m_gps);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(fp->currentIndex(), 0);
|
||||
|
||||
// check we sequence to the heading-to-alt wp
|
||||
bool ok = FGTestApi::runForTimeWithCheck(300.0, [fp] () {
|
||||
if (fp->currentIndex() == 1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
// leisurely climb out
|
||||
pilot->setVerticalFPM(1800);
|
||||
pilot->setTargetAltitudeFtMSL(8000);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(std::string{"VHHH-25R"}, std::string{m_gpsNode->getStringValue("wp/wp[0]/ID")});
|
||||
CPPUNIT_ASSERT_EQUAL(std::string{"TO_4000"}, std::string{m_gpsNode->getStringValue("wp/wp[1]/ID")});
|
||||
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(270.0, m_gpsNode->getDoubleValue("wp/wp[1]/bearing-true-deg"), 0.5);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(270.0, m_gpsNode->getDoubleValue("wp/leg-true-course-deg"), 0.5);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, m_gpsNode->getDoubleValue("wp/wp[1]/course-error-nm"), 0.05);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, m_gpsNode->getDoubleValue("wp/wp[1]/course-deviation-deg"), 0.5);
|
||||
|
||||
// fly until we're turned to to heading
|
||||
ok = FGTestApi::runForTimeWithCheck(20, [pilot] () {
|
||||
return pilot->isOnHeading(270.);
|
||||
});
|
||||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
// capture the position now
|
||||
SGGeod posAtHdgAltStart = globals->get_aircraft_position();
|
||||
FGTestApi::runForTime(40.0);
|
||||
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(270.0, m_gpsNode->getDoubleValue("wp/wp[1]/bearing-true-deg"), 0.5);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(270.0, m_gpsNode->getDoubleValue("wp/leg-true-course-deg"), 0.5);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, m_gpsNode->getDoubleValue("wp/wp[1]/course-error-nm"), 0.05);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, m_gpsNode->getDoubleValue("wp/wp[1]/course-deviation-deg"), 0.5);
|
||||
|
||||
const double crs = SGGeodesy::courseDeg(posAtHdgAltStart, globals->get_aircraft_position());
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(270.0, crs, 1.0);
|
||||
|
||||
ok = FGTestApi::runForTimeWithCheck(180.0, [fp] () {
|
||||
return (fp->currentIndex() == 2);
|
||||
});
|
||||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(4000.0, globals->get_aircraft_position().getElevationFt(), 100.0);
|
||||
|
||||
FGTestApi::runForTime(40.0);
|
||||
}
|
||||
|
||||
// ugly version: the heading to hold will be very mis-aligned with
|
||||
// the course when the leg is sequenced. (more than 90 degrees turn(
|
||||
void RNAVProcedureTests::testUglyHeadingToAlt()
|
||||
{
|
||||
auto vhhh = FGAirport::findByIdent("VHHH");
|
||||
// FGTestApi::setUp::logPositionToKML("heading_to_alt_ugly");
|
||||
|
||||
auto rm = globals->get_subsystem<FGRouteMgr>();
|
||||
auto fp = FlightPlan::create();
|
||||
|
||||
auto testDelegate = new TestFPDelegate;
|
||||
testDelegate->thePlan = fp;
|
||||
fp->addDelegate(testDelegate);
|
||||
|
||||
rm->setFlightPlan(fp);
|
||||
|
||||
// we don't have Nasal, but our delegate does the same work
|
||||
FGTestApi::setUp::populateFPWithNasal(fp, "VHHH", "07L", "EGLL", "27R", "HAZEL");
|
||||
|
||||
auto wp = new HeadingToAltitude(fp, "TO_4000", 210);
|
||||
wp->setAltitude(4000, RESTRICT_ABOVE);
|
||||
fp->insertWayptAtIndex(wp, 1); // between the runway WP and HAZEL
|
||||
|
||||
// FGTestApi::writeFlightPlanToKML(fp);
|
||||
|
||||
auto depRwy = fp->departureRunway();
|
||||
|
||||
setupStandardGPS();
|
||||
setPositionAndStabilise(fp->departureRunway()->pointOnCenterline(0.0));
|
||||
|
||||
fp->activate();
|
||||
m_gpsNode->setStringValue("command", "leg");
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(fp->currentIndex(), 0);
|
||||
|
||||
auto pilot = SGSharedPtr<FGTestApi::TestPilot>(new FGTestApi::TestPilot);
|
||||
pilot->resetAtPosition(depRwy->pointOnCenterline(0.0));
|
||||
pilot->setCourseTrue(depRwy->headingDeg());
|
||||
pilot->setSpeedKts(200);
|
||||
pilot->flyGPSCourse(m_gps);
|
||||
|
||||
// check we sequence to the heading-to-alt wp
|
||||
bool ok = FGTestApi::runForTimeWithCheck(240.0, [fp] () {
|
||||
if (fp->currentIndex() == 1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
// leisurely climb out
|
||||
pilot->setVerticalFPM(1800);
|
||||
pilot->setTargetAltitudeFtMSL(8000);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(std::string{"VHHH-07L"}, std::string{m_gpsNode->getStringValue("wp/wp[0]/ID")});
|
||||
CPPUNIT_ASSERT_EQUAL(std::string{"TO_4000"}, std::string{m_gpsNode->getStringValue("wp/wp[1]/ID")});
|
||||
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(210.0, m_gpsNode->getDoubleValue("wp/wp[1]/bearing-true-deg"), 0.5);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(210.0, m_gpsNode->getDoubleValue("wp/leg-true-course-deg"), 0.5);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, m_gpsNode->getDoubleValue("wp/wp[1]/course-error-nm"), 0.05);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, m_gpsNode->getDoubleValue("wp/wp[1]/course-deviation-deg"), 0.5);
|
||||
|
||||
// fly until we're turned to to heading
|
||||
ok = FGTestApi::runForTimeWithCheck(120, [pilot] () {
|
||||
return pilot->isOnHeading(210.0);
|
||||
});
|
||||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
// capture the position now
|
||||
SGGeod posAtHdgAltStart = globals->get_aircraft_position();
|
||||
FGTestApi::runForTime(40.0);
|
||||
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(210.0, m_gpsNode->getDoubleValue("wp/wp[1]/bearing-true-deg"), 0.5);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(210.0, m_gpsNode->getDoubleValue("wp/leg-true-course-deg"), 0.5);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, m_gpsNode->getDoubleValue("wp/wp[1]/course-error-nm"), 0.05);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, m_gpsNode->getDoubleValue("wp/wp[1]/course-deviation-deg"), 0.5);
|
||||
|
||||
const double crs = SGGeodesy::courseDeg(posAtHdgAltStart, globals->get_aircraft_position());
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(210.0, crs, 1.0);
|
||||
|
||||
ok = FGTestApi::runForTimeWithCheck(180.0, [fp] () {
|
||||
return (fp->currentIndex() == 2);
|
||||
});
|
||||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(4000.0, globals->get_aircraft_position().getElevationFt(), 100.0);
|
||||
|
||||
FGTestApi::runForTime(40.0);
|
||||
}
|
||||
|
||||
|
||||
void RNAVProcedureTests::testEGPH_TLA6C()
|
||||
{
|
||||
|
||||
auto egph = FGAirport::findByIdent("EGPH");
|
||||
|
||||
auto sid = egph->findSIDWithIdent("TLA6C");
|
||||
// procedures not loaded, abandon test
|
||||
if (!sid)
|
||||
return;
|
||||
|
||||
// FGTestApi::setUp::logPositionToKML("procedure_egph_tla6c");
|
||||
|
||||
auto rm = globals->get_subsystem<FGRouteMgr>();
|
||||
auto fp = FlightPlan::create();
|
||||
|
||||
auto testDelegate = new TestFPDelegate;
|
||||
testDelegate->thePlan = fp;
|
||||
fp->addDelegate(testDelegate);
|
||||
|
||||
rm->setFlightPlan(fp);
|
||||
FGTestApi::setUp::populateFPWithNasal(fp, "EGPH", "24", "EGLL", "27R", "DCS POL DTY");
|
||||
|
||||
fp->setSID(sid);
|
||||
|
||||
FGRunwayRef departureRunway = fp->departureRunway();
|
||||
CPPUNIT_ASSERT_EQUAL(std::string{"24"}, fp->legAtIndex(0)->waypoint()->source()->name());
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(std::string{"UW"}, fp->legAtIndex(1)->waypoint()->ident());
|
||||
|
||||
auto d242Wpt = fp->legAtIndex(2)->waypoint();
|
||||
CPPUNIT_ASSERT_EQUAL(std::string{"D242H"}, d242Wpt->ident());
|
||||
CPPUNIT_ASSERT_EQUAL(true, d242Wpt->flag(WPT_OVERFLIGHT));
|
||||
|
||||
const auto wp3Ident = fp->legAtIndex(3)->waypoint()->ident();
|
||||
|
||||
// depeding which versino fo the procedures we loaded, we can find
|
||||
// one ID or the other
|
||||
CPPUNIT_ASSERT((wp3Ident == "D346T") || (wp3Ident == "D345T"));
|
||||
|
||||
// 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);
|
||||
|
||||
// check what we sequenced to
|
||||
double elapsed = globals->get_sim_time_sec();
|
||||
ok = FGTestApi::runForTimeWithCheck(180.0, [fp] () {
|
||||
if (fp->currentIndex() == 2) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
CPPUNIT_ASSERT(ok);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, m_gpsNode->getDoubleValue("wp/wp[1]/course-error-nm"), 0.05);
|
||||
|
||||
elapsed = globals->get_sim_time_sec();
|
||||
|
||||
ok = FGTestApi::runForTimeWithCheck(180.0, [fp] () {
|
||||
if (fp->currentIndex() == 3) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
CPPUNIT_ASSERT(ok);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, m_gpsNode->getDoubleValue("wp/wp[1]/course-error-nm"), 0.05);
|
||||
|
||||
elapsed = globals->get_sim_time_sec();
|
||||
|
||||
ok = FGTestApi::runForTimeWithCheck(180.0, [fp] () {
|
||||
if (fp->currentIndex() == 4) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
CPPUNIT_ASSERT(ok);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, m_gpsNode->getDoubleValue("wp/wp[1]/course-error-nm"), 0.05);
|
||||
|
||||
ok = FGTestApi::runForTimeWithCheck(180.0, [fp] () {
|
||||
if (fp->currentIndex() == 5) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
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 = FlightPlan::create();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void RNAVProcedureTests::testTransitionsSID()
|
||||
{
|
||||
auto kjfk = FGAirport::findByIdent("kjfk");
|
||||
auto runway = kjfk->getRunwayByIdent("13L");
|
||||
|
||||
|
||||
auto sid = kjfk->selectSIDByTransition(runway, "CANDR");
|
||||
// procedures not loaded, abandon test
|
||||
if (!sid)
|
||||
return;
|
||||
|
||||
auto rm = globals->get_subsystem<FGRouteMgr>();
|
||||
auto fp = FlightPlan::create();
|
||||
|
||||
auto testDelegate = new TestFPDelegate;
|
||||
testDelegate->thePlan = fp;
|
||||
fp->addDelegate(testDelegate);
|
||||
|
||||
rm->setFlightPlan(fp);
|
||||
FGTestApi::setUp::populateFPWithNasal(fp, "KJFK", "13L", "KCLE", "24R", "");
|
||||
|
||||
fp->setSID(sid);
|
||||
CPPUNIT_ASSERT_EQUAL(8, fp->numLegs());
|
||||
auto wp = fp->legAtIndex(6);
|
||||
CPPUNIT_ASSERT_EQUAL(std::string{"CANDR"}, wp->waypoint()->ident());
|
||||
CPPUNIT_ASSERT(rm->activate());
|
||||
}
|
||||
|
||||
void RNAVProcedureTests::testTransitionsSTAR()
|
||||
{
|
||||
auto kjfk = FGAirport::findByIdent("kjfk");
|
||||
auto runway = kjfk->getRunwayByIdent("22L");
|
||||
auto star = kjfk->selectSTARByTransition(runway, "SEY");
|
||||
// procedures not loaded, abandon test
|
||||
if (!star)
|
||||
return;
|
||||
|
||||
auto rm = globals->get_subsystem<FGRouteMgr>();
|
||||
auto fp = FlightPlan::create();
|
||||
|
||||
auto testDelegate = new TestFPDelegate;
|
||||
testDelegate->thePlan = fp;
|
||||
fp->addDelegate(testDelegate);
|
||||
|
||||
rm->setFlightPlan(fp);
|
||||
FGTestApi::setUp::populateFPWithNasal(fp, "KBOS", "22R", "KJFK", "22L", "");
|
||||
|
||||
fp->setSTAR(star);
|
||||
CPPUNIT_ASSERT_EQUAL(9, fp->numLegs());
|
||||
auto wp = fp->legAtIndex(1);
|
||||
CPPUNIT_ASSERT_EQUAL(std::string{"SEY"}, wp->waypoint()->ident());
|
||||
CPPUNIT_ASSERT(rm->activate());
|
||||
}
|
||||
|
||||
void RNAVProcedureTests::testLEBL_LARP2F()
|
||||
{
|
||||
auto lebl = FGAirport::findByIdent("LEBL");
|
||||
auto sid = lebl->findSIDWithIdent("LARP1F.25L");
|
||||
// procedures not loaded, abandon test
|
||||
if (!sid)
|
||||
return;
|
||||
|
||||
FGTestApi::setUp::logPositionToKML("procedure_LEBL-LARP2F");
|
||||
|
||||
auto rm = globals->get_subsystem<FGRouteMgr>();
|
||||
auto fp = FlightPlan::create();
|
||||
|
||||
auto testDelegate = new TestFPDelegate;
|
||||
testDelegate->thePlan = fp;
|
||||
fp->addDelegate(testDelegate);
|
||||
|
||||
rm->setFlightPlan(fp);
|
||||
FGTestApi::setUp::populateFPWithNasal(fp, "LEBL", "25L", "LEIB", "06", "");
|
||||
|
||||
fp->setSID(sid);
|
||||
// I don't know if this should pass or not!
|
||||
// If its a bug, wrap next line in CPPUNIT_ASSERT_ASSERTION_FAIL
|
||||
CPPUNIT_ASSERT_EQUAL(fp->legAtIndex(1)->waypoint()->position(), SGGeod::fromDeg(0, 0));
|
||||
CPPUNIT_ASSERT_EQUAL(std::string{"LARPA"}, fp->legAtIndex(8)->waypoint()->ident());
|
||||
double d = fp->legAtIndex(8)->distanceAlongRoute();
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(46.6, d, 1.0); // ensure the route didn't blow up
|
||||
|
||||
FGTestApi::writeFlightPlanToKML(fp);
|
||||
|
||||
CPPUNIT_ASSERT(rm->activate());
|
||||
|
||||
FGRunwayRef departureRunway = fp->departureRunway();
|
||||
|
||||
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);
|
||||
pilot->setTargetAltitudeFtMSL(8000);
|
||||
pilot->setVerticalFPM(1800);
|
||||
FGTestApi::runForTime(20.0);
|
||||
bool ok = FGTestApi::runForTimeWithCheck(180.0, [fp] () {
|
||||
if (fp->currentIndex() == 1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
FGTestApi::runForTime(180.0);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(199, m_gpsNode->getDoubleValue("wp/leg-true-course-deg"), 0.5);
|
||||
}
|
||||
|
||||
// This could probably be in a better place but this allows it to access TestDelegate.
|
||||
// Also it does relate to procedures as its a bug that only occurs with procedures
|
||||
void RNAVProcedureTests::testIndexOf()
|
||||
{
|
||||
auto egkk = FGAirport::findByIdent("EGKK");
|
||||
auto sid = egkk->findSIDWithIdent("SAM3P");
|
||||
// procedures not loaded, abandon test
|
||||
if (!sid)
|
||||
return;
|
||||
|
||||
auto rm = globals->get_subsystem<FGRouteMgr>();
|
||||
auto fp = FlightPlan::create();
|
||||
|
||||
auto testDelegate = new TestFPDelegate;
|
||||
testDelegate->thePlan = fp;
|
||||
fp->addDelegate(testDelegate);
|
||||
|
||||
rm->setFlightPlan(fp);
|
||||
FGTestApi::setUp::populateFPWithNasal(fp, "EGKK", "08R", "EGJJ", "27", "LELNA");
|
||||
|
||||
fp->setSID(sid);
|
||||
FGPositioned::TypeFilter f{FGPositioned::VOR};
|
||||
auto southamptonVOR = fgpositioned_cast<FGNavRecord>(FGPositioned::findClosestWithIdent("SAM", SGGeod::fromDeg(-1.25, 51.0), &f));
|
||||
auto SAM = fp->legAtIndex(6)->waypoint();
|
||||
CPPUNIT_ASSERT_EQUAL(southamptonVOR->ident(), SAM->ident());
|
||||
CPPUNIT_ASSERT_EQUAL(6, fp->findWayptIndex(southamptonVOR));
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _FG_RNAV_PROCEDURE_UNIT_TESTS_HXX
|
||||
#define _FG_RNAV_PROCEDURE_UNIT_TESTS_HXX
|
||||
|
||||
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
#include <cppunit/TestFixture.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <simgear/props/props.hxx>
|
||||
|
||||
class SGGeod;
|
||||
class GPS;
|
||||
|
||||
// The flight plan unit tests.
|
||||
class RNAVProcedureTests : public CppUnit::TestFixture
|
||||
{
|
||||
// Set up the test suite.
|
||||
CPPUNIT_TEST_SUITE(RNAVProcedureTests);
|
||||
|
||||
CPPUNIT_TEST(testEGPH_TLA6C);
|
||||
CPPUNIT_TEST(testHeadingToAlt);
|
||||
CPPUNIT_TEST(testUglyHeadingToAlt);
|
||||
CPPUNIT_TEST(testLFKC_AJO1R);
|
||||
CPPUNIT_TEST(testTransitionsSID);
|
||||
CPPUNIT_TEST(testTransitionsSTAR);
|
||||
CPPUNIT_TEST(testLEBL_LARP2F);
|
||||
CPPUNIT_TEST(testIndexOf);
|
||||
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
void setPositionAndStabilise(const SGGeod& g);
|
||||
|
||||
GPS* setupStandardGPS(SGPropertyNode_ptr config = {},
|
||||
const std::string name = "gps", const int index = 0);
|
||||
void setupRouteManager();
|
||||
|
||||
public:
|
||||
// Set up function for each test.
|
||||
void setUp();
|
||||
|
||||
// Clean up after each test.
|
||||
void tearDown();
|
||||
|
||||
// The tests.
|
||||
//void testBasic();
|
||||
|
||||
void testEGPH_TLA6C();
|
||||
void testHeadingToAlt();
|
||||
void testUglyHeadingToAlt();
|
||||
void testLFKC_AJO1R();
|
||||
void testTransitionsSID();
|
||||
void testTransitionsSTAR();
|
||||
void testLEBL_LARP2F();
|
||||
void testIndexOf();
|
||||
private:
|
||||
GPS* m_gps = nullptr;
|
||||
SGPropertyNode_ptr m_gpsNode;
|
||||
|
||||
};
|
||||
|
||||
#endif // _FG_RNAV_PROCEDURE_UNIT_TESTS_HXX
|
||||
107
test_suite/unit_tests/Instrumentation/test_transponder.cxx
Normal file
107
test_suite/unit_tests/Instrumentation/test_transponder.cxx
Normal file
@@ -0,0 +1,107 @@
|
||||
#include "test_transponder.hxx"
|
||||
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
|
||||
#include "test_suite/FGTestApi/NavDataCache.hxx"
|
||||
#include "test_suite/FGTestApi/testGlobals.hxx"
|
||||
|
||||
#include <Airports/airport.hxx>
|
||||
#include <Navaids/NavDataCache.hxx>
|
||||
|
||||
#include <Instrumentation/transponder.hxx>
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <Main/locale.hxx>
|
||||
|
||||
// Set up function for each test.
|
||||
void TransponderTests::setUp()
|
||||
{
|
||||
FGTestApi::setUp::initTestGlobals("transponder");
|
||||
FGTestApi::setUp::initNavDataCache();
|
||||
}
|
||||
|
||||
|
||||
// Clean up after each test.
|
||||
void TransponderTests::tearDown()
|
||||
{
|
||||
FGTestApi::tearDown::shutdownTestGlobals();
|
||||
}
|
||||
|
||||
SGSubsystemRef TransponderTests::setupStandardTransponder(const std::string& name, int index)
|
||||
{
|
||||
SGPropertyNode_ptr configNode(new SGPropertyNode);
|
||||
|
||||
globals->get_props()->setDoubleValue("systems/electrical/outputs/transponder", 10.0);
|
||||
|
||||
configNode->setStringValue("name", name);
|
||||
configNode->setIntValue("number", index);
|
||||
auto r = new Transponder(configNode);
|
||||
|
||||
r->bind();
|
||||
r->init();
|
||||
|
||||
globals->add_subsystem("transponder", r, SGSubsystemMgr::FDM);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void TransponderTests::testBasic()
|
||||
{
|
||||
SGPropertyNode* altitudeSource = fgGetNode("/instrumentation/altimeter", true);
|
||||
|
||||
auto t = setupStandardTransponder("transponder", 0);
|
||||
altitudeSource->setDoubleValue("mode-s-alt-ft", 1234.0);
|
||||
|
||||
|
||||
SGPropertyNode* xpdrNode = fgGetNode("/instrumentation/transponder[0]");
|
||||
xpdrNode->setIntValue("inputs/knob-mode", 5); // KNOB_ALT
|
||||
xpdrNode->setIntValue("inputs/mode", 2); // MODE S
|
||||
|
||||
xpdrNode->setIntValue("id-code", 4621);
|
||||
|
||||
t->update(1.0);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(4, xpdrNode->getIntValue("inputs/digit[3]"));
|
||||
CPPUNIT_ASSERT_EQUAL(6, xpdrNode->getIntValue("inputs/digit[2]"));
|
||||
CPPUNIT_ASSERT_EQUAL(2, xpdrNode->getIntValue("inputs/digit[1]"));
|
||||
CPPUNIT_ASSERT_EQUAL(1, xpdrNode->getIntValue("inputs/digit[0]"));
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(true, xpdrNode->getBoolValue("altitude-valid"));
|
||||
CPPUNIT_ASSERT_EQUAL(1234, xpdrNode->getIntValue("altitude"));
|
||||
|
||||
xpdrNode->setIntValue("inputs/digit[2]", 2);
|
||||
CPPUNIT_ASSERT_EQUAL(4221, xpdrNode->getIntValue("id-code"));
|
||||
|
||||
t->update(1.0);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(4221, xpdrNode->getIntValue("transmitted-id"));
|
||||
|
||||
xpdrNode->setBoolValue("inputs/ident-btn", true);
|
||||
CPPUNIT_ASSERT_EQUAL(true, xpdrNode->getBoolValue("ident"));
|
||||
xpdrNode->setBoolValue("inputs/ident-btn", false);
|
||||
|
||||
// remain on for now
|
||||
CPPUNIT_ASSERT_EQUAL(true, xpdrNode->getBoolValue("ident"));
|
||||
|
||||
FGTestApi::runForTime(20.0);
|
||||
CPPUNIT_ASSERT_EQUAL(false, xpdrNode->getBoolValue("ident"));
|
||||
|
||||
xpdrNode->setIntValue("inputs/knob-mode", 1); // KNOB_STANDBY
|
||||
}
|
||||
|
||||
void TransponderTests::testStandby()
|
||||
{
|
||||
SGPropertyNode* altitudeSource = fgGetNode("/instrumentation/altimeter", true);
|
||||
auto t = setupStandardTransponder("transponder", 0);
|
||||
altitudeSource->setDoubleValue("mode-s-alt-ft", 1234.0);
|
||||
|
||||
|
||||
SGPropertyNode* xpdrNode = fgGetNode("/instrumentation/transponder[0]");
|
||||
xpdrNode->setIntValue("inputs/knob-mode", 1); // KNOB_STANDBY
|
||||
xpdrNode->setIntValue("id-code", 4621);
|
||||
|
||||
t->update(1.0);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(4621, xpdrNode->getIntValue("id-code"));
|
||||
CPPUNIT_ASSERT_EQUAL(-9999, xpdrNode->getIntValue("transmitted-id"));
|
||||
}
|
||||
55
test_suite/unit_tests/Instrumentation/test_transponder.hxx
Normal file
55
test_suite/unit_tests/Instrumentation/test_transponder.hxx
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Edward d'Auvergne
|
||||
*
|
||||
* 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 <simgear/structure/subsystem_mgr.hxx>
|
||||
|
||||
class Transponder;
|
||||
class SGGeod;
|
||||
|
||||
// The flight plan unit tests.
|
||||
class TransponderTests : public CppUnit::TestFixture
|
||||
{
|
||||
// Set up the test suite.
|
||||
CPPUNIT_TEST_SUITE(TransponderTests);
|
||||
|
||||
CPPUNIT_TEST(testBasic);
|
||||
CPPUNIT_TEST(testStandby);
|
||||
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
SGSubsystemRef setupStandardTransponder(const std::string& name, int index);
|
||||
|
||||
public:
|
||||
// Set up function for each test.
|
||||
void setUp();
|
||||
|
||||
// Clean up after each test.
|
||||
void tearDown();
|
||||
|
||||
// The tests.
|
||||
void testBasic();
|
||||
void testStandby();
|
||||
};
|
||||
16
test_suite/unit_tests/Main/CMakeLists.txt
Normal file
16
test_suite/unit_tests/Main/CMakeLists.txt
Normal file
@@ -0,0 +1,16 @@
|
||||
set(TESTSUITE_SOURCES
|
||||
${TESTSUITE_SOURCES}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/TestSuite.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_autosaveMigration.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_posinit.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_timeManager.cxx
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
set(TESTSUITE_HEADERS
|
||||
${TESTSUITE_HEADERS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_autosaveMigration.hxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_posinit.hxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_timeManager.hxx
|
||||
PARENT_SCOPE
|
||||
)
|
||||
28
test_suite/unit_tests/Main/TestSuite.cxx
Normal file
28
test_suite/unit_tests/Main/TestSuite.cxx
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Edward d'Auvergne
|
||||
*
|
||||
* 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_autosaveMigration.hxx"
|
||||
#include "test_posinit.hxx"
|
||||
#include "test_timeManager.hxx"
|
||||
|
||||
|
||||
// Set up the unit tests.
|
||||
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(AutosaveMigrationTests, "Unit tests");
|
||||
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(PosInitTests, "Unit tests");
|
||||
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(TimeManagerTests, "Unit tests");
|
||||
169
test_suite/unit_tests/Main/test_autosaveMigration.cxx
Normal file
169
test_suite/unit_tests/Main/test_autosaveMigration.cxx
Normal file
@@ -0,0 +1,169 @@
|
||||
// Written by James Turner, started 2017.
|
||||
//
|
||||
// Copyright (C) 2017 James Turner
|
||||
//
|
||||
// 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, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "test_autosaveMigration.hxx"
|
||||
|
||||
#include "test_suite/FGTestApi/testGlobals.hxx"
|
||||
|
||||
#include <simgear/props/props_io.hxx>
|
||||
#include <simgear/io/iostreams/sgstream.hxx>
|
||||
#include <simgear/misc/sg_dir.hxx>
|
||||
|
||||
#include "Main/globals.hxx"
|
||||
#include "Main/options.hxx"
|
||||
#include "Main/fg_props.hxx"
|
||||
|
||||
using namespace flightgear;
|
||||
|
||||
void writeLegacyAutosave(SGPath userData, int majorVersion, int minorVersion)
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "autosave_" << majorVersion << "_" << minorVersion << ".xml";
|
||||
|
||||
sg_ofstream of(userData / os.str());
|
||||
{
|
||||
of << "<?xml version=\"1.0\"?>" \
|
||||
"<PropertyList>" \
|
||||
"<sim>" \
|
||||
"<window-height>42</window-height>" \
|
||||
"<presets>" \
|
||||
"<foo>12</foo>" \
|
||||
"<child><bar>12</bar></child>" \
|
||||
"</presets>" \
|
||||
"<presets n=\"1\">" \
|
||||
"<foo>13</foo>" \
|
||||
"</presets>" \
|
||||
"<rendering>" \
|
||||
"<msaa>10</msaa>" \
|
||||
"<texture-size>512</texture-size>" \
|
||||
"<texture-pack>" \
|
||||
"<foo>abc</foo>" \
|
||||
"<wibble>abc</wibble>" \
|
||||
"</texture-pack>" \
|
||||
"</rendering>" \
|
||||
"<gui>" \
|
||||
"<dialog n=\"1\">" \
|
||||
"<widget>button</widget>" \
|
||||
"</dialog>" \
|
||||
"<dialog n=\"2\">" \
|
||||
"<widget>slider</widget>" \
|
||||
"</dialog>"\
|
||||
"</gui>" \
|
||||
"</sim>" \
|
||||
"<some-setting>888</some-setting>" \
|
||||
"<views>" \
|
||||
"<view>" \
|
||||
"<new-prop>somevalue</new-prop>" \
|
||||
"<old-prop>somevalue</old-prop>" \
|
||||
"</view>" \
|
||||
"</views>" \
|
||||
"</PropertyList>";
|
||||
|
||||
}
|
||||
of.close();
|
||||
}
|
||||
|
||||
void writeLegacyAutosave2(SGPath userData, int majorVersion, int minorVersion)
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "autosave_" << majorVersion << "_" << minorVersion << ".xml";
|
||||
|
||||
sg_ofstream of(userData / os.str());
|
||||
{
|
||||
of << "<?xml version=\"1.0\"?>" \
|
||||
"<PropertyList>" \
|
||||
"<sim>" \
|
||||
"<bad>1</bad>" \
|
||||
"</sim>" \
|
||||
"</views>" \
|
||||
"</PropertyList>";
|
||||
|
||||
}
|
||||
of.close();
|
||||
}
|
||||
|
||||
// Set up function for each test.
|
||||
void AutosaveMigrationTests::setUp()
|
||||
{
|
||||
FGTestApi::setUp::initTestGlobals("autosaveMigration");
|
||||
|
||||
Options::reset();
|
||||
}
|
||||
|
||||
|
||||
// Clean up after each test.
|
||||
void AutosaveMigrationTests::tearDown()
|
||||
{
|
||||
FGTestApi::tearDown::shutdownTestGlobals();
|
||||
}
|
||||
|
||||
|
||||
void AutosaveMigrationTests::testMigration()
|
||||
{
|
||||
SGPath testUserDataPath = globals->get_fg_home() / "test_autosave_migrate";
|
||||
if (!testUserDataPath.exists()) {
|
||||
SGPath p = testUserDataPath / "foo";
|
||||
p.create_dir(0755);
|
||||
}
|
||||
|
||||
simgear::Dir homeDir(testUserDataPath);
|
||||
for (auto path : homeDir.children(simgear::Dir::TYPE_FILE, ".xml")) {
|
||||
path.remove();
|
||||
}
|
||||
|
||||
writeLegacyAutosave(testUserDataPath, 2016, 1);
|
||||
|
||||
const string_list versionParts = simgear::strutils::split(VERSION, ".");
|
||||
CPPUNIT_ASSERT(versionParts.size() == 3);
|
||||
const int currentMajor = simgear::strutils::to_int(versionParts[0]);
|
||||
const int currentMinor = simgear::strutils::to_int(versionParts[1]);
|
||||
|
||||
// none of these should not be read
|
||||
writeLegacyAutosave2(testUserDataPath, 2016, 0);
|
||||
writeLegacyAutosave2(testUserDataPath, currentMajor, currentMinor + 1);
|
||||
writeLegacyAutosave2(testUserDataPath, currentMajor+1, currentMinor + 1);
|
||||
|
||||
SGPath p = globals->autosaveFilePath(testUserDataPath);
|
||||
if (p.exists()) {
|
||||
CPPUNIT_ASSERT(p.remove());
|
||||
}
|
||||
|
||||
// write some blck-list rules to property tree
|
||||
SGPropertyNode_ptr blacklist = fgGetNode("/sim/autosave-migration/blacklist", true);
|
||||
blacklist->addChild("path")->setStringValue("/sim[0]/presets[0]/*");
|
||||
blacklist->addChild("path")->setStringValue("/sim[0]/rendering[0]/texture-");
|
||||
blacklist->addChild("path")->setStringValue("/views[0]/view[*]/old-prop");
|
||||
blacklist->addChild("path")->setStringValue("/sim[0]/gui");
|
||||
|
||||
// execute method under test
|
||||
globals->loadUserSettings(testUserDataPath);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL((int)globals->get_props()->getNode("sim")->getChildren("presets").size(), 2);
|
||||
CPPUNIT_ASSERT_EQUAL((int)globals->get_props()->getNode("sim")->getChildren("gui").size(), 0);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(globals->get_props()->getIntValue("sim/window-height"), 42);
|
||||
CPPUNIT_ASSERT_EQUAL(globals->get_props()->getIntValue("sim/presets/foo"), 0);
|
||||
CPPUNIT_ASSERT_EQUAL(globals->get_props()->getIntValue("sim/presets[1]/foo"), 13);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(globals->get_props()->getIntValue("some-setting"), 888);
|
||||
|
||||
// if this is not zero, one of the bad autosaves was read
|
||||
CPPUNIT_ASSERT_EQUAL(globals->get_props()->getIntValue("sim/bad"), 0);
|
||||
}
|
||||
48
test_suite/unit_tests/Main/test_autosaveMigration.hxx
Normal file
48
test_suite/unit_tests/Main/test_autosaveMigration.hxx
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Edward d'Auvergne
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _FG_AUTOSAVE_MIGRATION_UNIT_TESTS_HXX
|
||||
#define _FG_AUTOSAVE_MIGRATION_UNIT_TESTS_HXX
|
||||
|
||||
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
#include <cppunit/TestFixture.h>
|
||||
|
||||
|
||||
// The unit tests.
|
||||
class AutosaveMigrationTests : public CppUnit::TestFixture
|
||||
{
|
||||
// Set up the test suite.
|
||||
CPPUNIT_TEST_SUITE(AutosaveMigrationTests);
|
||||
CPPUNIT_TEST(testMigration);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
// Set up function for each test.
|
||||
void setUp();
|
||||
|
||||
// Clean up after each test.
|
||||
void tearDown();
|
||||
|
||||
// The tests.
|
||||
void testMigration();
|
||||
};
|
||||
|
||||
#endif // _FG_AUTOSAVE_MIGRATION_UNIT_TESTS_HXX
|
||||
1029
test_suite/unit_tests/Main/test_posinit.cxx
Normal file
1029
test_suite/unit_tests/Main/test_posinit.cxx
Normal file
File diff suppressed because it is too large
Load Diff
143
test_suite/unit_tests/Main/test_posinit.hxx
Normal file
143
test_suite/unit_tests/Main/test_posinit.hxx
Normal file
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Edward d'Auvergne
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _FG_POSINIT_UNIT_TESTS_HXX
|
||||
#define _FG_POSINIT_UNIT_TESTS_HXX
|
||||
|
||||
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
#include <cppunit/TestFixture.h>
|
||||
#include "Airports/airport.hxx"
|
||||
|
||||
|
||||
// The unit tests of the FGNasalSys subsystem.
|
||||
class PosInitTests : public CppUnit::TestFixture
|
||||
{
|
||||
// Set up the test suite.
|
||||
CPPUNIT_TEST_SUITE(PosInitTests);
|
||||
|
||||
// Airport-based tests
|
||||
CPPUNIT_TEST(testAirportAltitudeOffsetStartup);
|
||||
CPPUNIT_TEST(testAirportAndMetarStartup);
|
||||
CPPUNIT_TEST(testAirportAndRunwayStartup);
|
||||
CPPUNIT_TEST(testAirportAndAvailableParkingStartup);
|
||||
CPPUNIT_TEST(testAirportAndParkingStartup);
|
||||
CPPUNIT_TEST(testAirportOnlyStartup);
|
||||
CPPUNIT_TEST(testAirportRunwayOffsetAltitudeStartup);
|
||||
CPPUNIT_TEST(testAirportRunwayOffsetGlideslopeStartup);
|
||||
CPPUNIT_TEST(testDefaultStartup);
|
||||
CPPUNIT_TEST(testRepositionAtParking);
|
||||
CPPUNIT_TEST(testParkAtOccupied);
|
||||
CPPUNIT_TEST(testParkInvalid);
|
||||
CPPUNIT_TEST(testAirportRunwayRepositionAirport);
|
||||
CPPUNIT_TEST(testParkNoAI);
|
||||
|
||||
|
||||
// Navaid tests
|
||||
CPPUNIT_TEST(testVOROnlyStartup);
|
||||
CPPUNIT_TEST(testVOROffsetAltitudeHeadingStartup);
|
||||
CPPUNIT_TEST(testFixOnlyStartup);
|
||||
CPPUNIT_TEST(testFixOffsetAltitudeHeadingStartup);
|
||||
CPPUNIT_TEST(testNDBOnlyStartup);
|
||||
CPPUNIT_TEST(testNDBOffsetAltitudeHeadingStartup);
|
||||
|
||||
CPPUNIT_TEST(testLatLonStartup);
|
||||
//CPPUNIT_TEST(testLatLonOffsetStartup); This is not yet supported.
|
||||
|
||||
// Carrier tests
|
||||
// We are not able to test the carrier code thoroughly as it depends
|
||||
// heavily on finalizePosition(), which requires that the carrier model
|
||||
// itself be loaded into the scenegraph.
|
||||
CPPUNIT_TEST(testCarrierStartup);
|
||||
|
||||
// Reposition tests
|
||||
CPPUNIT_TEST(testAirportRepositionAirport);
|
||||
CPPUNIT_TEST(testRepositionAtSameParking);
|
||||
CPPUNIT_TEST(testRepositionAtOccupied);
|
||||
CPPUNIT_TEST(testRepositionAtInvalid);
|
||||
|
||||
// MP tests
|
||||
CPPUNIT_TEST(testMPRunwayStart);
|
||||
CPPUNIT_TEST(testMPRunwayStartNoGroundnet);
|
||||
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
void simulateStartReposition();
|
||||
void simulateFinalizePosition();
|
||||
public:
|
||||
// Set up function for each test.
|
||||
void setUp();
|
||||
|
||||
// Clean up after each test.
|
||||
void tearDown();
|
||||
|
||||
// The tests.
|
||||
void testAirportAltitudeOffsetStartup();
|
||||
void testAirportAndMetarStartup();
|
||||
void testAirportAndAvailableParkingStartup();
|
||||
void testAirportAndParkingStartup();
|
||||
void testAirportAndRunwayStartup();
|
||||
void testAirportOnlyStartup();
|
||||
void testAirportRunwayOffsetAltitudeStartup();
|
||||
void testAirportRunwayOffsetGlideslopeStartup();
|
||||
void testDefaultStartup();
|
||||
void testParkAtOccupied();
|
||||
void testParkInvalid();
|
||||
void testParkNoAI();
|
||||
|
||||
// Navaid tests
|
||||
void testVOROnlyStartup();
|
||||
void testVOROffsetAltitudeHeadingStartup();
|
||||
void testFixOnlyStartup();
|
||||
void testFixOffsetAltitudeHeadingStartup();
|
||||
void testNDBOnlyStartup();
|
||||
void testNDBOffsetAltitudeHeadingStartup();
|
||||
|
||||
//Lat Lon tests
|
||||
void testLatLonStartup();
|
||||
void testLatLonOffsetStartup();
|
||||
|
||||
//Carrier tests
|
||||
void testCarrierStartup();
|
||||
|
||||
//Reposition tests
|
||||
void testAirportRepositionAirport();
|
||||
void testRepositionAtParking();
|
||||
void testRepositionAtSameParking();
|
||||
void testRepositionAtOccupied();
|
||||
void testRepositionAtInvalid();
|
||||
void testAirportRunwayRepositionAirport();
|
||||
|
||||
// MP tests
|
||||
void testMPRunwayStart();
|
||||
void testMPRunwayStartNoGroundnet();
|
||||
private:
|
||||
// Helper functions for tests. Return void as they use CPPUNIT_ASSERT
|
||||
void checkAlt(float value);
|
||||
void checkHeading(float value);
|
||||
void checkPosition(SGGeod expectedPos, float delta=1000.0);
|
||||
void checkClosestAirport(std::string icao);
|
||||
void checkStringProp(std::string property, std::string expected);
|
||||
void checkRunway(std::string expected);
|
||||
void checkOnGround();
|
||||
void checkInAir();
|
||||
};
|
||||
|
||||
#endif // _FG_POSINIT_UNIT_TESTS_HXX
|
||||
237
test_suite/unit_tests/Main/test_timeManager.cxx
Normal file
237
test_suite/unit_tests/Main/test_timeManager.cxx
Normal file
@@ -0,0 +1,237 @@
|
||||
// Written by James Turner, started 2021.
|
||||
//
|
||||
// Copyright (C) 2021 James Turner
|
||||
//
|
||||
// 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, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "test_timeManager.hxx"
|
||||
|
||||
#include "test_suite/FGTestApi/NavDataCache.hxx"
|
||||
#include "test_suite/FGTestApi/TestPilot.hxx"
|
||||
#include "test_suite/FGTestApi/testGlobals.hxx"
|
||||
|
||||
#include <simgear/io/iostreams/sgstream.hxx>
|
||||
#include <simgear/misc/sg_dir.hxx>
|
||||
#include <simgear/props/props_io.hxx>
|
||||
#include <simgear/timing/sg_time.hxx>
|
||||
|
||||
#include "Main/fg_props.hxx"
|
||||
#include "Main/globals.hxx"
|
||||
#include <Airports/airport.hxx>
|
||||
#include <Time/TimeManager.hxx>
|
||||
|
||||
using namespace flightgear;
|
||||
|
||||
|
||||
// Set up function for each test.
|
||||
void TimeManagerTests::setUp()
|
||||
{
|
||||
FGTestApi::setUp::initTestGlobals("timeManager");
|
||||
FGTestApi::setUp::initNavDataCache();
|
||||
}
|
||||
|
||||
|
||||
// Clean up after each test.
|
||||
void TimeManagerTests::tearDown()
|
||||
{
|
||||
FGTestApi::tearDown::shutdownTestGlobals();
|
||||
}
|
||||
|
||||
|
||||
void TimeManagerTests::testBasic()
|
||||
{
|
||||
auto timeManager = globals->get_subsystem<TimeManager>();
|
||||
|
||||
// set standard values
|
||||
fgSetBool("/sim/freeze", false);
|
||||
fgSetBool("/sim/sceneryloaded", true);
|
||||
fgSetDouble("/sim/model-hz", 120.0);
|
||||
|
||||
timeManager->bind();
|
||||
timeManager->init();
|
||||
timeManager->postinit();
|
||||
|
||||
double simDt, realDt;
|
||||
// first run: values are zero
|
||||
timeManager->computeTimeDeltas(simDt, realDt);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, simDt, 1.0e-6);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, realDt, 1.0e-6);
|
||||
|
||||
// manually modify the 'last time' to check delta computation
|
||||
timeManager->_lastStamp = SGTimeStamp::now() - SGTimeStamp::fromMSec(25);
|
||||
timeManager->computeTimeDeltas(simDt, realDt);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.025, simDt, 1.0e-3);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.025, realDt, 1.0e-3);
|
||||
|
||||
timeManager->update(simDt);
|
||||
}
|
||||
|
||||
void TimeManagerTests::testFreezeUnfreeze()
|
||||
{
|
||||
auto timeManager = globals->get_subsystem<TimeManager>();
|
||||
|
||||
// set standard values
|
||||
fgSetBool("/sim/freeze/clock", false);
|
||||
fgSetBool("/sim/sceneryloaded", true);
|
||||
fgSetDouble("/sim/model-hz", 120.0);
|
||||
|
||||
timeManager->postinit();
|
||||
|
||||
double simDt, realDt;
|
||||
// first run: values are zero
|
||||
timeManager->computeTimeDeltas(simDt, realDt);
|
||||
|
||||
// test hack: force dtRemainder to zero so we aren't affected by the
|
||||
// system -> stread clock offset in our test assertions. Without this,
|
||||
// depending on exaclty when thetest suite is run, we would get different
|
||||
// results
|
||||
timeManager->_dtRemainder = 0.0;
|
||||
|
||||
SGTimeStamp n;
|
||||
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, simDt, 1.0e-6);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, realDt, 1.0e-6);
|
||||
|
||||
timeManager->_lastStamp = SGTimeStamp::now() - SGTimeStamp::fromMSec(15);
|
||||
timeManager->computeTimeDeltas(simDt, realDt);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.008333, simDt, 1.0e-5);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.008333, realDt, 1.0e-5);
|
||||
|
||||
fgSetBool("/sim/freeze/clock", true);
|
||||
timeManager->_lastStamp = SGTimeStamp::now() - SGTimeStamp::fromMSec(20);
|
||||
timeManager->computeTimeDeltas(simDt, realDt);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, simDt, 1.0e-5); // sim time should not advance
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.025, realDt, 1.0e-5);
|
||||
}
|
||||
|
||||
void TimeManagerTests::testTimeZones()
|
||||
{
|
||||
auto timeManager = globals->get_subsystem<TimeManager>();
|
||||
|
||||
auto vabb = fgFindAirportID("VABB");
|
||||
FGTestApi::setPositionAndStabilise(vabb->geod());
|
||||
|
||||
|
||||
// set standard values
|
||||
fgSetBool("/sim/freeze", false);
|
||||
fgSetBool("/sim/sceneryloaded", true);
|
||||
fgSetDouble("/sim/model-hz", 120.0);
|
||||
|
||||
timeManager->postinit();
|
||||
|
||||
// fake Unix time by setting this; it will then
|
||||
// set the 'current unix time' passed to SGTime
|
||||
const auto testDate = 314611200L;
|
||||
fgSetInt("/sim/time/cur-time-override", testDate);
|
||||
|
||||
timeManager->update(0.0);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL((time_t)19800, globals->get_time_params()->get_local_offset());
|
||||
|
||||
auto gmt = globals->get_time_params()->getGmt();
|
||||
CPPUNIT_ASSERT_EQUAL(79, gmt->tm_year);
|
||||
CPPUNIT_ASSERT_EQUAL(11, gmt->tm_mon);
|
||||
CPPUNIT_ASSERT_EQUAL(21, gmt->tm_mday);
|
||||
|
||||
|
||||
// relocate to somewhere, check the time values update
|
||||
|
||||
auto eddf = FGAirport::getByIdent("EDDF");
|
||||
FGTestApi::setPositionAndStabilise(eddf->geod());
|
||||
timeManager->reposition();
|
||||
timeManager->update(0.0);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL((time_t)3600, globals->get_time_params()->get_local_offset());
|
||||
|
||||
|
||||
auto zbaa = FGAirport::getByIdent("ZBAA");
|
||||
FGTestApi::setPositionAndStabilise(zbaa->geod());
|
||||
timeManager->reposition();
|
||||
timeManager->update(0.0);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL((time_t)28800, globals->get_time_params()->get_local_offset());
|
||||
}
|
||||
|
||||
void TimeManagerTests::testETCTimeZones()
|
||||
{
|
||||
auto timeManager = globals->get_subsystem<TimeManager>();
|
||||
|
||||
auto phto = fgFindAirportID("PHTO");
|
||||
FGTestApi::setPositionAndStabilise(phto->geod());
|
||||
|
||||
timeManager->postinit();
|
||||
|
||||
// fake Unix time by setting this; it will then
|
||||
// set the 'current unix time' passed to SGTime
|
||||
const auto testDate = 314611200L;
|
||||
fgSetInt("/sim/time/cur-time-override", testDate);
|
||||
|
||||
FGTestApi::setPositionAndStabilise(phto->geod());
|
||||
timeManager->reposition();
|
||||
timeManager->update(0.0);
|
||||
|
||||
SGPropertyNode_ptr tzNameNode = fgGetNode("/sim/time/local-timezone", true);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL((time_t)-36000, globals->get_time_params()->get_local_offset());
|
||||
CPPUNIT_ASSERT_EQUAL("Pacific/Honolulu"s, string{tzNameNode->getStringValue()});
|
||||
|
||||
auto pilot = SGSharedPtr<FGTestApi::TestPilot>(new FGTestApi::TestPilot);
|
||||
pilot->setSpeedKts(1000);
|
||||
pilot->setCourseTrue(320.0);
|
||||
|
||||
bool ok = FGTestApi::runForTimeWithCheck(600.0, [tzNameNode]() {
|
||||
const string tz = tzNameNode->getStringValue();
|
||||
return tz == "Etc/GMT+10"s;
|
||||
});
|
||||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
ok = FGTestApi::runForTimeWithCheck(600.0, [tzNameNode]() {
|
||||
const string tz = tzNameNode->getStringValue();
|
||||
return tz == "Pacific/Honolulu"s;
|
||||
});
|
||||
CPPUNIT_ASSERT(ok);
|
||||
}
|
||||
|
||||
void TimeManagerTests::testSpecifyTimeOffset()
|
||||
{
|
||||
// disabled for now since this code depends on epehmeris as well
|
||||
// to define sun position
|
||||
return;
|
||||
|
||||
auto timeManager = globals->get_subsystem<TimeManager>();
|
||||
|
||||
// set standard values
|
||||
fgSetBool("/sim/freeze", false);
|
||||
fgSetBool("/sim/sceneryloaded", true);
|
||||
fgSetDouble("/sim/model-hz", 120.0);
|
||||
|
||||
timeManager->postinit();
|
||||
|
||||
const auto testDate = 314611200L;
|
||||
fgSetInt("/sim/time/cur-time-override", testDate);
|
||||
|
||||
auto uudd = fgFindAirportID("UUDD");
|
||||
FGTestApi::setPositionAndStabilise(uudd->geod());
|
||||
|
||||
timeManager->setTimeOffset("dawn", 0);
|
||||
|
||||
timeManager->update(0.0);
|
||||
|
||||
auto localTime = globals->get_time_params()->get_cur_time();
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL((time_t) 0, localTime);
|
||||
}
|
||||
52
test_suite/unit_tests/Main/test_timeManager.hxx
Normal file
52
test_suite/unit_tests/Main/test_timeManager.hxx
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2021 James Turner <james@flightgear.org>
|
||||
*
|
||||
* 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/extensions/HelperMacros.h>
|
||||
#include <cppunit/TestFixture.h>
|
||||
|
||||
|
||||
// The unit tests.
|
||||
class TimeManagerTests : public CppUnit::TestFixture
|
||||
{
|
||||
// Set up the test suite.
|
||||
CPPUNIT_TEST_SUITE(TimeManagerTests);
|
||||
CPPUNIT_TEST(testBasic);
|
||||
CPPUNIT_TEST(testTimeZones);
|
||||
CPPUNIT_TEST(testFreezeUnfreeze);
|
||||
CPPUNIT_TEST(testSpecifyTimeOffset);
|
||||
CPPUNIT_TEST(testETCTimeZones);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
// Set up function for each test.
|
||||
void setUp();
|
||||
|
||||
// Clean up after each test.
|
||||
void tearDown();
|
||||
|
||||
// The tests.
|
||||
void testBasic();
|
||||
void testTimeZones();
|
||||
void testFreezeUnfreeze();
|
||||
void testSpecifyTimeOffset();
|
||||
void testETCTimeZones();
|
||||
};
|
||||
19
test_suite/unit_tests/Navaids/CMakeLists.txt
Normal file
19
test_suite/unit_tests/Navaids/CMakeLists.txt
Normal file
@@ -0,0 +1,19 @@
|
||||
set(TESTSUITE_SOURCES
|
||||
${TESTSUITE_SOURCES}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/TestSuite.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_flightplan.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_fpNasal.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_navaids2.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_aircraftPerformance.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_routeManager.cxx
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
set(TESTSUITE_HEADERS
|
||||
${TESTSUITE_HEADERS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_flightplan.hxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_fpNasal.hxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_aircraftPerformance.hxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_routeManager.hxx
|
||||
PARENT_SCOPE
|
||||
)
|
||||
31
test_suite/unit_tests/Navaids/TestSuite.cxx
Normal file
31
test_suite/unit_tests/Navaids/TestSuite.cxx
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Edward d'Auvergne
|
||||
*
|
||||
* 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_flightplan.hxx"
|
||||
#include "test_navaids2.hxx"
|
||||
#include "test_aircraftPerformance.hxx"
|
||||
#include "test_routeManager.hxx"
|
||||
#include "test_fpNasal.hxx"
|
||||
|
||||
// Set up the unit tests.
|
||||
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(FlightplanTests, "Unit tests");
|
||||
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(FPNasalTests, "Unit tests");
|
||||
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(NavaidsTests, "Unit tests");
|
||||
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(AircraftPerformanceTests, "Unit tests");
|
||||
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(RouteManagerTests, "Unit tests");
|
||||
73
test_suite/unit_tests/Navaids/test_aircraftPerformance.cxx
Normal file
73
test_suite/unit_tests/Navaids/test_aircraftPerformance.cxx
Normal file
@@ -0,0 +1,73 @@
|
||||
#include "test_aircraftPerformance.hxx"
|
||||
|
||||
#include "test_suite/FGTestApi/testGlobals.hxx"
|
||||
#include "test_suite/FGTestApi/NavDataCache.hxx"
|
||||
|
||||
#include <simgear/misc/strutils.hxx>
|
||||
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <Aircraft/AircraftPerformance.hxx>
|
||||
|
||||
using namespace flightgear;
|
||||
|
||||
// Set up function for each test.
|
||||
void AircraftPerformanceTests::setUp()
|
||||
{
|
||||
FGTestApi::setUp::initTestGlobals("aircraft-perf");
|
||||
FGTestApi::setUp::initNavDataCache();
|
||||
}
|
||||
|
||||
|
||||
// Clean up after each test.
|
||||
void AircraftPerformanceTests::tearDown()
|
||||
{
|
||||
FGTestApi::tearDown::shutdownTestGlobals();
|
||||
}
|
||||
|
||||
void AircraftPerformanceTests::testBasic()
|
||||
{
|
||||
fgSetString("/aircraft/performance/icao-category", "C");
|
||||
AircraftPerformance ap;
|
||||
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(ap.groundSpeedForAltitudeKnots(1000), 152, 1e-3);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(ap.groundSpeedForAltitudeKnots(8000), 224, 1e-3);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(ap.groundSpeedForAltitudeKnots(20000), 491, 1e-3);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(ap.groundSpeedForAltitudeKnots(35000), 461, 1e-3);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(ap.groundSpeedForAltitudeKnots(38000), 458, 1e-3);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(ap.groundSpeedForAltitudeKnots(40000), 458, 1e-3);
|
||||
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(ap.timeBetween(3000, 6000), 100, 1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(ap.distanceNmBetween(3000, 6000), 5.430, 1e-3);
|
||||
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(ap.timeBetween(36000, 34000), 100, 1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(ap.distanceNmBetween(36000, 34000), 12.805, 1e-1);
|
||||
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(ap.timeBetween(15000, 20000), 300, 1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(ap.distanceNmBetween(15000, 18000), 14.270, 1);
|
||||
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(ap.timeBetween(2000, 25000), 1191.6, 1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(ap.distanceNmBetween(2000, 25000), 123.06, 1);
|
||||
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(ap.timeBetween(36000, 3000), 1666.6, 1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(ap.distanceNmBetween(36000, 3000), 162.02, 1);
|
||||
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(251.5, ap.timeToCruise(32.0, 350000), 1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(503.0, ap.timeToCruise(64.0, 380000), 1);
|
||||
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(1553.7, ap.turnRadiusMForAltitude(4000), 1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(3322.4, ap.turnRadiusMForAltitude(16000), 1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(4602.6, ap.turnRadiusMForAltitude(30000), 1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(4475.5, ap.turnRadiusMForAltitude(38000), 1);
|
||||
}
|
||||
|
||||
void AircraftPerformanceTests::testAltitudeGradient()
|
||||
{
|
||||
fgSetString("/aircraft/performance/icao-category", "E");
|
||||
AircraftPerformance ap;
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(8332, ap.computePreviousAltitude(10000, 6000), 1);
|
||||
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(3260, ap.computeNextAltitude(4000, 2000), 1);
|
||||
|
||||
|
||||
}
|
||||
|
||||
49
test_suite/unit_tests/Navaids/test_aircraftPerformance.hxx
Normal file
49
test_suite/unit_tests/Navaids/test_aircraftPerformance.hxx
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _FG_AIRCRAFT_PERFORMANCE_UNIT_TESTS_HXX
|
||||
#define _FG_AIRCRAFT_PERFORMANCE_UNIT_TESTS_HXX
|
||||
|
||||
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
#include <cppunit/TestFixture.h>
|
||||
|
||||
|
||||
class AircraftPerformanceTests : public CppUnit::TestFixture
|
||||
{
|
||||
// Set up the test suite.
|
||||
CPPUNIT_TEST_SUITE(AircraftPerformanceTests);
|
||||
CPPUNIT_TEST(testBasic);
|
||||
CPPUNIT_TEST(testAltitudeGradient);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
// Set up function for each test.
|
||||
void setUp();
|
||||
|
||||
// Clean up after each test.
|
||||
void tearDown();
|
||||
|
||||
// The tests.
|
||||
void testBasic();
|
||||
void testAltitudeGradient();
|
||||
};
|
||||
|
||||
#endif // AircraftPerformanceTests
|
||||
1310
test_suite/unit_tests/Navaids/test_flightplan.cxx
Normal file
1310
test_suite/unit_tests/Navaids/test_flightplan.cxx
Normal file
File diff suppressed because it is too large
Load Diff
105
test_suite/unit_tests/Navaids/test_flightplan.hxx
Normal file
105
test_suite/unit_tests/Navaids/test_flightplan.hxx
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Edward d'Auvergne
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef FG_FLIGHTPLAN_UNIT_TESTS_HXX
|
||||
#define FG_FLIGHTPLAN_UNIT_TESTS_HXX
|
||||
|
||||
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
#include <cppunit/TestFixture.h>
|
||||
|
||||
|
||||
// The flight plan unit tests.
|
||||
class FlightplanTests : public CppUnit::TestFixture
|
||||
{
|
||||
// Set up the test suite.
|
||||
CPPUNIT_TEST_SUITE(FlightplanTests);
|
||||
CPPUNIT_TEST(testBasic);
|
||||
CPPUNIT_TEST(testRoutePathBasic);
|
||||
CPPUNIT_TEST(testRoutePathSkipped);
|
||||
CPPUNIT_TEST(testRoutePathTrivialFlightPlan);
|
||||
CPPUNIT_TEST(testBasicAirways);
|
||||
CPPUNIT_TEST(testAirwayNetworkRoute);
|
||||
CPPUNIT_TEST(testBug1814);
|
||||
CPPUNIT_TEST(testRoutPathWpt0Midflight);
|
||||
CPPUNIT_TEST(testRoutePathVec);
|
||||
CPPUNIT_TEST(testRoutePathFinalLegVQPR15);
|
||||
CPPUNIT_TEST(testLoadSaveMachRestriction);
|
||||
CPPUNIT_TEST(testOnlyDiscontinuityRoute);
|
||||
CPPUNIT_TEST(testBasicDiscontinuity);
|
||||
CPPUNIT_TEST(testLeadingWPDynamic);
|
||||
CPPUNIT_TEST(testRadialIntercept);
|
||||
CPPUNIT_TEST(loadFGFPWithoutDepartureArrival);
|
||||
CPPUNIT_TEST(loadFGFPWithEmbeddedProcedures);
|
||||
CPPUNIT_TEST(loadFGFPWithOldProcedures);
|
||||
CPPUNIT_TEST(loadFGFPWithProcedureIdents);
|
||||
CPPUNIT_TEST(testCloningBasic);
|
||||
CPPUNIT_TEST(testCloningFGFP);
|
||||
CPPUNIT_TEST(testCloningProcedures);
|
||||
CPPUNIT_TEST(testBug2616);
|
||||
CPPUNIT_TEST(testRoute);
|
||||
CPPUNIT_TEST(testViaInsertIntoFP);
|
||||
CPPUNIT_TEST(testViaInsertIntoRoute);
|
||||
CPPUNIT_TEST(loadFGFPAsRoute);
|
||||
|
||||
// CPPUNIT_TEST(testParseICAORoute);
|
||||
// CPPUNIT_TEST(testParseICANLowLevelRoute);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
// Set up function for each test.
|
||||
void setUp();
|
||||
|
||||
// Clean up after each test.
|
||||
void tearDown();
|
||||
|
||||
// The tests.
|
||||
void testBasic();
|
||||
void testRoutePathBasic();
|
||||
void testRoutePathSkipped();
|
||||
void testRoutePathTrivialFlightPlan();
|
||||
void testBasicAirways();
|
||||
void testAirwayNetworkRoute();
|
||||
void testParseICAORoute();
|
||||
void testParseICANLowLevelRoute();
|
||||
void testBug1814();
|
||||
void testRoutPathWpt0Midflight();
|
||||
void testRoutePathVec();
|
||||
void testRoutePathFinalLegVQPR15();
|
||||
void testLoadSaveMachRestriction();
|
||||
void testBasicDiscontinuity();
|
||||
void testOnlyDiscontinuityRoute();
|
||||
void testLeadingWPDynamic();
|
||||
void testRadialIntercept();
|
||||
void loadFGFPWithoutDepartureArrival();
|
||||
void loadFGFPWithEmbeddedProcedures();
|
||||
void loadFGFPWithOldProcedures();
|
||||
void loadFGFPWithProcedureIdents();
|
||||
void testCloningBasic();
|
||||
void testCloningFGFP();
|
||||
void testCloningProcedures();
|
||||
void testBug2616();
|
||||
void testRoute();
|
||||
void testViaInsertIntoFP();
|
||||
void testViaInsertIntoRoute();
|
||||
void loadFGFPAsRoute();
|
||||
};
|
||||
|
||||
#endif // FG_FLIGHTPLAN_UNIT_TESTS_HXX
|
||||
476
test_suite/unit_tests/Navaids/test_fpNasal.cxx
Normal file
476
test_suite/unit_tests/Navaids/test_fpNasal.cxx
Normal file
@@ -0,0 +1,476 @@
|
||||
#include "test_fpNasal.hxx"
|
||||
|
||||
#include "test_suite/FGTestApi/testGlobals.hxx"
|
||||
#include "test_suite/FGTestApi/NavDataCache.hxx"
|
||||
|
||||
#include <simgear/misc/strutils.hxx>
|
||||
|
||||
#include <Navaids/FlightPlan.hxx>
|
||||
#include <Navaids/routePath.hxx>
|
||||
#include <Navaids/NavDataCache.hxx>
|
||||
#include <Navaids/waypoint.hxx>
|
||||
#include <Navaids/navlist.hxx>
|
||||
#include <Navaids/navrecord.hxx>
|
||||
#include <Navaids/airways.hxx>
|
||||
#include <Navaids/fix.hxx>
|
||||
|
||||
#include <Airports/airport.hxx>
|
||||
#include <Autopilot/route_mgr.hxx>
|
||||
|
||||
using namespace flightgear;
|
||||
|
||||
static bool static_haveProcedures = false;
|
||||
|
||||
// Set up function for each test.
|
||||
void FPNasalTests::setUp()
|
||||
{
|
||||
FGTestApi::setUp::initTestGlobals("flightplan");
|
||||
FGTestApi::setUp::initNavDataCache();
|
||||
|
||||
SGPath proceduresPath = SGPath::fromEnv("FG_PROCEDURES_PATH");
|
||||
if (proceduresPath.exists()) {
|
||||
static_haveProcedures = true;
|
||||
globals->append_fg_scenery(proceduresPath);
|
||||
}
|
||||
|
||||
// flightplan() acces needs the route manager
|
||||
globals->add_new_subsystem<FGRouteMgr>(SGSubsystemMgr::GENERAL);
|
||||
|
||||
globals->get_subsystem_mgr()->bind();
|
||||
globals->get_subsystem_mgr()->init();
|
||||
|
||||
FGTestApi::setUp::initStandardNasal();
|
||||
globals->get_subsystem_mgr()->postinit();
|
||||
}
|
||||
|
||||
|
||||
// Clean up after each test.
|
||||
void FPNasalTests::tearDown()
|
||||
{
|
||||
FGTestApi::tearDown::shutdownTestGlobals();
|
||||
}
|
||||
|
||||
static FlightPlanRef makeTestFP(const std::string& depICAO, const std::string& depRunway,
|
||||
const std::string& destICAO, const std::string& destRunway,
|
||||
const std::string& waypoints)
|
||||
{
|
||||
FlightPlanRef f = FlightPlan::create();
|
||||
FGTestApi::setUp::populateFPWithNasal(f, depICAO, depRunway, destICAO, destRunway, waypoints);
|
||||
return f;
|
||||
}
|
||||
|
||||
void FPNasalTests::testBasic()
|
||||
{
|
||||
|
||||
FlightPlanRef fp1 = makeTestFP("EGCC", "23L", "EHAM", "24",
|
||||
"TNT CLN");
|
||||
fp1->setIdent("testplan");
|
||||
|
||||
// setup the FP on the route-manager, so flightplan() call works
|
||||
auto rm = globals->get_subsystem<FGRouteMgr>();
|
||||
rm->setFlightPlan(fp1);
|
||||
rm->activate();
|
||||
|
||||
// modify leg data dfrom Nasal
|
||||
bool ok = FGTestApi::executeNasal(R"(
|
||||
var fp = flightplan(); # retrieve the global flightplan
|
||||
var leg = fp.getWP(3);
|
||||
leg.setAltitude(6000, 'AT');
|
||||
)");
|
||||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
// check the value updated in the leg
|
||||
CPPUNIT_ASSERT_EQUAL(RESTRICT_AT, fp1->legAtIndex(3)->altitudeRestriction());
|
||||
CPPUNIT_ASSERT_EQUAL(6000, fp1->legAtIndex(3)->altitudeFt());
|
||||
|
||||
// insert some waypoints from Nasal
|
||||
|
||||
ok = FGTestApi::executeNasal(R"(
|
||||
var fp = flightplan();
|
||||
var leg = fp.getWP(2);
|
||||
var newWP = createWPFrom(navinfo(leg.lat, leg.lon, 'COA')[0]);
|
||||
fp.insertWPAfter(newWP, 2);
|
||||
)");
|
||||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(string{"COSTA VOR-DME"}, fp1->legAtIndex(3)->waypoint()->source()->name());
|
||||
|
||||
ok = FGTestApi::executeNasal(R"(
|
||||
var fp = flightplan();
|
||||
fp.clearAll();
|
||||
unitTest.assert_equal(fp.getPlanSize(), 0);
|
||||
unitTest.assert_equal(fp.current, -1);
|
||||
unitTest.assert_equal(fp.departure, nil);
|
||||
unitTest.assert_equal(fp.sid, nil);
|
||||
unitTest.assert_equal(fp.cruiseSpeedKt, 0);
|
||||
)");
|
||||
CPPUNIT_ASSERT(ok);
|
||||
}
|
||||
|
||||
void FPNasalTests::testRestrictions()
|
||||
{
|
||||
FlightPlanRef fp1 = makeTestFP("EGCC", "23L", "EHAM", "24",
|
||||
"TNT CLN");
|
||||
fp1->setIdent("testplan");
|
||||
|
||||
// setup the FP on the route-manager, so flightplan() call works
|
||||
auto rm = globals->get_subsystem<FGRouteMgr>();
|
||||
rm->setFlightPlan(fp1);
|
||||
rm->activate();
|
||||
|
||||
// modify leg data dfrom Nasal
|
||||
bool ok = FGTestApi::executeNasal(R"(
|
||||
var fp = flightplan(); # retrieve the global flightplan
|
||||
var leg = fp.getWP(3);
|
||||
leg.setAltitude(6000, 'AT');
|
||||
)");
|
||||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
// check the value updated in the leg
|
||||
CPPUNIT_ASSERT_EQUAL(RESTRICT_AT, fp1->legAtIndex(3)->altitudeRestriction());
|
||||
CPPUNIT_ASSERT_EQUAL(6000, fp1->legAtIndex(3)->altitudeFt());
|
||||
|
||||
ok = FGTestApi::executeNasal(R"(
|
||||
var fp = flightplan(); # retrieve the global flightplan
|
||||
var leg = fp.getWP(3);
|
||||
leg.setAltitude(6000, 'above');
|
||||
)");
|
||||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(RESTRICT_ABOVE, fp1->legAtIndex(3)->altitudeRestriction());
|
||||
CPPUNIT_ASSERT_EQUAL(6000, fp1->legAtIndex(3)->altitudeFt());
|
||||
|
||||
ok = FGTestApi::executeNasal(R"(
|
||||
var fp = flightplan(); # retrieve the global flightplan
|
||||
var leg = fp.getWP(3);
|
||||
leg.setAltitude(6000, 'below');
|
||||
)");
|
||||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(RESTRICT_BELOW, fp1->legAtIndex(3)->altitudeRestriction());
|
||||
CPPUNIT_ASSERT_EQUAL(6000, fp1->legAtIndex(3)->altitudeFt());
|
||||
|
||||
ok = FGTestApi::executeNasal(R"(
|
||||
var fp = flightplan(); # retrieve the global flightplan
|
||||
var leg = fp.getWP(3);
|
||||
leg.setAltitude(6000, 'delete');
|
||||
)");
|
||||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(RESTRICT_DELETE, fp1->legAtIndex(3)->altitudeRestriction());
|
||||
|
||||
ok = FGTestApi::executeNasal(R"(
|
||||
var fp = flightplan(); # retrieve the global flightplan
|
||||
var leg = fp.getWP(3);
|
||||
leg.setSpeed(250, 'at');
|
||||
)");
|
||||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(RESTRICT_AT, fp1->legAtIndex(3)->speedRestriction());
|
||||
CPPUNIT_ASSERT_EQUAL(250, fp1->legAtIndex(3)->speedKts());
|
||||
|
||||
ok = FGTestApi::executeNasal(R"(
|
||||
var fp = flightplan(); # retrieve the global flightplan
|
||||
var leg = fp.getWP(3);
|
||||
leg.setSpeed(250, 'above');
|
||||
)");
|
||||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(RESTRICT_ABOVE, fp1->legAtIndex(3)->speedRestriction());
|
||||
CPPUNIT_ASSERT_EQUAL(250, fp1->legAtIndex(3)->speedKts());
|
||||
|
||||
ok = FGTestApi::executeNasal(R"(
|
||||
var fp = flightplan(); # retrieve the global flightplan
|
||||
var leg = fp.getWP(3);
|
||||
leg.setSpeed(250, 'below');
|
||||
)");
|
||||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(RESTRICT_BELOW, fp1->legAtIndex(3)->speedRestriction());
|
||||
CPPUNIT_ASSERT_EQUAL(250, fp1->legAtIndex(3)->speedKts());
|
||||
|
||||
ok = FGTestApi::executeNasal(R"(
|
||||
var fp = flightplan(); # retrieve the global flightplan
|
||||
var leg = fp.getWP(3);
|
||||
leg.setSpeed(250, 'delete');
|
||||
)");
|
||||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(RESTRICT_DELETE, fp1->legAtIndex(3)->speedRestriction());
|
||||
}
|
||||
|
||||
void FPNasalTests::testSegfaultWaypointGhost()
|
||||
{
|
||||
// checking for a segfault here, no segfault indicates success. A runtime error in the log is acceptable here.
|
||||
bool ok = FGTestApi::executeNasal(R"(
|
||||
var fp = createFlightplan();
|
||||
fp.departure = airportinfo("BIKF");
|
||||
fp.destination = airportinfo("EGLL");
|
||||
var wp = fp.getWP(1);
|
||||
fp.deleteWP(1);
|
||||
print(wp.wp_name);
|
||||
)");
|
||||
CPPUNIT_ASSERT(ok);
|
||||
}
|
||||
|
||||
void FPNasalTests::testSIDTransitionAPI()
|
||||
{
|
||||
if (!static_haveProcedures) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto rm = globals->get_subsystem<FGRouteMgr>();
|
||||
|
||||
bool ok = FGTestApi::executeNasal(R"(
|
||||
var fp = flightplan();
|
||||
fp.departure = airportinfo("KJFK");
|
||||
fp.destination = airportinfo("EGLL");
|
||||
|
||||
var sid = fp.departure.getSid("DEEZZ5.13L");
|
||||
|
||||
unitTest.assert(sid != nil, "SID not found");
|
||||
unitTest.assert_equal(sid.id, "DEEZZ5.13L", "Incorrect SID loaded");
|
||||
|
||||
var trans = sid.transition('CANDR');
|
||||
unitTest.assert_equal(trans.id, "CANDR", "Couldn't find transition");
|
||||
unitTest.assert_equal(trans.tp_type, "transition", "Procedure type incorrect");
|
||||
|
||||
fp.sid = trans;
|
||||
fp.departure_runway = fp.departure.runway('13L')
|
||||
)");
|
||||
|
||||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
auto fp = rm->flightPlan();
|
||||
|
||||
CPPUNIT_ASSERT(fp->departureRunway());
|
||||
CPPUNIT_ASSERT(fp->sid());
|
||||
CPPUNIT_ASSERT(fp->sidTransition());
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(fp->departureRunway()->ident(), string{"13L"});
|
||||
CPPUNIT_ASSERT_EQUAL(fp->sid()->ident(), string{"DEEZZ5.13L"});
|
||||
CPPUNIT_ASSERT_EQUAL(fp->sidTransition()->ident(), string{"CANDR"});
|
||||
|
||||
// test specify SID via transition in Nasal
|
||||
rm->setFlightPlan(FlightPlan::create());
|
||||
|
||||
ok = FGTestApi::executeNasal(R"(
|
||||
var fp = flightplan();
|
||||
fp.departure = airportinfo("KJFK");
|
||||
fp.destination = airportinfo("EGLL");
|
||||
fp.departure_runway = airportinfo("KJFK").runways["13L"];
|
||||
fp.sid = airportinfo("KJFK").getSid("DEEZZ5.13L");
|
||||
fp.sid_trans = "CANDR";
|
||||
|
||||
)");
|
||||
|
||||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
fp = rm->flightPlan();
|
||||
|
||||
CPPUNIT_ASSERT(fp->departureRunway());
|
||||
CPPUNIT_ASSERT(fp->sid());
|
||||
CPPUNIT_ASSERT(fp->sidTransition());
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(fp->departureRunway()->ident(), string{"13L"});
|
||||
CPPUNIT_ASSERT_EQUAL(fp->sid()->ident(), string{"DEEZZ5.13L"});
|
||||
CPPUNIT_ASSERT_EQUAL(fp->sidTransition()->ident(), string{"CANDR"});
|
||||
|
||||
|
||||
}
|
||||
|
||||
void FPNasalTests::testSTARTransitionAPI()
|
||||
{
|
||||
if (!static_haveProcedures) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto rm = globals->get_subsystem<FGRouteMgr>();
|
||||
|
||||
bool ok = FGTestApi::executeNasal(R"(
|
||||
var fp = flightplan();
|
||||
fp.departure = airportinfo("EGLL");
|
||||
fp.destination = airportinfo("EDDM");
|
||||
|
||||
var star = fp.destination.getStar("RIXE3A.26L");
|
||||
|
||||
unitTest.assert(star != nil, "STAR not found");
|
||||
unitTest.assert_equal(star.id, "RIXE3A.26L", "Incorrect STAR loaded");
|
||||
|
||||
|
||||
fp.star = star;
|
||||
fp.destination_runway = fp.destination.runway('26L')
|
||||
)");
|
||||
|
||||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
auto fp = rm->flightPlan();
|
||||
|
||||
CPPUNIT_ASSERT(fp->star());
|
||||
CPPUNIT_ASSERT(fp->starTransition() == nullptr);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(fp->star()->ident(), string{"RIXE3A.26L"});
|
||||
}
|
||||
|
||||
void FPNasalTests::testApproachTransitionAPI()
|
||||
{
|
||||
if (!static_haveProcedures) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto rm = globals->get_subsystem<FGRouteMgr>();
|
||||
|
||||
bool ok = FGTestApi::executeNasal(R"(
|
||||
var fp = flightplan();
|
||||
fp.departure = airportinfo("EGLL");
|
||||
fp.destination = airportinfo("EDDM");
|
||||
|
||||
var star = fp.destination.getStar("RIXE3A.08L");
|
||||
|
||||
unitTest.assert(star != nil, "STAR not found");
|
||||
unitTest.assert_equal(star.id, "RIXE3A.08L", "Incorrect STAR loaded");
|
||||
|
||||
fp.star = star;
|
||||
fp.destination_runway = fp.destination.runway('08L');
|
||||
|
||||
var approach = fp.destination.getApproach("ILS08L");
|
||||
unitTest.assert(approach != nil, "No approach loaded");
|
||||
|
||||
var trans = approach.transition('LUL1C');
|
||||
|
||||
unitTest.assert(trans != nil, "approach transition not found");
|
||||
unitTest.assert_equal(trans.id, "LUL1C", "Incorrect approach transition loaded");
|
||||
unitTest.assert_equal(trans.tp_type, "transition", "Procedure type incorrect");
|
||||
|
||||
fp.approach = trans;
|
||||
|
||||
unitTest.assert_equal(fp.approach.id, "ILS08L", "Incorrect approach returned");
|
||||
unitTest.assert_equal(fp.approach_trans.id, "LUL1C", "Incorrect transition returned");
|
||||
unitTest.assert_equal(fp.approach_trans.tp_type, "transition", "Procedure type incorrect");
|
||||
)");
|
||||
|
||||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
auto fp = rm->flightPlan();
|
||||
|
||||
CPPUNIT_ASSERT(fp->approach());
|
||||
CPPUNIT_ASSERT_EQUAL(string{"LUL1C"}, fp->approachTransition()->ident());
|
||||
CPPUNIT_ASSERT_EQUAL(string{"ILS08L"}, fp->approach()->ident());
|
||||
}
|
||||
|
||||
void FPNasalTests::testApproachTransitionAPIWithCloning()
|
||||
{
|
||||
if (!static_haveProcedures) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto rm = globals->get_subsystem<FGRouteMgr>();
|
||||
|
||||
bool ok = FGTestApi::executeNasal(R"(
|
||||
var fp = flightplan();
|
||||
fp.departure = airportinfo("EGLL");
|
||||
fp.destination = airportinfo("EHAM");
|
||||
fp.star = fp.destination.getStar("REDF1A");
|
||||
fp.destination_runway = fp.destination.runway('06');
|
||||
|
||||
var approach = fp.destination.getApproach("ILS06");
|
||||
unitTest.assert(approach != nil, "No approach loaded");
|
||||
|
||||
var trans = approach.transition('SUG2A');
|
||||
unitTest.assert(trans != nil, "approach transition not found");
|
||||
|
||||
fp.approach = trans;
|
||||
|
||||
unitTest.assert_equal(fp.approach.id, "ILS06", "Incorrect approach returned");
|
||||
unitTest.assert_equal(fp.approach_trans.id, "SUG2A", "Incorrect transition returned");
|
||||
unitTest.assert_equal(fp.approach_trans.tp_type, "transition", "Procedure type incorrect");
|
||||
)");
|
||||
|
||||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
auto fp = rm->flightPlan();
|
||||
|
||||
CPPUNIT_ASSERT(fp->approach());
|
||||
CPPUNIT_ASSERT_EQUAL(string{"SUG2A"}, fp->approachTransition()->ident());
|
||||
CPPUNIT_ASSERT_EQUAL(string{"ILS06"}, fp->approach()->ident());
|
||||
|
||||
auto fp2 = fp->clone("testplan2");
|
||||
CPPUNIT_ASSERT_EQUAL(string{"ILS06"}, fp2->approach()->ident());
|
||||
CPPUNIT_ASSERT_EQUAL(string{"SUG2A"}, fp2->approachTransition()->ident());
|
||||
}
|
||||
|
||||
void FPNasalTests::testAirwaysAPI()
|
||||
{
|
||||
bool ok = FGTestApi::executeNasal(R"(
|
||||
var airwayIdent = "L620";
|
||||
var airwayStore = airway(airwayIdent, "low");
|
||||
unitTest.assert(airwayStore != nil, "Airway " ~ airwayIdent ~ " not found");
|
||||
unitTest.assert(airwayStore.id == airwayIdent, "Incorrect airway found");
|
||||
unitTest.assert_equal(airwayStore.level, 'low', "Incorrect airway found");
|
||||
unitTest.assert_equal(airwayStore.level_code, Airway.LOW, "Incorrect airway found");
|
||||
|
||||
airwayIdent = "UL620";
|
||||
var cln = findNavaidsByID("CLN", "VOR")[0];
|
||||
airwayStore = airway(airwayIdent, cln);
|
||||
unitTest.assert(airwayStore != nil, "Airway " ~ airwayIdent ~ " not found");
|
||||
unitTest.assert(airwayStore.id == airwayIdent, "Incorrect airway found");
|
||||
unitTest.assert_equal(airwayStore.level_code, Airway.HIGH, "Incorrect airway found");
|
||||
|
||||
airwayIdent = "J547";
|
||||
airwayStore = airway(airwayIdent);
|
||||
unitTest.assert(airwayStore != nil, "Airway " ~ airwayIdent ~ " not found");
|
||||
unitTest.assert(airwayStore.id == airwayIdent, "Incorrect airway found");
|
||||
)");
|
||||
|
||||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
ok = FGTestApi::executeNasal(R"(
|
||||
|
||||
var airwayIdent = "L620";
|
||||
var airwayStore = airway(airwayIdent, Airway.LOW);
|
||||
var cln = findNavaidsByID("CLN", "VOR")[0];
|
||||
|
||||
var v1 = createViaTo(airwayIdent, "CLN");
|
||||
unitTest.assert_equal(v1.wp_type, "via");
|
||||
unitTest.assert_equal(v1.airway.id, 'L620');
|
||||
unitTest.assert_equal(v1.airway.level_code, Airway.LOW);
|
||||
|
||||
var v2 = createViaTo(airwayStore, "TULIP");
|
||||
unitTest.assert_equal(v2.wp_type, "via");
|
||||
unitTest.assert_equal(v2.airway.id, airwayIdent);
|
||||
|
||||
var v3 = createViaFromTo(cln, "L620", 'low', "TULIP");
|
||||
unitTest.assert_equal(v3.airway.id, 'L620');
|
||||
|
||||
var v4 = createViaFromTo(cln, "L620", "REDFA");
|
||||
unitTest.assert_equal(v4.airway.level_code, Airway.LOW);
|
||||
|
||||
# test direct API (no Vias)
|
||||
var wps = airwayStore.viaWaypoints(cln, "TULIP");
|
||||
unitTest.assert_equal(size(wps), 3);
|
||||
|
||||
unitTest.assert_equal(wps[1].wp_ident, 'REDFA');
|
||||
unitTest.assert_equal(wps[1].airway.ident, 'L620');
|
||||
|
||||
)");
|
||||
|
||||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
}
|
||||
|
||||
void FPNasalTests::testTotalDistanceAPI()
|
||||
{
|
||||
auto rm = globals->get_subsystem<FGRouteMgr>();
|
||||
|
||||
bool ok = FGTestApi::executeNasal(R"(
|
||||
var fp = flightplan();
|
||||
fp.departure = airportinfo("BIKF");
|
||||
fp.destination = airportinfo("EGLL");
|
||||
unitTest.assert_doubles_equal(1025.9, fp.totalDistanceNm, 0.1, "Distance assertion failed");
|
||||
)");
|
||||
|
||||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
auto fp = rm->flightPlan();
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(fp->totalDistanceNm(), 1025.9, 0.1);
|
||||
}
|
||||
61
test_suite/unit_tests/Navaids/test_fpNasal.hxx
Normal file
61
test_suite/unit_tests/Navaids/test_fpNasal.hxx
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Edward d'Auvergne
|
||||
*
|
||||
* 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/extensions/HelperMacros.h>
|
||||
#include <cppunit/TestFixture.h>
|
||||
|
||||
|
||||
// The flight plan unit tests.
|
||||
class FPNasalTests : public CppUnit::TestFixture
|
||||
{
|
||||
// Set up the test suite.
|
||||
CPPUNIT_TEST_SUITE(FPNasalTests);
|
||||
CPPUNIT_TEST(testBasic);
|
||||
CPPUNIT_TEST(testRestrictions);
|
||||
CPPUNIT_TEST(testSegfaultWaypointGhost);
|
||||
CPPUNIT_TEST(testSIDTransitionAPI);
|
||||
CPPUNIT_TEST(testSTARTransitionAPI);
|
||||
CPPUNIT_TEST(testApproachTransitionAPI);
|
||||
CPPUNIT_TEST(testApproachTransitionAPIWithCloning);
|
||||
CPPUNIT_TEST(testAirwaysAPI);
|
||||
CPPUNIT_TEST(testTotalDistanceAPI);
|
||||
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
// Set up function for each test.
|
||||
void setUp();
|
||||
|
||||
// Clean up after each test.
|
||||
void tearDown();
|
||||
|
||||
// The tests.
|
||||
void testBasic();
|
||||
void testRestrictions();
|
||||
void testSegfaultWaypointGhost();
|
||||
void testSIDTransitionAPI();
|
||||
void testSTARTransitionAPI();
|
||||
void testApproachTransitionAPI();
|
||||
void testApproachTransitionAPIWithCloning();
|
||||
void testAirwaysAPI();
|
||||
void testTotalDistanceAPI();
|
||||
};
|
||||
36
test_suite/unit_tests/Navaids/test_navaids2.cxx
Normal file
36
test_suite/unit_tests/Navaids/test_navaids2.cxx
Normal file
@@ -0,0 +1,36 @@
|
||||
#include "test_navaids2.hxx"
|
||||
|
||||
#include "test_suite/FGTestApi/testGlobals.hxx"
|
||||
#include "test_suite/FGTestApi/NavDataCache.hxx"
|
||||
|
||||
#include <Navaids/NavDataCache.hxx>
|
||||
#include <Navaids/navrecord.hxx>
|
||||
#include <Navaids/navlist.hxx>
|
||||
|
||||
|
||||
// Set up function for each test.
|
||||
void NavaidsTests::setUp()
|
||||
{
|
||||
FGTestApi::setUp::initTestGlobals("navaids2");
|
||||
FGTestApi::setUp::initNavDataCache();
|
||||
}
|
||||
|
||||
|
||||
// Clean up after each test.
|
||||
void NavaidsTests::tearDown()
|
||||
{
|
||||
FGTestApi::tearDown::shutdownTestGlobals();
|
||||
}
|
||||
|
||||
|
||||
void NavaidsTests::testBasic()
|
||||
{
|
||||
SGGeod egccPos = SGGeod::fromDeg(-2.27, 53.35);
|
||||
FGNavRecordRef tla = FGNavList::findByFreq(115.7, egccPos);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(strcmp(tla->get_ident(), "TNT"), 0);
|
||||
CPPUNIT_ASSERT(tla->ident() == "TNT");
|
||||
CPPUNIT_ASSERT(tla->name() == "TRENT VOR-DME");
|
||||
CPPUNIT_ASSERT_EQUAL(tla->get_freq(), 11570);
|
||||
CPPUNIT_ASSERT_EQUAL(tla->get_range(), 130);
|
||||
}
|
||||
48
test_suite/unit_tests/Navaids/test_navaids2.hxx
Normal file
48
test_suite/unit_tests/Navaids/test_navaids2.hxx
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Edward d'Auvergne
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _FG_NAVAIDS_UNIT_TESTS_HXX
|
||||
#define _FG_NAVAIDS_UNIT_TESTS_HXX
|
||||
|
||||
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
#include <cppunit/TestFixture.h>
|
||||
|
||||
|
||||
// The flight plan unit tests.
|
||||
class NavaidsTests : public CppUnit::TestFixture
|
||||
{
|
||||
// Set up the test suite.
|
||||
CPPUNIT_TEST_SUITE(NavaidsTests);
|
||||
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();
|
||||
};
|
||||
|
||||
#endif // _FG_NAVAIDS_UNIT_TESTS_HXX
|
||||
989
test_suite/unit_tests/Navaids/test_routeManager.cxx
Normal file
989
test_suite/unit_tests/Navaids/test_routeManager.cxx
Normal file
@@ -0,0 +1,989 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "test_routeManager.hxx"
|
||||
|
||||
#include <memory>
|
||||
#include <cstring>
|
||||
|
||||
#include <simgear/io/iostreams/sgstream.hxx>
|
||||
#include <simgear/misc/sg_dir.hxx>
|
||||
#include <simgear/structure/commands.hxx>
|
||||
|
||||
#include "test_suite/FGTestApi/testGlobals.hxx"
|
||||
#include "test_suite/FGTestApi/NavDataCache.hxx"
|
||||
#include "test_suite/FGTestApi/TestPilot.hxx"
|
||||
|
||||
#include <Navaids/FlightPlan.hxx>
|
||||
#include <Navaids/NavDataCache.hxx>
|
||||
#include <Navaids/navrecord.hxx>
|
||||
#include <Navaids/navlist.hxx>
|
||||
#include <Navaids/routePath.hxx>
|
||||
|
||||
// we need a default GPS instrument, hard to test seperately for now
|
||||
#include <Instrumentation/gps.hxx>
|
||||
|
||||
#include <Autopilot/route_mgr.hxx>
|
||||
|
||||
using namespace flightgear;
|
||||
|
||||
static bool static_haveProcedures = false;
|
||||
|
||||
static FlightPlanRef makeTestFP(const std::string& depICAO, const std::string& depRunway,
|
||||
const std::string& destICAO, const std::string& destRunway,
|
||||
const std::string& waypoints)
|
||||
{
|
||||
FlightPlanRef f = FlightPlan::create();
|
||||
FGTestApi::setUp::populateFPWithNasal(f, depICAO, depRunway, destICAO, destRunway, waypoints);
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
// Set up function for each test.
|
||||
void RouteManagerTests::setUp()
|
||||
{
|
||||
FGTestApi::setUp::initTestGlobals("routemanager");
|
||||
FGTestApi::setUp::initNavDataCache();
|
||||
|
||||
SGPath proceduresPath = SGPath::fromEnv("FG_PROCEDURES_PATH");
|
||||
if (proceduresPath.exists()) {
|
||||
static_haveProcedures = true;
|
||||
globals->append_fg_scenery(proceduresPath);
|
||||
}
|
||||
|
||||
globals->add_new_subsystem<FGRouteMgr>(SGSubsystemMgr::FDM);
|
||||
|
||||
// setup the default GPS, which is needed for waypoint
|
||||
// sequencing to work
|
||||
SGPropertyNode_ptr configNode(new SGPropertyNode);
|
||||
configNode->setStringValue("name", "gps");
|
||||
configNode->setIntValue("number", 0);
|
||||
GPS* gps(new GPS(configNode, true /* default mode */));
|
||||
m_gps = gps;
|
||||
|
||||
SGPropertyNode_ptr node = globals->get_props()->getNode("instrumentation", true)->getChild("gps", 0, true);
|
||||
// node->setBoolValue("serviceable", true);
|
||||
// globals->get_props()->setDoubleValue("systems/electrical/outputs/gps", 6.0);
|
||||
globals->add_subsystem("gps", gps, SGSubsystemMgr::POST_FDM);
|
||||
|
||||
globals->get_subsystem_mgr()->bind();
|
||||
globals->get_subsystem_mgr()->init();
|
||||
|
||||
FGTestApi::setUp::initStandardNasal();
|
||||
globals->get_subsystem_mgr()->postinit();
|
||||
}
|
||||
|
||||
|
||||
// Clean up after each test.
|
||||
void RouteManagerTests::tearDown()
|
||||
{
|
||||
m_gps = nullptr;
|
||||
FGTestApi::tearDown::shutdownTestGlobals();
|
||||
}
|
||||
|
||||
void RouteManagerTests::setPositionAndStabilise(const SGGeod& g)
|
||||
{
|
||||
FGTestApi::setPosition(g);
|
||||
for (int i=0; i<60; ++i) {
|
||||
globals->get_subsystem_mgr()->update(0.02);
|
||||
}
|
||||
}
|
||||
|
||||
void RouteManagerTests::testBasic()
|
||||
{
|
||||
//FGTestApi::setUp::logPositionToKML("rm_basic");
|
||||
|
||||
FlightPlanRef fp1 = makeTestFP("EGLC", "27", "EHAM", "06",
|
||||
"CLN IDESI RINIS VALKO RIVER RTM EKROS");
|
||||
fp1->setIdent("testplan");
|
||||
fp1->setCruiseFlightLevel(360);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL("RIVER"s, fp1->legAtIndex(5)->waypoint()->ident());
|
||||
|
||||
auto rm = globals->get_subsystem<FGRouteMgr>();
|
||||
rm->setFlightPlan(fp1);
|
||||
|
||||
auto gpsNode = globals->get_props()->getNode("instrumentation/gps", true);
|
||||
CPPUNIT_ASSERT(gpsNode->getStringValue("mode") == "obs");
|
||||
|
||||
rm->activate();
|
||||
|
||||
CPPUNIT_ASSERT(fp1->isActive());
|
||||
|
||||
// Nasal deleagte should have placed GPS into leg mode
|
||||
auto rmNode = globals->get_props()->getNode("autopilot/route-manager", true);
|
||||
|
||||
CPPUNIT_ASSERT(gpsNode->getStringValue("mode") == "leg");
|
||||
|
||||
CPPUNIT_ASSERT(rmNode->getStringValue("departure/airport") == "EGLC");
|
||||
CPPUNIT_ASSERT(rmNode->getStringValue("departure/runway") == "27");
|
||||
CPPUNIT_ASSERT(rmNode->getStringValue("departure/sid") == "");
|
||||
CPPUNIT_ASSERT(rmNode->getStringValue("departure/name") == "London City");
|
||||
|
||||
CPPUNIT_ASSERT(rmNode->getStringValue("destination/airport") == "EHAM");
|
||||
CPPUNIT_ASSERT(rmNode->getStringValue("destination/runway") == "06");
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(360, rmNode->getIntValue("cruise/flight-level"));
|
||||
CPPUNIT_ASSERT_EQUAL(false, rmNode->getBoolValue("airborne"));
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(0, rmNode->getIntValue("current-wp"));
|
||||
auto wp0Node = rmNode->getNode("wp");
|
||||
CPPUNIT_ASSERT(wp0Node->getStringValue("id") == "EGLC-27");
|
||||
|
||||
auto wp1Node = rmNode->getNode("wp[1]");
|
||||
CPPUNIT_ASSERT(wp1Node->getStringValue("id") == "CLN");
|
||||
|
||||
FGPositioned::TypeFilter f{FGPositioned::VOR};
|
||||
auto clactonVOR = fgpositioned_cast<FGNavRecord>(FGPositioned::findClosestWithIdent("CLN", SGGeod::fromDeg(0.0, 51.0), &f));
|
||||
|
||||
// verify hold entry course
|
||||
auto pilot = SGSharedPtr<FGTestApi::TestPilot>(new FGTestApi::TestPilot);
|
||||
FGTestApi::setPosition(fp1->departureRunway()->geod());
|
||||
pilot->resetAtPosition(fp1->departureRunway()->geod());
|
||||
|
||||
pilot->setSpeedKts(220);
|
||||
pilot->setCourseTrue(fp1->departureRunway()->headingDeg());
|
||||
pilot->setTargetAltitudeFtMSL(10000);
|
||||
|
||||
bool ok = FGTestApi::runForTimeWithCheck(30.0, [rmNode] () {
|
||||
if (rmNode->getIntValue("current-wp") == 1) return true;
|
||||
return false;
|
||||
});
|
||||
|
||||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
// continue outbound for some time
|
||||
pilot->setSpeedKts(250);
|
||||
FGTestApi::runForTime(30.0);
|
||||
|
||||
// turn towards Clacton VOR
|
||||
pilot->flyDirectTo(clactonVOR->geod());
|
||||
pilot->setTargetAltitudeFtMSL(30000);
|
||||
pilot->setSpeedKts(280);
|
||||
ok = FGTestApi::runForTimeWithCheck(6000.0, [rmNode] () {
|
||||
if (rmNode->getIntValue("current-wp") == 2) return true;
|
||||
return false;
|
||||
});
|
||||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
// short straight leg for testing
|
||||
pilot->flyHeading(90.0);
|
||||
pilot->setSpeedKts(330);
|
||||
FGTestApi::runForTime(30.0);
|
||||
|
||||
// let's engage LNAV mode :)
|
||||
pilot->flyGPSCourse(m_gps);
|
||||
|
||||
ok = FGTestApi::runForTimeWithCheck(6000.0, [rmNode] () {
|
||||
if (rmNode->getIntValue("current-wp") == 5) return true;
|
||||
return false;
|
||||
});
|
||||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
// check where we are - should be heading to RIVER from VALKO
|
||||
CPPUNIT_ASSERT_EQUAL(5, rmNode->getIntValue("current-wp"));
|
||||
CPPUNIT_ASSERT(wp0Node->getStringValue("id") == "RIVER");
|
||||
CPPUNIT_ASSERT(wp1Node->getStringValue("id") == "RTM");
|
||||
// slightly rapid descent
|
||||
pilot->setTargetAltitudeFtMSL(3000);
|
||||
|
||||
ok = FGTestApi::runForTimeWithCheck(6000.0, [rmNode] () {
|
||||
if (rmNode->getIntValue("current-wp") == 7) return true;
|
||||
return false;
|
||||
});
|
||||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
// run until the GPS reverts to OBS mode at the end of the flight plan
|
||||
ok = FGTestApi::runForTimeWithCheck(6000.0, [gpsNode] () {
|
||||
if (gpsNode->getStringValue("mode") == "obs") return true;
|
||||
return false;
|
||||
});
|
||||
|
||||
CPPUNIT_ASSERT(ok);
|
||||
CPPUNIT_ASSERT_EQUAL(-1, fp1->currentIndex());
|
||||
CPPUNIT_ASSERT(!fp1->isActive());
|
||||
CPPUNIT_ASSERT_EQUAL(-1, rmNode->getIntValue("current-wp"));
|
||||
CPPUNIT_ASSERT_EQUAL(false, rmNode->getBoolValue("active"));
|
||||
}
|
||||
|
||||
void RouteManagerTests::testDefaultSID()
|
||||
{
|
||||
FlightPlanRef fp1 = makeTestFP("EGLC", "27", "EHAM", "24",
|
||||
"CLN IDRID VALKO");
|
||||
fp1->setIdent("testplan");
|
||||
|
||||
auto rm = globals->get_subsystem<FGRouteMgr>();
|
||||
rm->setFlightPlan(fp1);
|
||||
|
||||
auto rmNode = globals->get_props()->getNode("autopilot/route-manager", true);
|
||||
rmNode->setStringValue("departure/sid", "DEFAULT");
|
||||
|
||||
// let's see what we got :)
|
||||
|
||||
rm->activate();
|
||||
|
||||
CPPUNIT_ASSERT(fp1->isActive());
|
||||
}
|
||||
|
||||
void RouteManagerTests::testDirectToLegOnFlightplanAndResume()
|
||||
{
|
||||
// FGTestApi::setUp::logPositionToKML("rm_dto_resume_leg");
|
||||
|
||||
// this is very similar to the identiucally name dtest in GPSTests, but relies on the Nasal
|
||||
// route manager delegate to perform the same task
|
||||
FlightPlanRef fp1 = makeTestFP("EBBR", "07L", "EGGD", "27",
|
||||
"NIK COA DVR TAWNY WOD");
|
||||
auto rm = globals->get_subsystem<FGRouteMgr>();
|
||||
rm->setFlightPlan(fp1);
|
||||
// FGTestApi::writeFlightPlanToKML(fp1);
|
||||
|
||||
auto gpsNode = globals->get_props()->getNode("instrumentation/gps", true);
|
||||
auto rmNode = globals->get_props()->getNode("autopilot/route-manager", true);
|
||||
|
||||
CPPUNIT_ASSERT(gpsNode->getStringValue("mode") == "obs");
|
||||
rm->activate();
|
||||
|
||||
CPPUNIT_ASSERT(fp1->isActive());
|
||||
|
||||
FGTestApi::setPosition(fp1->departureRunway()->pointOnCenterline(0.0));
|
||||
FGTestApi::runForTime(10.0); // let the GPS stabilize
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(std::string{"leg"}, gpsNode->getStringValue("mode"));
|
||||
CPPUNIT_ASSERT_EQUAL(std::string{"EBBR-07L"}, gpsNode->getStringValue("wp/wp[1]/ID"));
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(0, rmNode->getIntValue("current-wp"));
|
||||
auto wp0Node = rmNode->getNode("wp");
|
||||
CPPUNIT_ASSERT(wp0Node->getStringValue("id") == "EBBR-07L");
|
||||
|
||||
auto wp1Node = rmNode->getNode("wp[1]");
|
||||
CPPUNIT_ASSERT(wp1Node->getStringValue("id") == "NIK");
|
||||
|
||||
// initiate a direct to
|
||||
SGGeod p2 = fp1->departureRunway()->pointOnCenterline(5.0* SG_NM_TO_METER);
|
||||
FGTestApi::setPosition(p2);
|
||||
|
||||
auto doverVOR = fp1->legAtIndex(3)->waypoint()->source();
|
||||
|
||||
double distanceToDover = SGGeodesy::distanceNm(p2, doverVOR->geod());
|
||||
double bearingToDover = SGGeodesy::courseDeg(p2, doverVOR->geod());
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(std::string{"DVR"}, doverVOR->ident());
|
||||
gpsNode->setStringValue("scratch/ident", "DVR");
|
||||
gpsNode->setDoubleValue("scratch/longitude-deg", doverVOR->geod().getLongitudeDeg());
|
||||
gpsNode->setDoubleValue("scratch/latitude-deg", doverVOR->geod().getLatitudeDeg());
|
||||
gpsNode->setStringValue("command", "direct");
|
||||
CPPUNIT_ASSERT_EQUAL(std::string{"dto"}, gpsNode->getStringValue("mode"));
|
||||
|
||||
// check that upon reaching DOVER, we sequence to TAWNY and resume leg mode
|
||||
// this is handled by the default delegate in Nasal
|
||||
|
||||
SGGeod posNearDover = SGGeodesy::direct(p2, bearingToDover, (distanceToDover - 8.0) * SG_NM_TO_METER);
|
||||
FGTestApi::setPosition(posNearDover);
|
||||
|
||||
auto pilot = SGSharedPtr<FGTestApi::TestPilot>(new FGTestApi::TestPilot);
|
||||
pilot->resetAtPosition(posNearDover);
|
||||
pilot->setSpeedKts(250);
|
||||
pilot->flyGPSCourse(m_gps);
|
||||
|
||||
bool ok = FGTestApi::runForTimeWithCheck(180.0, [fp1] () {
|
||||
if (fp1->currentIndex() == 4) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
CPPUNIT_ASSERT(ok);
|
||||
CPPUNIT_ASSERT_EQUAL(std::string{"leg"}, gpsNode->getStringValue("mode"));
|
||||
}
|
||||
|
||||
void RouteManagerTests::testSequenceDiscontinuityAndResume()
|
||||
{
|
||||
// FGTestApi::setUp::logPositionToKML("rm_seq_discon_resume_leg");
|
||||
|
||||
FlightPlanRef fp1 = makeTestFP("LIRF", "16R", "LEBL", "07L",
|
||||
"GITRI BALEN MUREN TOSNU");
|
||||
|
||||
|
||||
fp1->insertWayptAtIndex(new Discontinuity(fp1), 3);
|
||||
|
||||
auto rm = globals->get_subsystem<FGRouteMgr>();
|
||||
rm->setFlightPlan(fp1);
|
||||
|
||||
auto gpsNode = globals->get_props()->getNode("instrumentation/gps", true);
|
||||
// auto rmNode = globals->get_props()->getNode("autopilot/route-manager", true);
|
||||
|
||||
auto balenLeg = fp1->legAtIndex(2);
|
||||
auto pos = fp1->pointAlongRoute(2, -8.0); // 8nm before BALEN
|
||||
FGTestApi::setPosition(pos);
|
||||
FGTestApi::runForTime(10.0); // let the GPS stabilize
|
||||
|
||||
CPPUNIT_ASSERT(gpsNode->getStringValue("mode") == "obs");
|
||||
rm->activate();
|
||||
|
||||
CPPUNIT_ASSERT(fp1->isActive());
|
||||
|
||||
CPPUNIT_ASSERT(gpsNode->getStringValue("mode") == "leg");
|
||||
fp1->setCurrentIndex(2);
|
||||
|
||||
auto pilot = SGSharedPtr<FGTestApi::TestPilot>(new FGTestApi::TestPilot);
|
||||
pilot->resetAtPosition(pos);
|
||||
pilot->setCourseTrue(270);
|
||||
pilot->setSpeedKts(250);
|
||||
pilot->flyGPSCourse(m_gps);
|
||||
|
||||
bool ok = FGTestApi::runForTimeWithCheck(180.0, [gpsNode] () {
|
||||
return (gpsNode->getStringValue("mode") == "obs");
|
||||
});
|
||||
|
||||
CPPUNIT_ASSERT(ok);
|
||||
CPPUNIT_ASSERT_EQUAL(2, fp1->currentIndex()); // shouldn't sequence
|
||||
|
||||
FGTestApi::runForTime(30.0);
|
||||
|
||||
|
||||
// 2nm before MUREN : this should be on the GC course from BALEN
|
||||
auto pos2 = fp1->pointAlongRoute(4, -6.0);
|
||||
FGTestApi::setPosition(pos2);
|
||||
FGTestApi::runForTime(2.0); // let the GPS stabilize
|
||||
|
||||
// initiate a direct-to the next real WP
|
||||
const auto murenPos = fp1->legAtIndex(4)->waypoint()->position();
|
||||
gpsNode->setStringValue("scratch/ident", "MUREN");
|
||||
gpsNode->setDoubleValue("scratch/longitude-deg", murenPos.getLongitudeDeg());
|
||||
gpsNode->setDoubleValue("scratch/latitude-deg", murenPos.getLatitudeDeg());
|
||||
gpsNode->setStringValue("command", "direct");
|
||||
CPPUNIT_ASSERT_EQUAL(std::string{"dto"}, gpsNode->getStringValue("mode"));
|
||||
|
||||
pilot->resetAtPosition(pos2);
|
||||
pilot->flyGPSCourse(m_gps);
|
||||
|
||||
ok = FGTestApi::runForTimeWithCheck(600.0, [fp1] () {
|
||||
if (fp1->currentIndex() == 5) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
CPPUNIT_ASSERT(ok);
|
||||
CPPUNIT_ASSERT_EQUAL(std::string{"leg"}, gpsNode->getStringValue("mode"));
|
||||
CPPUNIT_ASSERT_EQUAL(5, fp1->currentIndex());
|
||||
|
||||
FGTestApi::runForTime(30.0);
|
||||
|
||||
}
|
||||
|
||||
void RouteManagerTests::testDefaultApproach()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void RouteManagerTests::testHiddenWaypoints()
|
||||
{
|
||||
FlightPlanRef fp1 = makeTestFP("NZCH", "02", "NZAA", "05L",
|
||||
"ALADA NS WB WN MAMOD KAPTI OH");
|
||||
fp1->setIdent("testplan");
|
||||
fp1->setCruiseFlightLevel(360);
|
||||
|
||||
auto rm = globals->get_subsystem<FGRouteMgr>();
|
||||
rm->setFlightPlan(fp1);
|
||||
|
||||
auto gpsNode = globals->get_props()->getNode("instrumentation/gps", true);
|
||||
CPPUNIT_ASSERT(gpsNode->getStringValue("mode") == "obs");
|
||||
|
||||
// FIXME: use real Nasal test macros soon
|
||||
auto testNode = globals->get_props()->getNode("test-data", true);
|
||||
|
||||
fp1->legAtIndex(3)->waypoint()->setFlag(WPT_HIDDEN);
|
||||
|
||||
// ensure no visual path is generated for hidden waypoints
|
||||
RoutePath path(fp1);
|
||||
|
||||
// no path should be generated between 2 and 3
|
||||
CPPUNIT_ASSERT(path.pathForIndex(3).empty());
|
||||
|
||||
// no path should be generated between 3 and 4
|
||||
CPPUNIT_ASSERT(path.pathForIndex(4).empty());
|
||||
CPPUNIT_ASSERT(!path.pathForIndex(5).empty());
|
||||
|
||||
|
||||
bool ok = FGTestApi::executeNasal(R"(
|
||||
var fp = flightplan(); # retrieve the global flightplan
|
||||
setprop("/test-data/a", fp.numRemainingWaypoints());
|
||||
)");
|
||||
CPPUNIT_ASSERT(ok);
|
||||
CPPUNIT_ASSERT_EQUAL(fp1->numLegs(), testNode->getIntValue("a"));
|
||||
|
||||
rm->activate();
|
||||
fp1->setCurrentIndex(2);
|
||||
|
||||
ok = FGTestApi::executeNasal(R"(
|
||||
var fp = flightplan(); # retrieve the global flightplan
|
||||
setprop("/test-data/a", fp.numRemainingWaypoints());
|
||||
setprop("/test-data/b", fp.currentWP(2).id);
|
||||
)");
|
||||
|
||||
CPPUNIT_ASSERT(ok);
|
||||
CPPUNIT_ASSERT_EQUAL(7, testNode->getIntValue("a"));
|
||||
CPPUNIT_ASSERT_EQUAL(std::string{"WN"}, testNode->getStringValue("b"));
|
||||
|
||||
ok = FGTestApi::executeNasal(R"(
|
||||
var fp = flightplan(); # retrieve the global flightplan
|
||||
setprop("/test-data/c", fp.currentWP(-1).id);
|
||||
|
||||
# ensure invalid offset returns nil
|
||||
setprop("/test-data/d", fp.currentWP(-100) == nil);
|
||||
)");
|
||||
|
||||
CPPUNIT_ASSERT(ok);
|
||||
CPPUNIT_ASSERT_EQUAL(std::string{"ALADA"}, testNode->getStringValue("c"));
|
||||
CPPUNIT_ASSERT_EQUAL(true, testNode->getBoolValue("d"));
|
||||
}
|
||||
|
||||
void RouteManagerTests::testHoldFromNasal()
|
||||
{
|
||||
// FGTestApi::setUp::logPositionToKML("rm_hold_from_nasal");
|
||||
|
||||
// test that Nasal can set a hold-count (implicitly converting a leg
|
||||
// to a Hold waypt), configure the hold radial, and exit the hold
|
||||
|
||||
FlightPlanRef fp1 = makeTestFP("NZCH", "02", "NZAA", "05L",
|
||||
"ALADA NS WB WN MAMOD KAPTI OH");
|
||||
fp1->setIdent("testplan");
|
||||
fp1->setCruiseFlightLevel(360);
|
||||
|
||||
auto rm = globals->get_subsystem<FGRouteMgr>();
|
||||
rm->setFlightPlan(fp1);
|
||||
// FGTestApi::writeFlightPlanToKML(fp1);
|
||||
|
||||
auto gpsNode = globals->get_props()->getNode("instrumentation/gps", true);
|
||||
CPPUNIT_ASSERT(gpsNode->getStringValue("mode") == "obs");
|
||||
|
||||
rm->activate();
|
||||
|
||||
CPPUNIT_ASSERT(fp1->isActive());
|
||||
CPPUNIT_ASSERT(gpsNode->getStringValue("mode") == "leg");
|
||||
|
||||
SGGeod posEnrouteToWB = fp1->pointAlongRoute(3, -10.0);
|
||||
FGTestApi::setPositionAndStabilise(posEnrouteToWB);
|
||||
|
||||
// sequence everything to the correct wp
|
||||
fp1->setCurrentIndex(3);
|
||||
|
||||
// setup some hold data from Nasal. To make it more challenging,
|
||||
// do it once the wp is already active
|
||||
bool ok = FGTestApi::executeNasal(R"(
|
||||
var fp = flightplan(); # retrieve the global flightplan
|
||||
var leg = fp.getWP(3);
|
||||
leg.hold_count = 4;
|
||||
leg.hold_heading_radial_deg = 310;
|
||||
)");
|
||||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
// check the value updated in the leg
|
||||
CPPUNIT_ASSERT_EQUAL(4, fp1->legAtIndex(3)->holdCount());
|
||||
|
||||
// check we converted to a hold
|
||||
auto wp = fp1->legAtIndex(3)->waypoint();
|
||||
auto holdWpt = static_cast<flightgear::Hold*>(wp);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(wp->type(), std::string{"hold"});
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(310.0, holdWpt->headingRadialDeg(), 0.5);
|
||||
|
||||
// establish the test pilot at this position too
|
||||
auto pilot = SGSharedPtr<FGTestApi::TestPilot>(new FGTestApi::TestPilot);
|
||||
pilot->resetAtPosition(posEnrouteToWB);
|
||||
pilot->setSpeedKts(250);
|
||||
pilot->flyGPSCourse(m_gps);
|
||||
pilot->setCourseTrue(gpsNode->getDoubleValue("wp/leg-true-course-deg"));
|
||||
|
||||
// run for a bit to stabilize everything
|
||||
FGTestApi::runForTime(5.0);
|
||||
|
||||
// check we upgraded to a hold controller internally, and are flying to it :)
|
||||
auto statusNode = gpsNode->getNode("rnav-controller-status");
|
||||
CPPUNIT_ASSERT_EQUAL(std::string{"leg-to-hold"}, statusNode->getStringValue());
|
||||
|
||||
// check we're on the leg
|
||||
CPPUNIT_ASSERT_EQUAL(std::string{"NELSON VOR-DME"}, fp1->legAtIndex(2)->waypoint()->source()->name());
|
||||
|
||||
auto wbPos = fp1->legAtIndex(3)->waypoint()->position();
|
||||
auto nsPos = fp1->legAtIndex(2)->waypoint()->position();
|
||||
const double crsToWB = SGGeodesy::courseDeg(globals->get_aircraft_position(), wbPos);
|
||||
const double crsNSWB = SGGeodesy::courseDeg(nsPos,wbPos);
|
||||
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(crsToWB, gpsNode->getDoubleValue("wp/wp[1]/bearing-true-deg"), 0.5);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(crsNSWB, gpsNode->getDoubleValue("wp/leg-true-course-deg"), 0.5);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, gpsNode->getDoubleValue("wp/wp[1]/course-error-nm"), 0.05);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, gpsNode->getDoubleValue("wp/wp[1]/course-deviation-deg"), 0.5);
|
||||
|
||||
// fly into the hold, should be teardrop entry
|
||||
ok = FGTestApi::runForTimeWithCheck(600.0, [statusNode] () {
|
||||
if (statusNode->getStringValue() == "entry-teardrop") return true;
|
||||
return false;
|
||||
});
|
||||
|
||||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
ok = FGTestApi::runForTimeWithCheck(600.0, [statusNode] () {
|
||||
if (statusNode->getStringValue() == "hold-inbound") return true;
|
||||
return false;
|
||||
});
|
||||
|
||||
ok = FGTestApi::runForTimeWithCheck(600.0, [statusNode] () {
|
||||
if (statusNode->getStringValue() == "hold-outbound") return true;
|
||||
return false;
|
||||
});
|
||||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
// half way through the outbound turn
|
||||
FGTestApi::runForTime(30.0);
|
||||
|
||||
ok = FGTestApi::executeNasal(R"(
|
||||
setprop("/instrumentation/gps/command", "exit-hold");
|
||||
)");
|
||||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
// no change yet
|
||||
CPPUNIT_ASSERT_EQUAL(std::string{"hold-outbound"}, statusNode->getStringValue());
|
||||
|
||||
// then we fly inbound
|
||||
ok = FGTestApi::runForTimeWithCheck(600.0, [statusNode] () {
|
||||
if (statusNode->getStringValue() == "hold-inbound") return true;
|
||||
return false;
|
||||
});
|
||||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
// and then we exit
|
||||
ok = FGTestApi::runForTimeWithCheck(600.0, [fp1] () {
|
||||
if (fp1->currentIndex() == 4) return true;
|
||||
return false;
|
||||
});
|
||||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
// get back on course
|
||||
FGTestApi::runForTime(60.0);
|
||||
}
|
||||
|
||||
// check that when loading a GPX, airport waypoints are created
|
||||
// by the default delegate
|
||||
// https://sourceforge.net/p/flightgear/codetickets/2227/
|
||||
void RouteManagerTests::loadGPX()
|
||||
{
|
||||
auto rm = globals->get_subsystem<FGRouteMgr>();
|
||||
|
||||
FlightPlanRef f = FlightPlan::create();
|
||||
rm->setFlightPlan(f);
|
||||
|
||||
SGPath gpxPath = simgear::Dir::current().path() / "test_gpx.gpx";
|
||||
{
|
||||
sg_ofstream s(gpxPath);
|
||||
s << R"(<?xml version="1.0" encoding="UTF-8"?>
|
||||
<gpx xmlns="http://www.topografix.com/GPX/1/1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" creator="SkyVector" version="1.1" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">
|
||||
<rte>
|
||||
<name>KJFK-KBOS</name>
|
||||
<rtept lat="40.639928" lon="-73.778692">
|
||||
<name>KJFK</name>
|
||||
<overfly>false</overfly>
|
||||
</rtept>
|
||||
<rtept lat="41.641106" lon="-72.547419">
|
||||
<name>HFD</name>
|
||||
<overfly>false</overfly>
|
||||
</rtept>
|
||||
<rtept lat="42.362944" lon="-71.006389">
|
||||
<name>KBOS</name>
|
||||
<overfly>false</overfly>
|
||||
</rtept>
|
||||
|
||||
</rte>
|
||||
</gpx>
|
||||
)";
|
||||
}
|
||||
|
||||
CPPUNIT_ASSERT(f->load(gpxPath));
|
||||
|
||||
auto kbos = FGAirport::getByIdent("KBOS");
|
||||
auto kjfk = FGAirport::getByIdent("KJFK");
|
||||
CPPUNIT_ASSERT_EQUAL(kjfk, f->departureAirport());
|
||||
CPPUNIT_ASSERT_EQUAL(static_cast<FGRunway*>(nullptr), f->departureRunway());
|
||||
CPPUNIT_ASSERT_EQUAL(3, f->numLegs());
|
||||
CPPUNIT_ASSERT_EQUAL(kbos, f->destinationAirport());
|
||||
|
||||
auto wp1 = f->legAtIndex(1);
|
||||
CPPUNIT_ASSERT_EQUAL(std::string{"HFD"}, wp1->waypoint()->ident());
|
||||
auto wp2 = f->legAtIndex(2);
|
||||
CPPUNIT_ASSERT_EQUAL(std::string{"KBOS"}, wp2->waypoint()->ident());
|
||||
}
|
||||
|
||||
const std::string flightPlanXMLData =
|
||||
R"(<?xml version="1.0" encoding="UTF-8"?>
|
||||
<PropertyList>
|
||||
<version type="int">2</version>
|
||||
<departure>
|
||||
<airport type="string">EDDM</airport>
|
||||
<runway type="string">08R</runway>
|
||||
</departure>
|
||||
<destination>
|
||||
<airport type="string">EDDF</airport>
|
||||
</destination>
|
||||
<route>
|
||||
<wp>
|
||||
<type type="string">runway</type>
|
||||
<departure type="bool">true</departure>
|
||||
<ident type="string">08R</ident>
|
||||
<icao type="string">EDDM</icao>
|
||||
</wp>
|
||||
<wp n="1">
|
||||
<type type="string">navaid</type>
|
||||
<ident type="string">GIVMI</ident>
|
||||
<lon type="double">11.364700</lon>
|
||||
<lat type="double">48.701100</lat>
|
||||
</wp>
|
||||
<wp n="2">
|
||||
<type type="string">navaid</type>
|
||||
<ident type="string">ERNAS</ident>
|
||||
<lon type="double">11.219400</lon>
|
||||
<lat type="double">48.844700</lat>
|
||||
</wp>
|
||||
<wp n="3">
|
||||
<type type="string">navaid</type>
|
||||
<ident type="string">TALAL</ident>
|
||||
<lon type="double">11.085300</lon>
|
||||
<lat type="double">49.108300</lat>
|
||||
</wp>
|
||||
<wp n="4">
|
||||
<type type="string">navaid</type>
|
||||
<ident type="string">ERMEL</ident>
|
||||
<lon type="double">11.044700</lon>
|
||||
<lat type="double">49.187800</lat>
|
||||
</wp>
|
||||
<wp n="5">
|
||||
<type type="string">navaid</type>
|
||||
<ident type="string">PSA</ident>
|
||||
<lon type="double">9.348300</lon>
|
||||
<lat type="double">49.862200</lat>
|
||||
</wp>
|
||||
</route>
|
||||
</PropertyList>
|
||||
)";
|
||||
|
||||
|
||||
// The same test as above, but for a file exported from the route manager or online
|
||||
void RouteManagerTests::loadFGFP()
|
||||
{
|
||||
auto rm = globals->get_subsystem<FGRouteMgr>();
|
||||
|
||||
FlightPlanRef f = FlightPlan::create();
|
||||
rm->setFlightPlan(f);
|
||||
|
||||
SGPath fgfpPath = simgear::Dir::current().path() / "test_fgfp.fgfp";
|
||||
{
|
||||
sg_ofstream s(fgfpPath);
|
||||
s << flightPlanXMLData;
|
||||
}
|
||||
|
||||
CPPUNIT_ASSERT(f->load(fgfpPath));
|
||||
|
||||
auto eddm = FGAirport::getByIdent("EDDM");
|
||||
auto eddf = FGAirport::getByIdent("EDDF");
|
||||
CPPUNIT_ASSERT_EQUAL(eddm, f->departureAirport());
|
||||
CPPUNIT_ASSERT_EQUAL(eddm->getRunwayByIdent("08R")->ident(), f->departureRunway()->ident());
|
||||
CPPUNIT_ASSERT_EQUAL(7, f->numLegs());
|
||||
CPPUNIT_ASSERT_EQUAL(eddf, f->destinationAirport());
|
||||
|
||||
auto wp1 = f->legAtIndex(1);
|
||||
CPPUNIT_ASSERT_EQUAL(std::string{"GIVMI"}, wp1->waypoint()->ident());
|
||||
auto wp2 = f->legAtIndex(6);
|
||||
CPPUNIT_ASSERT_EQUAL(std::string{"EDDF"}, wp2->waypoint()->ident());
|
||||
|
||||
}
|
||||
|
||||
void RouteManagerTests::testRouteWithProcedures()
|
||||
{
|
||||
if (!static_haveProcedures)
|
||||
return;
|
||||
|
||||
auto rm = globals->get_subsystem<FGRouteMgr>();
|
||||
|
||||
FlightPlanRef f = FlightPlan::create();
|
||||
rm->setFlightPlan(f);
|
||||
|
||||
auto kjfk = FGAirport::findByIdent("KJFK");
|
||||
auto eham = FGAirport::findByIdent("EHAM");
|
||||
f->setDeparture(kjfk->getRunwayByIdent("13L"));
|
||||
f->setSID(kjfk->findSIDWithIdent("DEEZZ5.13L"), "CANDR");
|
||||
|
||||
f->setDestination(eham->getRunwayByIdent("18R"));
|
||||
f->setSTAR(eham->findSTARWithIdent("EEL1A"), "BEDUM");
|
||||
|
||||
f->setApproach(eham->findApproachWithIdent("VDM18R"));
|
||||
|
||||
auto w = f->waypointFromString("TOMYE");
|
||||
f->insertWayptAtIndex(w, f->indexOfFirstNonDepartureWaypoint());
|
||||
|
||||
auto w2 = f->waypointFromString("DEVOL");
|
||||
f->insertWayptAtIndex(w2, f->indexOfFirstArrivalWaypoint());
|
||||
|
||||
// let's check what we got
|
||||
|
||||
auto endOfSID = f->legAtIndex(f->indexOfFirstNonDepartureWaypoint() - 1);
|
||||
CPPUNIT_ASSERT_EQUAL(endOfSID->waypoint()->ident(), string{"CANDR"});
|
||||
|
||||
auto startOfSTAR = f->legAtIndex(f->indexOfFirstArrivalWaypoint());
|
||||
CPPUNIT_ASSERT_EQUAL(startOfSTAR->waypoint()->ident(), string{"BEDUM"});
|
||||
|
||||
auto endOfSTAR = f->legAtIndex(f->indexOfFirstApproachWaypoint() - 1);
|
||||
CPPUNIT_ASSERT_EQUAL(endOfSTAR->waypoint()->ident(), string{"ARTIP"});
|
||||
|
||||
auto startOfApproach = f->legAtIndex(f->indexOfFirstApproachWaypoint());
|
||||
CPPUNIT_ASSERT_EQUAL(startOfApproach->waypoint()->ident(), string{"D070O"});
|
||||
|
||||
auto landingRunway = f->legAtIndex(f->indexOfDestinationRunwayWaypoint());
|
||||
CPPUNIT_ASSERT(landingRunway->waypoint()->source() == f->destinationRunway());
|
||||
|
||||
auto firstMiss = f->legAtIndex(f->indexOfDestinationRunwayWaypoint() + 1);
|
||||
CPPUNIT_ASSERT_EQUAL(firstMiss->waypoint()->ident(), string{"(461)"});
|
||||
|
||||
// check it in Nasal too
|
||||
bool ok = FGTestApi::executeNasal(
|
||||
R"(
|
||||
var f = flightplan();
|
||||
var depEnd = f.getWP(f.firstNonDepartureLeg - 1);
|
||||
var firstArrival = f.getWP(f.firstArrivalLeg);
|
||||
var firstApproach = f.getWP(f.firstApproachLeg);
|
||||
var destRunway = f.getWP(f.destination_runway_leg);
|
||||
|
||||
unitTest.assert_equal(depEnd.id, 'CANDR');
|
||||
|
||||
var firstEnroute = f.getWP(f.firstNonDepartureLeg );
|
||||
unitTest.assert_equal(firstEnroute.id, 'TOMYE');
|
||||
|
||||
unitTest.assert_equal(firstArrival.id, 'BEDUM');
|
||||
unitTest.assert_equal(firstApproach.id, 'D070O');
|
||||
unitTest.assert_equal(destRunway.id, '18R');
|
||||
)");
|
||||
|
||||
CPPUNIT_ASSERT(ok);
|
||||
}
|
||||
|
||||
void RouteManagerTests::testRouteWithApproachProcedures()
|
||||
{
|
||||
if (!static_haveProcedures)
|
||||
return;
|
||||
|
||||
auto rm = globals->get_subsystem<FGRouteMgr>();
|
||||
|
||||
FlightPlanRef f = FlightPlan::create();
|
||||
rm->setFlightPlan(f);
|
||||
|
||||
auto kjfk = FGAirport::findByIdent("KJFK");
|
||||
auto eddm = FGAirport::findByIdent("EDDM");
|
||||
f->setDeparture(kjfk->getRunwayByIdent("13L"));
|
||||
|
||||
f->setDestination(eddm->getRunwayByIdent("08R"));
|
||||
f->setSTAR(eddm->findSTARWithIdent("ABGA3A.08R"));
|
||||
|
||||
f->setApproach(eddm->findApproachWithIdent("ILS08R"), "NAP08");
|
||||
|
||||
auto w = f->waypointFromString("TOMYE");
|
||||
f->insertWayptAtIndex(w, f->indexOfFirstNonDepartureWaypoint());
|
||||
|
||||
auto w2 = f->waypointFromString("DEVOL");
|
||||
f->insertWayptAtIndex(w2, f->indexOfFirstArrivalWaypoint());
|
||||
|
||||
// let's check what we got
|
||||
|
||||
auto startOfSTAR = f->legAtIndex(f->indexOfFirstArrivalWaypoint());
|
||||
CPPUNIT_ASSERT_EQUAL(startOfSTAR->waypoint()->ident(), string{"ABGAS"});
|
||||
|
||||
auto endOfSTAR = f->legAtIndex(f->indexOfFirstApproachWaypoint() - 1);
|
||||
CPPUNIT_ASSERT_EQUAL(endOfSTAR->waypoint()->ident(), string{"MIQ"});
|
||||
|
||||
auto startOfApproach = f->legAtIndex(f->indexOfFirstApproachWaypoint());
|
||||
CPPUNIT_ASSERT_EQUAL(startOfApproach->waypoint()->ident(), string{"NAPSA"});
|
||||
|
||||
auto startOfCoreApproach = f->legAtIndex(f->indexOfFirstApproachWaypoint() + 6);
|
||||
CPPUNIT_ASSERT_EQUAL(startOfCoreApproach->waypoint()->ident(), string{"BEGEN"});
|
||||
|
||||
auto landingRunway = f->legAtIndex(f->indexOfDestinationRunwayWaypoint());
|
||||
CPPUNIT_ASSERT(landingRunway->waypoint()->source() == f->destinationRunway());
|
||||
|
||||
|
||||
// // check it in Nasal too
|
||||
// bool ok = FGTestApi::executeNasal(
|
||||
// R"(
|
||||
// var f = flightplan();
|
||||
// var depEnd = f.getWP(f.firstNonDepartureLeg - 1);
|
||||
// var firstArrival = f.getWP(f.firstArrivalLeg);
|
||||
// var firstApproach = f.getWP(f.firstApproachLeg);
|
||||
// var destRunway = f.getWP(f.destination_runway_leg);
|
||||
//
|
||||
// unitTest.assert_equal(depEnd.id, 'CANDR');
|
||||
//
|
||||
// var firstEnroute = f.getWP(f.firstNonDepartureLeg );
|
||||
// unitTest.assert_equal(firstEnroute.id, 'TOMYE');
|
||||
//
|
||||
// unitTest.assert_equal(firstArrival.id, 'BEDUM');
|
||||
// unitTest.assert_equal(firstApproach.id, 'D070O');
|
||||
// unitTest.assert_equal(destRunway.id, '18R');
|
||||
// )");
|
||||
//
|
||||
// CPPUNIT_ASSERT(ok);
|
||||
}
|
||||
|
||||
void RouteManagerTests::testsSelectNavaid()
|
||||
{
|
||||
// this captures the issue at:
|
||||
// https://sourceforge.net/p/flightgear/codetickets/2372/
|
||||
|
||||
auto rm = globals->get_subsystem<FGRouteMgr>();
|
||||
|
||||
FlightPlanRef f = FlightPlan::create();
|
||||
rm->setFlightPlan(f);
|
||||
|
||||
auto usss = FGAirport::findByIdent("USSS");
|
||||
auto eddh = FGAirport::findByIdent("EDDH");
|
||||
f->setDeparture(usss); // Yekaterinberg
|
||||
f->setDestination(eddh);
|
||||
|
||||
auto rmNode = globals->get_props()->getNode("autopilot/route-manager", true);
|
||||
rmNode->setStringValue("input", "@INSERT1:UUDD");
|
||||
rmNode->setStringValue("input", "@INSERT2:UKKM");
|
||||
rmNode->setStringValue("input", "@INSERT2:IP");
|
||||
rmNode->setStringValue("input", "@INSERT3:OD");
|
||||
|
||||
auto leg = f->legAtIndex(2);
|
||||
auto wp1 = leg->waypoint();
|
||||
CPPUNIT_ASSERT_EQUAL(wp1->ident(), string{"IP"});
|
||||
CPPUNIT_ASSERT_EQUAL(wp1->source()->name(), string{"ZAKHAROVKA NDB"});
|
||||
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(227, leg->courseDeg(), 0.5);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(59, leg->distanceNm(), 0.5);
|
||||
|
||||
leg = f->legAtIndex(3);
|
||||
auto wp2 = leg->waypoint();
|
||||
CPPUNIT_ASSERT_EQUAL(wp2->ident(), string{"OD"});
|
||||
CPPUNIT_ASSERT_EQUAL(wp2->source()->name(), string{"BRYANSK NDB"});
|
||||
}
|
||||
|
||||
void RouteManagerTests::testsSelectWaypoint()
|
||||
{
|
||||
// another part of the issue at
|
||||
// https://sourceforge.net/p/flightgear/codetickets/2372/
|
||||
|
||||
auto rm = globals->get_subsystem<FGRouteMgr>();
|
||||
|
||||
FlightPlanRef f = FlightPlan::create();
|
||||
rm->setFlightPlan(f);
|
||||
|
||||
auto rmNode = globals->get_props()->getNode("autopilot/route-manager", true);
|
||||
rmNode->setStringValue("input", "70N,015E");
|
||||
rmNode->setStringValue("input", "55N,015E");
|
||||
rmNode->setStringValue("input", "@INSERT1:OSS");
|
||||
rmNode->setStringValue("input", "@INSERT2:BOR");
|
||||
|
||||
auto leg = f->legAtIndex(1);
|
||||
auto wp1 = leg->waypoint();
|
||||
CPPUNIT_ASSERT_EQUAL(wp1->ident(), string{"OSS"});
|
||||
CPPUNIT_ASSERT_EQUAL(wp1->source()->name(), string{"OSTERSUND VOR-DME"});
|
||||
|
||||
// CPPUNIT_ASSERT_DOUBLES_EQUAL(227, leg->courseDeg(), 0.5);
|
||||
// CPPUNIT_ASSERT_DOUBLES_EQUAL(59, leg->distanceNm(), 0.5);
|
||||
|
||||
leg = f->legAtIndex(2);
|
||||
auto wp2 = leg->waypoint();
|
||||
CPPUNIT_ASSERT_EQUAL(wp2->ident(), string{"BOR"});
|
||||
CPPUNIT_ASSERT_EQUAL(wp2->source()->name(), string{"BORLANGE VOR-DME"});
|
||||
}
|
||||
|
||||
void RouteManagerTests::testCommandAPI()
|
||||
{
|
||||
auto rm = globals->get_subsystem<FGRouteMgr>();
|
||||
SGPath fgfpPath = simgear::Dir::current().path() / "test_fgfp_2.fgfp";
|
||||
{
|
||||
sg_ofstream s(fgfpPath);
|
||||
s << flightPlanXMLData;
|
||||
}
|
||||
|
||||
{
|
||||
SGPropertyNode_ptr args(new SGPropertyNode);
|
||||
args->setStringValue("path", fgfpPath.utf8Str());
|
||||
CPPUNIT_ASSERT(globals->get_commands()->execute("load-flightplan", args));
|
||||
}
|
||||
|
||||
auto f = rm->flightPlan();
|
||||
CPPUNIT_ASSERT_EQUAL(7, f->numLegs());
|
||||
|
||||
CPPUNIT_ASSERT(!f->isActive());
|
||||
|
||||
{
|
||||
SGPropertyNode_ptr args(new SGPropertyNode);
|
||||
CPPUNIT_ASSERT(globals->get_commands()->execute("activate-flightplan", args));
|
||||
}
|
||||
|
||||
CPPUNIT_ASSERT(f->isActive());
|
||||
|
||||
{
|
||||
SGPropertyNode_ptr args(new SGPropertyNode);
|
||||
args->setIntValue("index", 3);
|
||||
CPPUNIT_ASSERT(globals->get_commands()->execute("set-active-waypt", args));
|
||||
}
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(3, f->currentIndex());
|
||||
|
||||
{
|
||||
SGPropertyNode_ptr args(new SGPropertyNode);
|
||||
args->setIntValue("index", 4);
|
||||
args->setStringValue("navaid", "WLD");
|
||||
|
||||
// let's build an offset waypoint for fun
|
||||
args->setDoubleValue("offset-nm", 10.0);
|
||||
args->setDoubleValue("radial", 30);
|
||||
CPPUNIT_ASSERT(globals->get_commands()->execute("insert-waypt", args));
|
||||
}
|
||||
|
||||
auto waldaWpt = f->legAtIndex(4)->waypoint();
|
||||
auto waldaVOR = waldaWpt->source();
|
||||
CPPUNIT_ASSERT_EQUAL(string{"WALDA VOR-DME"}, waldaVOR->name());
|
||||
|
||||
auto d = SGGeodesy::distanceNm(waldaVOR->geod(), waldaWpt->position());
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(10.0, d, 0.1);
|
||||
}
|
||||
|
||||
void RouteManagerTests::testRMBug2616()
|
||||
{
|
||||
auto edty = FGAirport::findByIdent("EDTY"s);
|
||||
edty->testSuiteInjectProceduresXML(SGPath::fromUtf8(FG_TEST_SUITE_DATA) / "EDTY.procedures.xml");
|
||||
|
||||
auto ils28Approach = edty->findApproachWithIdent("ILS28"s);
|
||||
CPPUNIT_ASSERT(ils28Approach);
|
||||
|
||||
auto rm = globals->get_subsystem<FGRouteMgr>();
|
||||
auto f = rm->flightPlan();
|
||||
f->clearLegs();
|
||||
|
||||
auto edds = FGAirport::findByIdent("EDDS");
|
||||
f->setDeparture(edds->getRunwayByIdent("25"));
|
||||
f->setDestination(edty->getRunwayByIdent("28"));
|
||||
f->setApproach(ils28Approach);
|
||||
|
||||
CPPUNIT_ASSERT(f->destinationRunway()->ident() == "28"s);
|
||||
CPPUNIT_ASSERT(f->approach()->ident() == "ILS28"s);
|
||||
|
||||
|
||||
rm->activate();
|
||||
CPPUNIT_ASSERT(f->isActive());
|
||||
}
|
||||
|
||||
void RouteManagerTests::testsSelectWaypoint2()
|
||||
{
|
||||
auto rm = globals->get_subsystem<FGRouteMgr>();
|
||||
FlightPlanRef f = FlightPlan::create();
|
||||
rm->setFlightPlan(f);
|
||||
auto rmNode = globals->get_props()->getNode("autopilot/route-manager", true);
|
||||
rmNode->setStringValue("input", "UAAA");
|
||||
rmNode->setStringValue("input", "EDDH");
|
||||
rmNode->setStringValue("input", "@INSERT1:ALM");
|
||||
auto leg = f->legAtIndex(1);
|
||||
auto wp1 = leg->waypoint();
|
||||
CPPUNIT_ASSERT_EQUAL(wp1->ident(), string{"ALM"});
|
||||
// CPPUNIT_ASSERT_EQUAL(wp1->source()->name(), string{"ALMATY VOR-DME"});
|
||||
}
|
||||
87
test_suite/unit_tests/Navaids/test_routeManager.hxx
Normal file
87
test_suite/unit_tests/Navaids/test_routeManager.hxx
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _FG_ROUTE_MANAGER_UNIT_TESTS_HXX
|
||||
#define _FG_ROUTE_MANAGER_UNIT_TESTS_HXX
|
||||
|
||||
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
#include <cppunit/TestFixture.h>
|
||||
|
||||
class SGGeod;
|
||||
class GPS;
|
||||
|
||||
// The flight plan unit tests.
|
||||
class RouteManagerTests : public CppUnit::TestFixture
|
||||
{
|
||||
// Set up the test suite.
|
||||
CPPUNIT_TEST_SUITE(RouteManagerTests);
|
||||
CPPUNIT_TEST(testBasic);
|
||||
CPPUNIT_TEST(testDefaultSID);
|
||||
CPPUNIT_TEST(testDefaultApproach);
|
||||
CPPUNIT_TEST(testDirectToLegOnFlightplanAndResume);
|
||||
CPPUNIT_TEST(testHoldFromNasal);
|
||||
CPPUNIT_TEST(testSequenceDiscontinuityAndResume);
|
||||
CPPUNIT_TEST(testHiddenWaypoints);
|
||||
CPPUNIT_TEST(loadGPX);
|
||||
CPPUNIT_TEST(loadFGFP);
|
||||
CPPUNIT_TEST(testRouteWithProcedures);
|
||||
CPPUNIT_TEST(testRouteWithApproachProcedures);
|
||||
CPPUNIT_TEST(testsSelectNavaid);
|
||||
CPPUNIT_TEST(testCommandAPI);
|
||||
CPPUNIT_TEST(testRMBug2616);
|
||||
CPPUNIT_TEST(testsSelectWaypoint);
|
||||
CPPUNIT_TEST(testsSelectWaypoint2);
|
||||
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
// void setPositionAndStabilise(FGNavRadio* r, const SGGeod& g);
|
||||
|
||||
public:
|
||||
// Set up function for each test.
|
||||
void setUp();
|
||||
|
||||
// Clean up after each test.
|
||||
void tearDown();
|
||||
|
||||
void setPositionAndStabilise(const SGGeod& g);
|
||||
|
||||
// The tests.
|
||||
void testBasic();
|
||||
void testDefaultSID();
|
||||
void testDefaultApproach();
|
||||
void testDirectToLegOnFlightplanAndResume();
|
||||
void testHoldFromNasal();
|
||||
void testSequenceDiscontinuityAndResume();
|
||||
void testHiddenWaypoints();
|
||||
void loadGPX();
|
||||
void loadFGFP();
|
||||
void testRouteWithProcedures();
|
||||
void testRouteWithApproachProcedures();
|
||||
void testsSelectNavaid();
|
||||
void testCommandAPI();
|
||||
void testsSelectWaypoint();
|
||||
void testRMBug2616();
|
||||
void testsSelectWaypoint2();
|
||||
private:
|
||||
GPS* m_gps = nullptr;
|
||||
};
|
||||
|
||||
#endif // _FG_ROUTE_MANAGER_UNIT_TESTS_HXX
|
||||
14
test_suite/unit_tests/Network/CMakeLists.txt
Normal file
14
test_suite/unit_tests/Network/CMakeLists.txt
Normal file
@@ -0,0 +1,14 @@
|
||||
set(TESTSUITE_SOURCES
|
||||
${TESTSUITE_SOURCES}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/TestSuite.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_swiftService.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_swiftAircraftManager.cxx
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
set(TESTSUITE_HEADERS
|
||||
${TESTSUITE_HEADERS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_swiftService.hxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_swiftAircraftManager.hxx
|
||||
PARENT_SCOPE
|
||||
)
|
||||
11
test_suite/unit_tests/Network/TestSuite.cxx
Normal file
11
test_suite/unit_tests/Network/TestSuite.cxx
Normal file
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: (C) 2022 Lars Toenning <dev@ltoenning.de>
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "test_swiftAircraftManager.hxx"
|
||||
#include "test_swiftService.hxx"
|
||||
|
||||
// Set up the unit tests.
|
||||
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(SwiftAircraftManagerTest, "Unit tests");
|
||||
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(SwiftServiceTest, "Unit tests");
|
||||
105
test_suite/unit_tests/Network/test_swiftAircraftManager.cxx
Normal file
105
test_suite/unit_tests/Network/test_swiftAircraftManager.cxx
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: (C) 2022 Lars Toenning <dev@ltoenning.de>
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "test_swiftAircraftManager.hxx"
|
||||
|
||||
#include "test_suite/FGTestApi/testGlobals.hxx"
|
||||
|
||||
#include "Network/Swift/SwiftAircraftManager.h"
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <Main/globals.hxx>
|
||||
|
||||
void SwiftAircraftManagerTest::setUp()
|
||||
{
|
||||
FGTestApi::setUp::initTestGlobals("SwiftService");
|
||||
}
|
||||
|
||||
void SwiftAircraftManagerTest::tearDown()
|
||||
{
|
||||
FGTestApi::tearDown::shutdownTestGlobals();
|
||||
}
|
||||
|
||||
|
||||
std::vector<SGSharedPtr<FGAIBase>> SwiftAircraftManagerTest::getAIList()
|
||||
{
|
||||
return globals->get_subsystem<FGAIManager>()->get_ai_list();
|
||||
}
|
||||
|
||||
void SwiftAircraftManagerTest::testAircraftManager()
|
||||
{
|
||||
globals->add_new_subsystem<FGAIManager>(SGSubsystemMgr::POST_FDM);
|
||||
globals->get_subsystem<FGAIManager>()->bind();
|
||||
globals->get_subsystem<FGAIManager>()->init();
|
||||
|
||||
FGSwiftAircraftManager acm;
|
||||
acm.addPlane("BER123", "PATH_TO_MODEL");
|
||||
CPPUNIT_ASSERT_EQUAL(globals->get_subsystem<FGAIManager>()->get_ai_list().size(), (size_t)1);
|
||||
|
||||
acm.addPlane("BAW123", "PATH_TO_MODEL");
|
||||
CPPUNIT_ASSERT_EQUAL(globals->get_subsystem<FGAIManager>()->get_ai_list().size(), (size_t)2);
|
||||
|
||||
for (auto& aircraft : getAIList()) {
|
||||
CPPUNIT_ASSERT(!aircraft->getDie());
|
||||
}
|
||||
|
||||
acm.removeAllPlanes();
|
||||
for (auto& aircraft : getAIList()) {
|
||||
CPPUNIT_ASSERT(aircraft->getDie());
|
||||
}
|
||||
|
||||
acm.addPlane("BER123", "PATH_TO_MODEL");
|
||||
CPPUNIT_ASSERT(!getAIList()[2]->getDie());
|
||||
acm.removePlane("BER123");
|
||||
CPPUNIT_ASSERT(getAIList()[2]->getDie());
|
||||
|
||||
// Test position updates
|
||||
acm.addPlane("SAS123", "PATH_TO_MODEL");
|
||||
SGGeod position;
|
||||
position.setLatitudeDeg(50.0);
|
||||
position.setLongitudeDeg(6.0);
|
||||
position.setElevationM(1024);
|
||||
|
||||
acm.updatePlanes({{"SAS123", position, SGVec3d(1.0, 2.0, 3.0), 200, false}});
|
||||
CPPUNIT_ASSERT_EQUAL(fgGetString("/ai/models/swift[3]/callsign"), std::string("SAS123"));
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(fgGetDouble("/ai/models/swift[3]/orientation/pitch-deg"), 1.0, 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(fgGetDouble("/ai/models/swift[3]/orientation/roll-deg"), 2.0, 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(fgGetDouble("/ai/models/swift[3]/orientation/true-heading-deg"), 3.0, 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(fgGetDouble("/ai/models/swift[3]/position/latitude-deg"), 50.0, 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(fgGetDouble("/ai/models/swift[3]/position/longitude-deg"), 6.0, 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(fgGetDouble("/ai/models/swift[3]/velocities/true-airspeed-kt"), 200, 0.1);
|
||||
|
||||
position.setLatitudeDeg(20.0);
|
||||
position.setLongitudeDeg(4.0);
|
||||
acm.updatePlanes({{"SAS123", position, SGVec3d(5.0, 6.0, 7.0), 400, false}});
|
||||
CPPUNIT_ASSERT_EQUAL(fgGetString("/ai/models/swift[3]/callsign"), std::string("SAS123"));
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(fgGetDouble("/ai/models/swift[3]/orientation/pitch-deg"), 5.0, 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(fgGetDouble("/ai/models/swift[3]/orientation/roll-deg"), 6.0, 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(fgGetDouble("/ai/models/swift[3]/orientation/true-heading-deg"), 7.0, 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(fgGetDouble("/ai/models/swift[3]/position/latitude-deg"), 20.0, 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(fgGetDouble("/ai/models/swift[3]/position/longitude-deg"), 4.0, 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(fgGetDouble("/ai/models/swift[3]/velocities/true-airspeed-kt"), 400, 0.1);
|
||||
|
||||
|
||||
// Update another aircraft
|
||||
acm.addPlane("DAL123", "PATH_TO_MODEL");
|
||||
position.setLatitudeDeg(-20.0);
|
||||
position.setLongitudeDeg(5.0);
|
||||
acm.updatePlanes({{"DAL123", position, SGVec3d(1.0, 1.0, 1.0), 250, false}});
|
||||
CPPUNIT_ASSERT_EQUAL(fgGetString("/ai/models/swift[4]/callsign"), std::string("DAL123"));
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(fgGetDouble("/ai/models/swift[4]/orientation/pitch-deg"), 1.0, 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(fgGetDouble("/ai/models/swift[4]/orientation/roll-deg"), 1.0, 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(fgGetDouble("/ai/models/swift[4]/orientation/true-heading-deg"), 1.0, 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(fgGetDouble("/ai/models/swift[4]/position/latitude-deg"), -20.0, 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(fgGetDouble("/ai/models/swift[4]/position/longitude-deg"), 5.0, 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(fgGetDouble("/ai/models/swift[4]/velocities/true-airspeed-kt"), 250, 0.1);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(fgGetString("/ai/models/swift[3]/callsign"), std::string("SAS123"));
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(fgGetDouble("/ai/models/swift[3]/orientation/pitch-deg"), 5.0, 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(fgGetDouble("/ai/models/swift[3]/orientation/roll-deg"), 6.0, 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(fgGetDouble("/ai/models/swift[3]/orientation/true-heading-deg"), 7.0, 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(fgGetDouble("/ai/models/swift[3]/position/latitude-deg"), 20.0, 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(fgGetDouble("/ai/models/swift[3]/position/longitude-deg"), 4.0, 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(fgGetDouble("/ai/models/swift[3]/velocities/true-airspeed-kt"), 400, 0.1);
|
||||
}
|
||||
37
test_suite/unit_tests/Network/test_swiftAircraftManager.hxx
Normal file
37
test_suite/unit_tests/Network/test_swiftAircraftManager.hxx
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: (C) 2022 Lars Toenning <dev@ltoenning.de>
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef FLIGHTGEAR_TEST_SWIFTAIRCRAFTMANAGER_H
|
||||
#define FLIGHTGEAR_TEST_SWIFTAIRCRAFTMANAGER_H
|
||||
|
||||
#include <cppunit/TestFixture.h>
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
|
||||
#include <AIModel/AIBase.hxx>
|
||||
|
||||
|
||||
class SwiftAircraftManagerTest : public CppUnit::TestFixture
|
||||
{
|
||||
// Set up the test suite.
|
||||
CPPUNIT_TEST_SUITE(SwiftAircraftManagerTest);
|
||||
CPPUNIT_TEST(testAircraftManager);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
// Set up function for each test.
|
||||
void setUp();
|
||||
|
||||
// Clean up after each test.
|
||||
void tearDown();
|
||||
|
||||
// Test
|
||||
void testAircraftManager();
|
||||
|
||||
// Helper
|
||||
std::vector<SGSharedPtr<FGAIBase>> getAIList();
|
||||
};
|
||||
|
||||
|
||||
#endif //FLIGHTGEAR_TEST_SWIFTAIRCRAFTMANAGER_H
|
||||
140
test_suite/unit_tests/Network/test_swiftService.cxx
Normal file
140
test_suite/unit_tests/Network/test_swiftService.cxx
Normal file
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: (C) 2022 Lars Toenning <dev@ltoenning.de>
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "test_swiftService.hxx"
|
||||
|
||||
#include "test_suite/FGTestApi/testGlobals.hxx"
|
||||
|
||||
#include "Network/Swift/service.h"
|
||||
#include <Main/fg_props.hxx>
|
||||
|
||||
void SwiftServiceTest::setUp()
|
||||
{
|
||||
FGTestApi::setUp::initTestGlobals("SwiftService");
|
||||
|
||||
// Setup properties
|
||||
fgSetBool("/sim/freeze/master", true);
|
||||
fgSetDouble("/position/latitude-deg", 50.12);
|
||||
fgSetDouble("/position/longitude-deg", 6.3);
|
||||
fgSetDouble("/position/altitude-ft", 12000.0);
|
||||
fgSetDouble("/position/altitude-agl-ft", 1020.0);
|
||||
fgSetDouble("/velocities/groundspeed-kt", 242.0);
|
||||
fgSetDouble("/orientation/pitch-deg", 3.0);
|
||||
fgSetDouble("/orientation/roll-deg", 1.0);
|
||||
fgSetDouble("/orientation/heading-deg", 230.0);
|
||||
fgSetBool("/gear/gear/wow", false);
|
||||
fgSetDouble("/instrumentation/comm/frequencies/selected-mhz", 122.8);
|
||||
fgSetDouble("/instrumentation/comm/frequencies/standby-mhz", 135.65);
|
||||
fgSetDouble("/instrumentation/comm[1]/frequencies/selected-mhz", 121.5);
|
||||
fgSetDouble("/instrumentation/comm[1]/frequencies/standby-mhz", 118.3);
|
||||
fgSetInt("/instrumentation/transponder/id-code", 1234);
|
||||
fgSetInt("/instrumentation/transponder/inputs/knob-mode", 1);
|
||||
fgSetBool("/instrumentation/transponder/ident", true);
|
||||
fgSetBool("/controls/lighting/beacon", true);
|
||||
fgSetBool("/controls/lighting/landing-lights", false);
|
||||
fgSetBool("/controls/lighting/nav-lights", true);
|
||||
fgSetBool("/controls/lighting/strobe", true);
|
||||
fgSetBool("/controls/lighting/taxi-light", false);
|
||||
fgSetBool("/instrumentation/altimeter/serviceable", true);
|
||||
fgSetDouble("/instrumentation/altimeter/pressure-alt-ft", 24000.0);
|
||||
fgSetDouble("/surface-positions/flap-pos-norm", 0.0);
|
||||
fgSetDouble("/gear/gear/position-norm", 0.7);
|
||||
fgSetDouble("/surface-positions/speedbrake-pos-norm", 0.4);
|
||||
fgSetString("/sim/aircraft", "glider");
|
||||
fgSetDouble("/position/ground-elev-m", 778.0);
|
||||
fgSetDouble("/velocities/speed-east-fps", 20.0);
|
||||
fgSetDouble("/velocities/speed-down-fps", -30.0);
|
||||
fgSetDouble("/velocities/speed-north-fps", -10.2);
|
||||
fgSetDouble("/orientation/roll-rate-degps", 1.0);
|
||||
fgSetDouble("/orientation/pitch-rate-degps", 0.0);
|
||||
fgSetDouble("/orientation/yaw-rate-degps", -2.0);
|
||||
fgSetDouble("/instrumentation/comm/volume", 42.0);
|
||||
fgSetDouble("/instrumentation/comm[1]/volume", 100.0);
|
||||
}
|
||||
|
||||
void SwiftServiceTest::tearDown()
|
||||
{
|
||||
FGTestApi::tearDown::shutdownTestGlobals();
|
||||
}
|
||||
|
||||
void SwiftServiceTest::testService()
|
||||
{
|
||||
FGSwiftBus::CService service;
|
||||
CPPUNIT_ASSERT(service.isPaused());
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(service.getLatitude(), 50.12, 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(service.getLongitude(), 6.3, 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(service.getAltitudeMSL(), 12000.0, 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(service.getHeightAGL(), 1020.0, 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(service.getGroundSpeed(), 242.0, 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(service.getPitch(), 3.0, 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(service.getRoll(), 1.0, 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(service.getTrueHeading(), 230.0, 0.1);
|
||||
CPPUNIT_ASSERT(!service.getAllWheelsOnGround());
|
||||
CPPUNIT_ASSERT_EQUAL(service.getCom1Active(), 122800);
|
||||
CPPUNIT_ASSERT_EQUAL(service.getCom1Standby(), 135650);
|
||||
CPPUNIT_ASSERT_EQUAL(service.getCom2Active(), 121500);
|
||||
CPPUNIT_ASSERT_EQUAL(service.getCom2Standby(), 118300);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(service.getTransponderCode(), 1234);
|
||||
CPPUNIT_ASSERT_EQUAL(service.getTransponderMode(), 1);
|
||||
CPPUNIT_ASSERT(service.getTransponderIdent());
|
||||
CPPUNIT_ASSERT(service.getBeaconLightsOn());
|
||||
CPPUNIT_ASSERT(!service.getLandingLightsOn());
|
||||
CPPUNIT_ASSERT(service.getNavLightsOn());
|
||||
CPPUNIT_ASSERT(service.getStrobeLightsOn());
|
||||
CPPUNIT_ASSERT(!service.getTaxiLightsOn());
|
||||
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(service.getFlapsDeployRatio(), 0.0, 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(service.getGearDeployRatio(), 0.7, 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(service.getSpeedBrakeRatio(), 0.4, 0.1);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(service.getAircraftName(), std::string("glider"));
|
||||
|
||||
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(service.getGroundElevation(), 778.0, 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(service.getVelocityX(), 20.0 * SG_FEET_TO_METER, 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(service.getVelocityY(), -30.0 * SG_FEET_TO_METER * -1, 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(service.getVelocityZ(), -10.2 * SG_FEET_TO_METER, 0.1);
|
||||
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(service.getRollRate(), 1.0 * SG_DEGREES_TO_RADIANS, 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(service.getPitchRate(), 0.0 * SG_DEGREES_TO_RADIANS, 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(service.getYawRate(), -2.0 * SG_DEGREES_TO_RADIANS, 0.1);
|
||||
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(service.getCom1Volume(), 42.0, 0.1);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(service.getCom2Volume(), 100.0, 0.1);
|
||||
|
||||
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(service.getPressAlt(), 24000.0, 0.1);
|
||||
fgSetBool("/instrumentation/altimeter/serviceable", false);
|
||||
// Fallback if altimeter is not serviceable
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(service.getPressAlt(), service.getAltitudeMSL(), 0.1);
|
||||
|
||||
// Test setter
|
||||
service.setCom1Active(128550);
|
||||
CPPUNIT_ASSERT_EQUAL(service.getCom1Active(), 128550);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(fgGetDouble("/instrumentation/comm/frequencies/selected-mhz"), 128.550, 0.1);
|
||||
|
||||
service.setCom1Standby(128650);
|
||||
CPPUNIT_ASSERT_EQUAL(service.getCom1Standby(), 128650);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(fgGetDouble("/instrumentation/comm/frequencies/standby-mhz"), 128.650, 0.1);
|
||||
|
||||
|
||||
service.setCom2Active(121900);
|
||||
CPPUNIT_ASSERT_EQUAL(service.getCom2Active(), 121900);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(fgGetDouble("/instrumentation/comm[1]/frequencies/selected-mhz"), 121.900, 0.1);
|
||||
|
||||
service.setCom2Standby(121600);
|
||||
CPPUNIT_ASSERT_EQUAL(service.getCom2Standby(), 121600);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(fgGetDouble("/instrumentation/comm[1]/frequencies/standby-mhz"), 121.600, 0.1);
|
||||
|
||||
service.setTransponderCode(2000);
|
||||
CPPUNIT_ASSERT_EQUAL(service.getTransponderCode(), 2000);
|
||||
CPPUNIT_ASSERT_EQUAL(fgGetInt("/instrumentation/transponder/id-code"), 2000);
|
||||
|
||||
|
||||
service.setTransponderMode(0);
|
||||
CPPUNIT_ASSERT_EQUAL(service.getTransponderMode(), 0);
|
||||
CPPUNIT_ASSERT_EQUAL(fgGetInt("/instrumentation/transponder/inputs/knob-mode"), 0);
|
||||
}
|
||||
31
test_suite/unit_tests/Network/test_swiftService.hxx
Normal file
31
test_suite/unit_tests/Network/test_swiftService.hxx
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: (C) 2022 Lars Toenning <dev@ltoenning.de>
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef FLIGHTGEAR_TEST_SWIFTSERVICE_H
|
||||
#define FLIGHTGEAR_TEST_SWIFTSERVICE_H
|
||||
|
||||
#include <cppunit/TestFixture.h>
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
|
||||
class SwiftServiceTest : public CppUnit::TestFixture
|
||||
{
|
||||
// Set up the test suite.
|
||||
CPPUNIT_TEST_SUITE(SwiftServiceTest);
|
||||
CPPUNIT_TEST(testService);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
// Set up function for each test.
|
||||
void setUp();
|
||||
|
||||
// Clean up after each test.
|
||||
void tearDown();
|
||||
|
||||
// Test
|
||||
void testService();
|
||||
};
|
||||
|
||||
|
||||
#endif //FLIGHTGEAR_TEST_SWIFTSERVICE_H
|
||||
14
test_suite/unit_tests/Scripting/CMakeLists.txt
Normal file
14
test_suite/unit_tests/Scripting/CMakeLists.txt
Normal file
@@ -0,0 +1,14 @@
|
||||
set(TESTSUITE_SOURCES
|
||||
${TESTSUITE_SOURCES}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/TestSuite.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/testNasalSys.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/testGC.cxx
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
set(TESTSUITE_HEADERS
|
||||
${TESTSUITE_HEADERS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/testNasalSys.hxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/testGC.hxx
|
||||
PARENT_SCOPE
|
||||
)
|
||||
25
test_suite/unit_tests/Scripting/TestSuite.cxx
Normal file
25
test_suite/unit_tests/Scripting/TestSuite.cxx
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Edward d'Auvergne
|
||||
*
|
||||
* 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 "testNasalSys.hxx"
|
||||
#include "testGC.hxx"
|
||||
|
||||
// Set up the unit tests.
|
||||
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(NasalSysTests, "Unit tests");
|
||||
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(NasalGCTests, "Unit tests");
|
||||
75
test_suite/unit_tests/Scripting/testGC.cxx
Normal file
75
test_suite/unit_tests/Scripting/testGC.cxx
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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 "testGC.hxx"
|
||||
|
||||
#include "test_suite/FGTestApi/testGlobals.hxx"
|
||||
|
||||
#include <Main/globals.hxx>
|
||||
#include <Main/util.hxx>
|
||||
#include <Scripting/NasalSys.hxx>
|
||||
|
||||
#include <Main/FGInterpolator.hxx>
|
||||
|
||||
extern bool global_nasalMinimalInit;
|
||||
|
||||
// Set up function for each test.
|
||||
void NasalGCTests::setUp()
|
||||
{
|
||||
FGTestApi::setUp::initTestGlobals("NasalGC");
|
||||
|
||||
fgInitAllowedPaths();
|
||||
auto nasalNode = globals->get_props()->getNode("nasal", true);
|
||||
|
||||
globals->add_subsystem("prop-interpolator", new FGInterpolator, SGSubsystemMgr::INIT);
|
||||
|
||||
globals->get_subsystem_mgr()->bind();
|
||||
globals->get_subsystem_mgr()->init();
|
||||
|
||||
global_nasalMinimalInit = true;
|
||||
globals->add_new_subsystem<FGNasalSys>(SGSubsystemMgr::INIT);
|
||||
|
||||
globals->get_subsystem_mgr()->postinit();
|
||||
}
|
||||
|
||||
|
||||
// Clean up after each test.
|
||||
void NasalGCTests::tearDown()
|
||||
{
|
||||
global_nasalMinimalInit = false;
|
||||
FGTestApi::tearDown::shutdownTestGlobals();
|
||||
}
|
||||
|
||||
|
||||
// Test test
|
||||
void NasalGCTests::testDummy()
|
||||
{
|
||||
bool ok = FGTestApi::executeNasal(R"(
|
||||
var foo = {
|
||||
"name": "PFD-Test",
|
||||
"size": [512, 512],
|
||||
"view": [768, 1024],
|
||||
"mipmapping": 1
|
||||
};
|
||||
|
||||
globals.foo1 = foo;
|
||||
)");
|
||||
CPPUNIT_ASSERT(ok);
|
||||
}
|
||||
44
test_suite/unit_tests/Scripting/testGC.hxx
Normal file
44
test_suite/unit_tests/Scripting/testGC.hxx
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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/extensions/HelperMacros.h>
|
||||
#include <cppunit/TestFixture.h>
|
||||
|
||||
|
||||
class NasalGCTests : public CppUnit::TestFixture
|
||||
{
|
||||
// Set up the test suite.
|
||||
CPPUNIT_TEST_SUITE(NasalGCTests);
|
||||
CPPUNIT_TEST(testDummy);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
// Set up function for each test.
|
||||
void setUp();
|
||||
|
||||
// Clean up after each test.
|
||||
void tearDown();
|
||||
|
||||
// The tests.
|
||||
void testDummy();
|
||||
};
|
||||
243
test_suite/unit_tests/Scripting/testNasalSys.cxx
Normal file
243
test_suite/unit_tests/Scripting/testNasalSys.cxx
Normal file
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Edward d'Auvergne
|
||||
*
|
||||
* 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 "testNasalSys.hxx"
|
||||
|
||||
#include "test_suite/FGTestApi/testGlobals.hxx"
|
||||
#include "test_suite/FGTestApi/NavDataCache.hxx"
|
||||
|
||||
#include <simgear/structure/commands.hxx>
|
||||
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <Main/globals.hxx>
|
||||
#include <Main/util.hxx>
|
||||
|
||||
#include <Scripting/NasalSys.hxx>
|
||||
|
||||
#include <Main/FGInterpolator.hxx>
|
||||
|
||||
// Set up function for each test.
|
||||
void NasalSysTests::setUp()
|
||||
{
|
||||
FGTestApi::setUp::initTestGlobals("NasalSys");
|
||||
FGTestApi::setUp::initNavDataCache();
|
||||
|
||||
fgInitAllowedPaths();
|
||||
globals->get_props()->getNode("nasal", true);
|
||||
|
||||
globals->add_subsystem("prop-interpolator", new FGInterpolator, SGSubsystemMgr::INIT);
|
||||
|
||||
globals->get_subsystem_mgr()->bind();
|
||||
globals->get_subsystem_mgr()->init();
|
||||
|
||||
globals->add_new_subsystem<FGNasalSys>(SGSubsystemMgr::INIT);
|
||||
|
||||
globals->get_subsystem_mgr()->postinit();
|
||||
}
|
||||
|
||||
|
||||
// Clean up after each test.
|
||||
void NasalSysTests::tearDown()
|
||||
{
|
||||
FGTestApi::tearDown::shutdownTestGlobals();
|
||||
}
|
||||
|
||||
|
||||
// Test test
|
||||
void NasalSysTests::testStructEquality()
|
||||
{
|
||||
bool ok = FGTestApi::executeNasal(R"(
|
||||
var foo = {
|
||||
"name": "Bob",
|
||||
"size": [512, 512],
|
||||
"mipmapping": 1.9
|
||||
};
|
||||
|
||||
var bar = {
|
||||
"name": "Bob",
|
||||
"size": [512, 512],
|
||||
"mipmapping": 1.9
|
||||
};
|
||||
|
||||
unitTest.assert_equal(foo, bar);
|
||||
|
||||
append(bar.size, "Wowow");
|
||||
unitTest.assert(unitTest.equal(foo, bar) == 0);
|
||||
|
||||
append(foo.size, "Wowow");
|
||||
unitTest.assert_equal(foo, bar);
|
||||
|
||||
foo.wibble = 99.1;
|
||||
unitTest.assert(unitTest.equal(foo, bar) == 0);
|
||||
|
||||
bar.wibble = 99;
|
||||
unitTest.assert(unitTest.equal(foo, bar) == 0);
|
||||
bar.wibble = 99.1;
|
||||
unitTest.assert_equal(foo, bar);
|
||||
|
||||
)");
|
||||
CPPUNIT_ASSERT(ok);
|
||||
}
|
||||
|
||||
void NasalSysTests::testCommands()
|
||||
{
|
||||
auto nasalSys = globals->get_subsystem<FGNasalSys>();
|
||||
nasalSys->getAndClearErrorList();
|
||||
|
||||
fgSetInt("/foo/test", 7);
|
||||
bool ok = FGTestApi::executeNasal(R"(
|
||||
var f = func {
|
||||
var i = getprop('/foo/test');
|
||||
setprop('foo/test', i + 4);
|
||||
};
|
||||
|
||||
addcommand('do-foo', f);
|
||||
var ok = fgcommand('do-foo');
|
||||
unitTest.assert(ok);
|
||||
)");
|
||||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(11, fgGetInt("/foo/test"));
|
||||
|
||||
SGPropertyNode_ptr args(new SGPropertyNode);
|
||||
ok = globals->get_commands()->execute("do-foo", args);
|
||||
CPPUNIT_ASSERT(ok);
|
||||
CPPUNIT_ASSERT_EQUAL(15, fgGetInt("/foo/test"));
|
||||
|
||||
ok = FGTestApi::executeNasal(R"(
|
||||
var g = func { print('fail'); };
|
||||
addcommand('do-foo', g);
|
||||
)");
|
||||
|
||||
CPPUNIT_ASSERT(ok);
|
||||
auto errors = nasalSys->getAndClearErrorList();
|
||||
CPPUNIT_ASSERT_EQUAL(errors.size(), static_cast<size_t>(1));
|
||||
|
||||
// old command should still be registered and work
|
||||
ok = globals->get_commands()->execute("do-foo", args);
|
||||
CPPUNIT_ASSERT(ok);
|
||||
CPPUNIT_ASSERT_EQUAL(19, fgGetInt("/foo/test"));
|
||||
|
||||
ok = FGTestApi::executeNasal(R"(
|
||||
removecommand('do-foo');
|
||||
)");
|
||||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
ok = FGTestApi::executeNasal(R"(
|
||||
var ok = fgcommand('do-foo');
|
||||
unitTest.assert(!ok);
|
||||
)");
|
||||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
errors = nasalSys->getAndClearErrorList();
|
||||
CPPUNIT_ASSERT_EQUAL(0UL, (unsigned long) errors.size());
|
||||
|
||||
// should fail, command is removed
|
||||
ok = globals->get_commands()->execute("do-foo", args);
|
||||
CPPUNIT_ASSERT(!ok);
|
||||
CPPUNIT_ASSERT_EQUAL(19, fgGetInt("/foo/test"));
|
||||
}
|
||||
|
||||
void NasalSysTests::testAirportGhost()
|
||||
{
|
||||
auto nasalSys = globals->get_subsystem<FGNasalSys>();
|
||||
nasalSys->getAndClearErrorList();
|
||||
|
||||
bool ok = FGTestApi::executeNasal(R"(
|
||||
var apt = airportinfo('LFBD');
|
||||
var taxiways = apt.taxiways;
|
||||
unitTest.assert_equal(size(taxiways), 0);
|
||||
)");
|
||||
CPPUNIT_ASSERT(ok);
|
||||
|
||||
}
|
||||
|
||||
// https://sourceforge.net/p/flightgear/codetickets/2246/
|
||||
|
||||
void NasalSysTests::testCompileLarge()
|
||||
{
|
||||
// auto nasalSys = globals->get_subsystem<FGNasalSys>();
|
||||
// nasalSys->getAndClearErrorList();
|
||||
//
|
||||
//
|
||||
// string code = "var foo = 0;\n";
|
||||
// for (int i=0; i<14; ++i) {
|
||||
// code = code + code;
|
||||
// }
|
||||
//
|
||||
// nasalSys->parseAndRun(code);
|
||||
|
||||
// bool ok = FGTestApi::executeNasal(R"(
|
||||
//var try_compile = func(code) {
|
||||
// call(compile, [code], nil,nil,var err=[]);
|
||||
// return size(err);
|
||||
//}
|
||||
//
|
||||
//var expression = "var foo = 0;\n";
|
||||
//var code = "";
|
||||
//
|
||||
//for(var i=0;i<=10000;i+=1) {
|
||||
// code ~= expression;
|
||||
// if (try_compile(code) == 1) {
|
||||
// print("Error compiling, LOC count is:", i+1);
|
||||
// break;
|
||||
// }
|
||||
//}
|
||||
// )");
|
||||
// CPPUNIT_ASSERT(ok);
|
||||
}
|
||||
|
||||
|
||||
void NasalSysTests::testRoundFloor()
|
||||
{
|
||||
auto nasalSys = globals->get_subsystem<FGNasalSys>();
|
||||
nasalSys->getAndClearErrorList();
|
||||
|
||||
bool ok = FGTestApi::executeNasal(R"(
|
||||
unitTest.assert_equal(math.round(121266, 1000), 121000);
|
||||
unitTest.assert_equal(math.round(121.1234, 0.01), 121.12);
|
||||
unitTest.assert_equal(math.round(121266, 10), 121270);
|
||||
|
||||
unitTest.assert_equal(math.floor(121766, 1000), 121000);
|
||||
unitTest.assert_equal(math.floor(121.1299, 0.01), 121.12);
|
||||
|
||||
# floor towards lower value
|
||||
unitTest.assert_equal(math.floor(-121.1229, 0.01), -121.13);
|
||||
|
||||
# truncate towards zero
|
||||
unitTest.assert_equal(math.trunc(-121.1229, 0.01), -121.12);
|
||||
unitTest.assert_equal(math.trunc(-121.1299, 0.01), -121.12);
|
||||
)");
|
||||
CPPUNIT_ASSERT(ok);
|
||||
}
|
||||
|
||||
void NasalSysTests::testRange()
|
||||
{
|
||||
auto nasalSys = globals->get_subsystem<FGNasalSys>();
|
||||
nasalSys->getAndClearErrorList();
|
||||
|
||||
bool ok = FGTestApi::executeNasal(R"(
|
||||
unitTest.assert_equal(range(5), [0, 1, 2, 3, 4]);
|
||||
unitTest.assert_equal(range(2, 8), [2, 3, 4, 5, 6, 7]);
|
||||
unitTest.assert_equal(range(2, 10, 3), [2, 5, 8]);
|
||||
|
||||
)");
|
||||
CPPUNIT_ASSERT(ok);
|
||||
}
|
||||
58
test_suite/unit_tests/Scripting/testNasalSys.hxx
Normal file
58
test_suite/unit_tests/Scripting/testNasalSys.hxx
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Edward d'Auvergne
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _FG_NASALSYS_UNIT_TESTS_HXX
|
||||
#define _FG_NASALSYS_UNIT_TESTS_HXX
|
||||
|
||||
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
#include <cppunit/TestFixture.h>
|
||||
|
||||
|
||||
// The unit tests of the FGNasalSys subsystem.
|
||||
class NasalSysTests : public CppUnit::TestFixture
|
||||
{
|
||||
// Set up the test suite.
|
||||
CPPUNIT_TEST_SUITE(NasalSysTests);
|
||||
CPPUNIT_TEST(testStructEquality);
|
||||
CPPUNIT_TEST(testCommands);
|
||||
CPPUNIT_TEST(testAirportGhost);
|
||||
CPPUNIT_TEST(testCompileLarge);
|
||||
CPPUNIT_TEST(testRoundFloor);
|
||||
CPPUNIT_TEST(testRange);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
// Set up function for each test.
|
||||
void setUp();
|
||||
|
||||
// Clean up after each test.
|
||||
void tearDown();
|
||||
|
||||
// The tests.
|
||||
void testStructEquality();
|
||||
void testCommands();
|
||||
void testAirportGhost();
|
||||
void testCompileLarge();
|
||||
void testRoundFloor();
|
||||
void testRange();
|
||||
};
|
||||
|
||||
#endif // _FG_NASALSYS_UNIT_TESTS_HXX
|
||||
14
test_suite/unit_tests/general/CMakeLists.txt
Normal file
14
test_suite/unit_tests/general/CMakeLists.txt
Normal file
@@ -0,0 +1,14 @@
|
||||
set(TESTSUITE_SOURCES
|
||||
${TESTSUITE_SOURCES}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/TestSuite.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test-mktime.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_Views.cxx
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
set(TESTSUITE_HEADERS
|
||||
${TESTSUITE_HEADERS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test-mktime.hxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_Views.hxx
|
||||
PARENT_SCOPE
|
||||
)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user