1
0
Fork 0

UNit-tests for the NavRadio

Still needs ILS and GS tests, but starting to be useful already.
This commit is contained in:
James Turner 2018-10-10 09:26:06 +01:00
parent 1e636ce4cf
commit c7b28a1960
9 changed files with 335 additions and 6 deletions

View file

@ -37,22 +37,29 @@ AudioIdent::AudioIdent( const std::string & fx_name, const double interval_secs,
void AudioIdent::init()
{
auto soundManager = globals->get_subsystem<SGSoundMgr>();
if (!soundManager)
return; // sound disabled
_timer = 0.0;
_ident = "";
_running = false;
_sgr = globals->get_subsystem<SGSoundMgr>()->find("avionics", true);
_sgr = soundManager->find("avionics", true);
_sgr->tie_to_listener();
}
void AudioIdent::stop()
{
if( _sgr->exists( _fx_name ) )
if (_sgr && _sgr->exists( _fx_name ) )
_sgr->stop( _fx_name );
_running = false;
}
void AudioIdent::start()
{
if (!_sgr)
return;
_timer = _interval;
_sgr->play_once(_fx_name);
_running = true;
@ -60,10 +67,11 @@ void AudioIdent::start()
void AudioIdent::setVolumeNorm( double volumeNorm )
{
if (!_sgr)
return;
SG_CLAMP_RANGE(volumeNorm, 0.0, 1.0);
SGSoundSample *sound = _sgr->find( _fx_name );
if ( sound != NULL ) {
sound->set_volume( volumeNorm );
}
@ -71,6 +79,9 @@ void AudioIdent::setVolumeNorm( double volumeNorm )
void AudioIdent::setIdent( const std::string & ident, double volumeNorm )
{
if (!_sgr)
return;
// Signal may flicker very frequently (due to our realistic newnavradio...).
// Avoid recreating identical sound samples all the time, instead turn off
// volume when signal is lost, and save the most recent sample.

View file

@ -15,7 +15,7 @@
#include <simgear/structure/event_mgr.hxx>
#include <simgear/timing/timestamp.hxx>
#include <simgear/math/sg_geodesy.hxx>
namespace FGTestApi {
@ -59,6 +59,12 @@ void initTestGlobals(const std::string& testName)
} // End of namespace setUp.
void setPosition(const SGGeod& g)
{
globals->get_props()->setDoubleValue("position/latitude-deg", g.getLatitudeDeg());
globals->get_props()->setDoubleValue("position/longitude-deg", g.getLongitudeDeg());
globals->get_props()->setDoubleValue("position/altitude-ft", g.getElevationFt());
}
namespace tearDown {

View file

@ -3,6 +3,8 @@
#include <string>
class SGGeod;
namespace FGTestApi {
namespace setUp {
@ -11,6 +13,7 @@ void initTestGlobals(const std::string& testName);
} // End of namespace setUp.
void setPosition(const SGGeod& g);
namespace tearDown {

View file

@ -6,6 +6,7 @@ foreach( unit_test_category
Input
Main
Navaids
Instrumentation
Scripting
)

View file

@ -20,7 +20,7 @@
#include "test_hidinput.hxx"
#include "test_suite/helpers/globals.hxx"
#include "test_suite/FGTestApi/globals.hxx"
#include <simgear/misc/test_macros.hxx>

View file

@ -0,0 +1,12 @@
set(TESTSUITE_SOURCES
${TESTSUITE_SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/TestSuite.cxx
${CMAKE_CURRENT_SOURCE_DIR}/test_navRadio.cxx
PARENT_SCOPE
)
set(TESTSUITE_HEADERS
${TESTSUITE_HEADERS}
${CMAKE_CURRENT_SOURCE_DIR}/test_navRadio.hxx
PARENT_SCOPE
)

View file

@ -0,0 +1,23 @@
/*
* 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_navRadio.hxx"
// Set up the unit tests.
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(NavRadioTests, "Unit tests");

View file

@ -0,0 +1,206 @@
#include "test_navRadio.hxx"
#include <memory>
#include <cstring>
#include "test_suite/FGTestApi/globals.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);
}
}
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/navtest", 6.0);
node->setDoubleValue("frequencies/selected-mhz", 113.8);
SGGeod pos = SGGeod::fromDegFt(-3.352780, 55.499199, 20000);
setPositionAndStabilise(r.get(), pos);
CPPUNIT_ASSERT(!strcmp("TLA", node->getStringValue("nav-id")));
CPPUNIT_ASSERT_EQUAL(true, node->getBoolValue("in-range"));
}
void NavRadioTests::testCDIDeflection()
{
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/navtest", 6.0);
node->setDoubleValue("frequencies/selected-mhz", 113.55);
node->setDoubleValue("radials/selected-deg", 25);
FGPositioned::TypeFilter f{FGPositioned::VOR};
FGNavRecordRef nav = fgpositioned_cast<FGNavRecord>(FGPositioned::findClosestWithIdent("MCT", SGGeod::fromDeg(-2.26, 53.3), &f));
// twist of MCT is 5.0, so we use a bearing of 20 here, not 25
SGGeod posOnRadial = SGGeodesy::direct(nav->geod(), 20.0, 10 * SG_NM_TO_METER);
posOnRadial.setElevationFt(10000);
setPositionAndStabilise(r.get(), posOnRadial);
CPPUNIT_ASSERT(!strcmp("MCT", node->getStringValue("nav-id")));
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, node->getDoubleValue("heading-needle-deflection"), 0.01);
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, node->getDoubleValue("heading-needle-deflection-norm"), 0.01);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, node->getDoubleValue("signal-quality-norm"), 0.01);
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, node->getDoubleValue("crosstrack-error-m"), 0.01);
CPPUNIT_ASSERT(node->getBoolValue("from-flag"));
CPPUNIT_ASSERT(!node->getBoolValue("to-flag"));
// move off course
SGGeod posOffRadial = SGGeodesy::direct(nav->geod(), 15.0, 20 * SG_NM_TO_METER); // 5 degress off
posOffRadial.setElevationFt(12000);
setPositionAndStabilise(r.get(), posOffRadial);
CPPUNIT_ASSERT_DOUBLES_EQUAL(5.0, node->getDoubleValue("heading-needle-deflection"), 0.1);
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.5, node->getDoubleValue("heading-needle-deflection-norm"), 0.01);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, node->getDoubleValue("signal-quality-norm"), 0.01);
double xtkE = sin(5.0 * SG_DEGREES_TO_RADIANS) * 20 * SG_NM_TO_METER;
CPPUNIT_ASSERT_DOUBLES_EQUAL(xtkE, node->getDoubleValue("crosstrack-error-m"), 50.0);
CPPUNIT_ASSERT(node->getBoolValue("from-flag"));
CPPUNIT_ASSERT(!node->getBoolValue("to-flag"));
posOffRadial = SGGeodesy::direct(nav->geod(), 28.0, 30 * SG_NM_TO_METER); // 8 degress off
posOffRadial.setElevationFt(16000);
setPositionAndStabilise(r.get(), posOffRadial);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, node->getDoubleValue("signal-quality-norm"), 0.01);
CPPUNIT_ASSERT_DOUBLES_EQUAL(-8.0, node->getDoubleValue("heading-needle-deflection"), 0.1);
CPPUNIT_ASSERT_DOUBLES_EQUAL(-0.8, node->getDoubleValue("heading-needle-deflection-norm"), 0.01);
xtkE = sin(-8.0 * SG_DEGREES_TO_RADIANS) * 30 * SG_NM_TO_METER;
CPPUNIT_ASSERT_DOUBLES_EQUAL(xtkE, node->getDoubleValue("crosstrack-error-m"), 50.0);
// move more than ten degrees off course
posOffRadial = SGGeodesy::direct(nav->geod(), 33.0, 40 * SG_NM_TO_METER); // 13 degress off
posOffRadial.setElevationFt(16000);
setPositionAndStabilise(r.get(), posOffRadial);
// pegged to full deflection
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, node->getDoubleValue("signal-quality-norm"), 0.01);
CPPUNIT_ASSERT_DOUBLES_EQUAL(-10.0, node->getDoubleValue("heading-needle-deflection"), 0.1);
CPPUNIT_ASSERT_DOUBLES_EQUAL(-1.0, node->getDoubleValue("heading-needle-deflection-norm"), 0.01);
// cross track error is computed based on true deflection, not clamped
xtkE = sin(-13.0 * SG_DEGREES_TO_RADIANS) * 40 * SG_NM_TO_METER;
CPPUNIT_ASSERT_DOUBLES_EQUAL(xtkE, node->getDoubleValue("crosstrack-error-m"), 50.0);
CPPUNIT_ASSERT(node->getBoolValue("from-flag"));
CPPUNIT_ASSERT(!node->getBoolValue("to-flag"));
// try on the TO side of the station
// let's use Perth VOR, but the Australian one to check southern
// hemisphere operation
node->setDoubleValue("frequencies/selected-mhz", 113.7);
node->setDoubleValue("radials/selected-deg", 42.0); // twist is -2.0
CPPUNIT_ASSERT(!strcmp("113.70", node->getStringValue("frequencies/selected-mhz-fmt")));
auto perthVOR = fgpositioned_cast<FGNavRecord>(
FGPositioned::findClosestWithIdent("PH", SGGeod::fromDeg(115.95, -31.9), &f));
SGGeod p = SGGeodesy::direct(perthVOR->geod(), 220.0, 20 * SG_NM_TO_METER); // on the reciprocal radial
p.setElevationFt(12000);
setPositionAndStabilise(r.get(), p);
CPPUNIT_ASSERT(!strcmp("PH", node->getStringValue("nav-id")));
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, node->getDoubleValue("signal-quality-norm"), 0.01);
CPPUNIT_ASSERT_DOUBLES_EQUAL(40.0, node->getDoubleValue("heading-deg"), 0.5);
// actual radial has twist subtracted
CPPUNIT_ASSERT_DOUBLES_EQUAL(222.0, node->getDoubleValue("radials/actual-deg"), 0.01);
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, node->getDoubleValue("heading-needle-deflection"), 0.1);
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, node->getDoubleValue("heading-needle-deflection-norm"), 0.01);
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, node->getDoubleValue("crosstrack-error-m"), 50.0);
CPPUNIT_ASSERT(!node->getBoolValue("from-flag"));
CPPUNIT_ASSERT(node->getBoolValue("to-flag"));
// off course on the TO side
p = SGGeodesy::direct(perthVOR->geod(), 227.0, 100 * SG_NM_TO_METER);
p.setElevationFt(18000);
setPositionAndStabilise(r.get(), p);
CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, node->getDoubleValue("signal-quality-norm"), 0.01);
CPPUNIT_ASSERT_DOUBLES_EQUAL(47.0, node->getDoubleValue("heading-deg"), 1);
CPPUNIT_ASSERT_DOUBLES_EQUAL(229.0, node->getDoubleValue("radials/actual-deg"), 0.01);
CPPUNIT_ASSERT_DOUBLES_EQUAL(7.0, node->getDoubleValue("heading-needle-deflection"), 0.1);
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.7, node->getDoubleValue("heading-needle-deflection-norm"), 0.01);
xtkE = sin(7.0 * SG_DEGREES_TO_RADIANS) * 100 * SG_NM_TO_METER;
CPPUNIT_ASSERT_DOUBLES_EQUAL(xtkE, node->getDoubleValue("crosstrack-error-m"), 50.0);
CPPUNIT_ASSERT(!node->getBoolValue("from-flag"));
CPPUNIT_ASSERT(node->getBoolValue("to-flag"));
}
void NavRadioTests::testILSBasic()
{
// also check ILS back course
}
void NavRadioTests::testGS()
{
}
void NavRadioTests::testILSFalseCourse()
{
// also GS false lobes
}
void NavRadioTests::testILSPaired()
{
// EGPH and countless more
}
void NavRadioTests::testILSAdjacentPaired()
{
// eg KJFK
}

View file

@ -0,0 +1,67 @@
/*
* 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(testCDIDeflection);
CPPUNIT_TEST(testILSBasic);
CPPUNIT_TEST(testGS);
CPPUNIT_TEST(testILSFalseCourse);
CPPUNIT_TEST(testILSPaired);
CPPUNIT_TEST(testILSAdjacentPaired);
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();
// The tests.
void testBasic();
void testCDIDeflection();
void testILSBasic();
void testGS();
void testILSFalseCourse();
void testILSPaired();
void testILSAdjacentPaired();
};
#endif // _FG_NAVRADIO_UNIT_TESTS_HXX