first commit
This commit is contained in:
@@ -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
|
||||
)
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
};
|
||||
@@ -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());
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
};
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
Reference in New Issue
Block a user