Add coherent noise filter to the AP
This commit is contained in:
parent
0c89bacc25
commit
7b87b061da
7 changed files with 251 additions and 0 deletions
|
@ -303,6 +303,8 @@ endif (USE_DBUS)
|
||||||
##############################################################################
|
##############################################################################
|
||||||
## Qt5 setup setup
|
## Qt5 setup setup
|
||||||
if (ENABLE_QT)
|
if (ENABLE_QT)
|
||||||
|
set(CMAKE_AUTOMOC ON)
|
||||||
|
|
||||||
find_package(Qt5 5.9 COMPONENTS Widgets Gui Network Qml Quick Svg)
|
find_package(Qt5 5.9 COMPONENTS Widgets Gui Network Qml Quick Svg)
|
||||||
if (Qt5Widgets_FOUND)
|
if (Qt5Widgets_FOUND)
|
||||||
set(HAVE_QT 1)
|
set(HAVE_QT 1)
|
||||||
|
|
|
@ -201,6 +201,26 @@ public:
|
||||||
double compute( double dt, double input );
|
double compute( double dt, double input );
|
||||||
virtual void initialize( double initvalue );
|
virtual void initialize( double initvalue );
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class CoherentNoiseFilterImplementation : public DigitalFilterImplementation
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
InputValueList _amplitude;
|
||||||
|
|
||||||
|
std::vector<double> _discreteValues;
|
||||||
|
bool _absoluteVal = false;
|
||||||
|
size_t _numDiscreteValues = 1024;
|
||||||
|
|
||||||
|
bool configure(SGPropertyNode& cfg_node,
|
||||||
|
const std::string& cfg_name,
|
||||||
|
SGPropertyNode& prop_root) override;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CoherentNoiseFilterImplementation();
|
||||||
|
double compute(double dt, double input) override;
|
||||||
|
void initialize(double initvalue) override;
|
||||||
|
};
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------- */
|
/* --------------------------------------------------------------------------------- */
|
||||||
/* --------------------------------------------------------------------------------- */
|
/* --------------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
@ -703,6 +723,66 @@ bool LeadLagFilterImplementation::configure( SGPropertyNode& cfg_node,
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
CoherentNoiseFilterImplementation::CoherentNoiseFilterImplementation() : _amplitude(1.0),
|
||||||
|
_numDiscreteValues(1024)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoherentNoiseFilterImplementation::initialize(double initvalue)
|
||||||
|
{
|
||||||
|
// allocate the array one bigger so we don't need to worry about
|
||||||
|
// wrapping.bound checking the discrete +1 lookup
|
||||||
|
_discreteValues.resize(_numDiscreteValues + 1);
|
||||||
|
std::generate(_discreteValues.begin(), _discreteValues.end(), []() {
|
||||||
|
return (sg_random() * 2.0) - 1.0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static double lerp(double a, double b, double t)
|
||||||
|
{
|
||||||
|
return (a * (1.0 - t)) + (b * t);
|
||||||
|
}
|
||||||
|
|
||||||
|
double CoherentNoiseFilterImplementation::compute(double dt, double input)
|
||||||
|
{
|
||||||
|
const double a = _amplitude.get_value();
|
||||||
|
|
||||||
|
const double t = input * _numDiscreteValues;
|
||||||
|
const int i = static_cast<int>(floor(t)) % _numDiscreteValues;
|
||||||
|
const auto v0 = _discreteValues.at(i);
|
||||||
|
const auto v1 = _discreteValues.at(i + 1);
|
||||||
|
|
||||||
|
const auto weight = t - floor(t);
|
||||||
|
const double output = lerp(v0, v1, weight);
|
||||||
|
|
||||||
|
return _absoluteVal ? fabs(output) * a : output * a;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
bool CoherentNoiseFilterImplementation::configure(SGPropertyNode& cfg_node,
|
||||||
|
const std::string& cfg_name,
|
||||||
|
SGPropertyNode& prop_root)
|
||||||
|
{
|
||||||
|
if (cfg_name == "discrete-resolution") {
|
||||||
|
_numDiscreteValues = cfg_node.getIntValue();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cfg_name == "amplitude") {
|
||||||
|
_amplitude.push_back(new InputValue(prop_root, cfg_node, 1.0));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cfg_name == "absolute") {
|
||||||
|
_absoluteVal = cfg_node.getBoolValue();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* Digital Filter Component Implementation */
|
/* Digital Filter Component Implementation */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
|
@ -746,6 +826,7 @@ bool DigitalFilter::configure( SGPropertyNode& prop_root,
|
||||||
componentForge["lead-lag" ] = digitalFilterFactory<LeadLagFilterImplementation>;
|
componentForge["lead-lag" ] = digitalFilterFactory<LeadLagFilterImplementation>;
|
||||||
componentForge["integrator" ] = digitalFilterFactory<IntegratorFilterImplementation>;
|
componentForge["integrator" ] = digitalFilterFactory<IntegratorFilterImplementation>;
|
||||||
componentForge["damped-oscillation" ] = digitalFilterFactory<DampedOscillationFilterImplementation>;
|
componentForge["damped-oscillation" ] = digitalFilterFactory<DampedOscillationFilterImplementation>;
|
||||||
|
componentForge["coherent-noise"] = digitalFilterFactory<CoherentNoiseFilterImplementation>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto type = simgear::strutils::strip(cfg.getStringValue("type"));
|
const auto type = simgear::strutils::strip(cfg.getStringValue("type"));
|
||||||
|
|
12
test_suite/unit_tests/Autopilot/CMakeLists.txt
Normal file
12
test_suite/unit_tests/Autopilot/CMakeLists.txt
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
set(TESTSUITE_SOURCES
|
||||||
|
${TESTSUITE_SOURCES}
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/TestSuite.cxx
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/testDigitalFilter.cxx
|
||||||
|
PARENT_SCOPE
|
||||||
|
)
|
||||||
|
|
||||||
|
set(TESTSUITE_HEADERS
|
||||||
|
${TESTSUITE_HEADERS}
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/testDigitalFilter.hxx
|
||||||
|
PARENT_SCOPE
|
||||||
|
)
|
24
test_suite/unit_tests/Autopilot/TestSuite.cxx
Normal file
24
test_suite/unit_tests/Autopilot/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 "testDigitalFilter.hxx"
|
||||||
|
|
||||||
|
|
||||||
|
// Set up the unit tests.
|
||||||
|
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(DigitalFilterTests, "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.h>
|
||||||
|
#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);
|
||||||
|
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();
|
||||||
|
};
|
|
@ -9,6 +9,7 @@ foreach( unit_test_category
|
||||||
Instrumentation
|
Instrumentation
|
||||||
Scripting
|
Scripting
|
||||||
AI
|
AI
|
||||||
|
Autopilot
|
||||||
)
|
)
|
||||||
|
|
||||||
add_subdirectory(${unit_test_category})
|
add_subdirectory(${unit_test_category})
|
||||||
|
|
Loading…
Reference in a new issue