├── .clang-format ├── .github └── workflows │ └── workflow-build.yml ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── cmake └── SourcesAndHeaders.cmake ├── include └── RLSFilter.h └── test ├── CMakeLists.txt └── test_rls_filter.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: Google 3 | 4 | ... 5 | -------------------------------------------------------------------------------- /.github/workflows/workflow-build.yml: -------------------------------------------------------------------------------- 1 | name: Linux / OXS build 2 | 3 | on: 4 | push: 5 | branches: 6 | - '**' 7 | pull_request: 8 | branches: 9 | - '**' 10 | 11 | 12 | jobs: 13 | build: 14 | name: '[${{ matrix.os }}@${{ matrix.build_type }}]' 15 | runs-on: ${{ matrix.os }} 16 | strategy: 17 | matrix: 18 | build_type: [ Debug, Release ] 19 | os: [ ubuntu-latest, macOS-latest ] #windows-latest, 20 | 21 | steps: 22 | - uses: actions/checkout@master 23 | 24 | # Print environment variables to simplify development and debugging 25 | - name: Environment Variables 26 | shell: bash 27 | run: env 28 | 29 | # ============ 30 | # DEPENDENCIES 31 | # ============ 32 | - name: Googletest installer 33 | # You may pin to the exact commit or the version. 34 | # uses: MarkusJx/googletest-installer@2dbed3d0a9dc19bebe3e36773185ab9c17664c8d 35 | uses: MarkusJx/googletest-installer@v1.1 36 | 37 | - name: Dependencies [Windows] 38 | if: matrix.os == 'windows-latest' 39 | run: | 40 | vcpkg install eigen3 41 | 42 | - name: Dependencies [macOS] 43 | if: matrix.os == 'macOS-latest' 44 | run: | 45 | brew install eigen doxygen graphviz 46 | # googletest 47 | 48 | - name: Dependencies [Ubuntu] 49 | if: matrix.os == 'ubuntu-latest' 50 | run: | 51 | sudo apt-get update 52 | sudo apt-get install -y libeigen3-dev 53 | sudo apt-get install -y graphviz doxygen 54 | 55 | - name: Create Build Environment 56 | # Some projects don't allow in-source building, so create a separate build directory 57 | # We'll use this as our working directory for all subsequent commands 58 | run: cmake -E make_directory ${{github.workspace}}/build 59 | 60 | - name: Configure CMake 61 | # Use a bash shell so we can use the same syntax for environment variable 62 | # access regardless of the host operating system 63 | shell: bash 64 | working-directory: ${{github.workspace}}/build 65 | # Note the current convention is to use the -S and -B options here to specify source 66 | # and build directories, but this is only available with CMake 3.13 and higher. 67 | # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 68 | run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE 69 | 70 | - name: Build 71 | working-directory: ${{github.workspace}}/build 72 | shell: bash 73 | # Execute the build. You can specify a specific target with "--target " 74 | run: cmake --build . 75 | 76 | - name: Test 77 | working-directory: ${{github.workspace}}/build 78 | shell: bash 79 | # Execute tests defined by the CMake configuration. 80 | # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail 81 | run: ctest -C $BUILD_TYPE 82 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | 35 | # Clion dir 36 | .idea 37 | 38 | # Build dir 39 | cmake-build* 40 | 41 | # Documentation 42 | docs 43 | 44 | build/ 45 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.19) 2 | project(RLSFilter LANGUAGES CXX VERSION 0.0.0 DESCRIPTION "C++ RLS filter") 3 | set(CMAKE_CXX_STANDARD 20) 4 | 5 | set(RLSFilter_ENABLE_DOXYGEN True) 6 | 7 | include(cmake/SourcesAndHeaders.cmake) 8 | 9 | add_library(${PROJECT_NAME} INTERFACE) 10 | 11 | target_sources(${PROJECT_NAME} INTERFACE ${headers}) 12 | 13 | set_target_properties(${PROJECT_NAME} PROPERTIES LINKER_LANGUAGE CXX) 14 | set_target_properties(${PROJECT_NAME} PROPERTIES VERSION ${PROJECT_VERSION}) 15 | 16 | set_target_properties(${PROJECT_NAME} PROPERTIES PUBLIC_HEADER include) 17 | 18 | target_include_directories(${PROJECT_NAME} INTERFACE include) 19 | #target_include_directories(${PROJECT_NAME} PRIVATE src) 20 | 21 | find_package (Eigen3 3.3 REQUIRED NO_MODULE) 22 | 23 | target_link_libraries( 24 | ${PROJECT_NAME} 25 | INTERFACE 26 | Eigen3::Eigen 27 | ) 28 | 29 | if(NOT ${PROJECT_NAME}_CLANG_FORMAT_BINARY) 30 | find_program(${PROJECT_NAME}_CLANG_FORMAT_BINARY clang-format) 31 | endif() 32 | 33 | 34 | if( CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR ) 35 | add_custom_target(clang-format 36 | COMMAND ${${PROJECT_NAME}_CLANG_FORMAT_BINARY} 37 | -i ${ALL_SOURCE_FILES} ${headers} ${test_sources}) 38 | endif() 39 | 40 | enable_testing() 41 | add_subdirectory(test) 42 | 43 | if(${PROJECT_NAME}_ENABLE_DOXYGEN) 44 | set(DOXYGEN_CALLER_GRAPH YES) 45 | set(DOXYGEN_CALL_GRAPH YES) 46 | set(DOXYGEN_EXTRACT_ALL YES) 47 | set(DOXYGEN_EXTRACT_PRIVATE YES) 48 | set(DOXYGEN_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/docs) 49 | 50 | find_package(Doxygen REQUIRED dot) 51 | doxygen_add_docs(doxygen-docs ${PROJECT_SOURCE_DIR}) 52 | 53 | message( VERBOSE "Doxygen has been setup and documentation is now available.") 54 | endif() 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Davide Pilastro 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RLSFilter 2 | 3 | [![Linux / OXS build](https://github.com/dadep88/RLSFilter/actions/workflows/workflow-build.yml/badge.svg?branch=main&event=push)](https://github.com/dadep88/RLSFilter/actions/workflows/workflow-build.yml) 4 | 5 | Header-only C++ template library implementing a Recursive Least Squares (RLS) filter, managing static and dynamic implementation and different floating point base type (*float*, *double*, *long double*). 6 | 7 | ## Reference 8 | 9 | https://en.wikipedia.org/wiki/Recursive_least_squares_filter 10 | 11 | ## Dependencies 12 | 13 | * Eigen3 14 | 15 | ## Authors 16 | 17 | * **Davide Pilastro** - [@dadep88](https://github.com/dadep88) 18 | -------------------------------------------------------------------------------- /cmake/SourcesAndHeaders.cmake: -------------------------------------------------------------------------------- 1 | file(GLOB_RECURSE sources 2 | "src/*.*" 3 | ) 4 | 5 | file(GLOB_RECURSE test_sources 6 | "test/*.cpp" 7 | ) 8 | 9 | file(GLOB_RECURSE headers 10 | "include/*.*" 11 | ) 12 | 13 | -------------------------------------------------------------------------------- /include/RLSFilter.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Davide Pilastro on 2/19/21. 3 | // 4 | 5 | #ifndef RLS_DYN_MODEL_IDENT_RLS_H 6 | #define RLS_DYN_MODEL_IDENT_RLS_H 7 | 8 | #include 9 | #include 10 | using namespace Eigen; 11 | 12 | namespace rls_filter { 13 | 14 | template 15 | using EnableIfB = std::enable_if_t; 16 | 17 | /// Template class implementing a Recursive Least Square (RLS) filter, managing 18 | /// both static and dynamic implementation. 19 | /// \tparam T filter data values type 20 | /// \tparam N filter order(Static) or -1 (Dynamic) 21 | template 22 | class RLSFilter { 23 | static_assert((std::is_same::value || 24 | std::is_same_v || 25 | std::is_same::value), 26 | "T must be: long double, double or float"); 27 | 28 | public: 29 | using VectorXt = Matrix; 30 | using MatrixXt = Matrix; 31 | 32 | private: 33 | unsigned int n_; /**< Filter order */ 34 | T lam_; /**< Forgetting factor */ 35 | T lam_inv_; /**< Inverse forgetting factor */ 36 | T delta_; /**< Initial gain value of matrix P */ 37 | VectorXt w_; /**< Filter coefficients vector */ 38 | MatrixXt P_; /**< Inverse covariance error matrix */ 39 | MatrixXt P_supp_; /**< Inverse covariance error matrix */ 40 | VectorXt g_; /**< Filter gains */ 41 | T err_; /**< A priori error */ 42 | unsigned long long count_; /**< Count of filter updates */ 43 | 44 | public: 45 | /// Recursive least square filter static ctor 46 | /// \param lam - Forgetting factor 47 | /// \param delta - Initial gain value of matrix P 48 | template 0)> = 0> 49 | RLSFilter(T lam, T delta) 50 | : n_(N), 51 | lam_(1.0), 52 | lam_inv_(1.0), 53 | delta_(delta), 54 | w_(VectorXt::Zero()), 55 | P_(MatrixXt::Identity()), 56 | g_(VectorXt::Zero()), 57 | err_(0.0), 58 | count_(0) { 59 | setForgettingFactor(lam); 60 | setInitialCovarianceMatrixGain(delta); 61 | P_ *= delta_; 62 | } 63 | 64 | /// Recursive least square filter dynamic ctor 65 | /// \param n - Filter order 66 | /// \param lam - Forgetting factor 67 | /// \param delta - Initial gain value of matrix P 68 | template = 0> 69 | RLSFilter(unsigned int n, T lam, T delta) 70 | : n_(n), 71 | lam_(1.0), 72 | lam_inv_(1.0), 73 | delta_(delta), 74 | w_(VectorXt::Zero(n_)), 75 | P_(MatrixXt::Identity(n_, n_)), 76 | g_(VectorXt::Zero(n_)), 77 | err_(0.0), 78 | count_(0) { 79 | setForgettingFactor(lam); 80 | setInitialCovarianceMatrixGain(delta); 81 | P_ *= delta_; 82 | } 83 | 84 | /// Update filter with new data 85 | /// \param x - Input vector 86 | /// \param y - Output value 87 | void update(const VectorXt &x, T y) { 88 | err_ = y - predict(x); 89 | P_supp_.noalias() = P_ * lam_inv_; 90 | g_.noalias() = (P_ * x) / (lam_ + (x.transpose() * P_ * x).value()); 91 | P_.noalias() = (MatrixXt::Identity(n_, n_) - g_ * x.transpose()) * P_supp_; 92 | w_.noalias() += g_ * err_; 93 | count_++; 94 | }; 95 | 96 | /// Estimate filter output 97 | /// \param x 98 | /// \return a priori output estimate 99 | T predict(const VectorXt &x) const noexcept { return w_.transpose() * x; }; 100 | 101 | /// Set filter coefficient values 102 | /// \param w0 - Coefficient values 103 | void setEstimatedCoefficients(const VectorXt &w0) { 104 | if (w0.rows() == n_) { 105 | w_ = w0; 106 | } else { 107 | throw std::invalid_argument("Wrong initial state dimension."); 108 | } 109 | } 110 | 111 | /// Set forgetting factor value 112 | /// \param lam - Forgetting factor value 113 | void setForgettingFactor(double lam) { 114 | if ((lam > 0) && (lam <= 1.0)) { 115 | lam_ = lam; 116 | lam_inv_ = 1.0 / lam_; 117 | } else { 118 | throw std::invalid_argument( 119 | "Invalid forgetting factor (0 < lambda <= 1)."); 120 | } 121 | } 122 | 123 | /// Set initial covariance matrix gain 124 | /// \param delta 125 | void setInitialCovarianceMatrixGain(double delta) { 126 | if (delta > 0.0) { 127 | delta_ = delta; 128 | } else { 129 | throw std::invalid_argument( 130 | "Invalid covariance matrix gain factor (delta > 0)."); 131 | } 132 | } 133 | 134 | /// Get estimated filter coefficients 135 | /// \return vector of estimated filter coefficients 136 | const VectorXt &estimatedCoefficients() const noexcept { return w_; }; 137 | 138 | /// Get a priori estimate error 139 | /// \return a priori error 140 | T a_priori_err() const noexcept { return err_; }; 141 | 142 | /// Get filter gains vector 143 | /// \return filter gains vector 144 | const VectorXt &gains() const noexcept { return g_; }; 145 | 146 | /// Get filter covariance matrix 147 | /// \return filter covariance matrix 148 | const MatrixXt &P() const noexcept { return P_; }; 149 | 150 | /// Get number of performed updates 151 | /// \return update count 152 | unsigned long long count() const noexcept { return count_; }; 153 | 154 | /// Reset filter to initial values 155 | void reset() noexcept { 156 | w_ = VectorXt::Zero(n_); 157 | P_ = MatrixXt::Identity(n_, n_) * delta_; 158 | g_ = VectorXt::Zero(n_); 159 | err_ = 0.0; 160 | count_ = 0; 161 | }; 162 | }; 163 | 164 | } // namespace rls_filter 165 | 166 | #endif // RLS_DYN_MODEL_IDENT_RLS_H -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.19) 2 | #project(test_rls_filter LANGUAGES CXX) 3 | 4 | #Setup testing 5 | find_package(GTest REQUIRED) 6 | include_directories(${GTEST_INCLUDE_DIR}) 7 | 8 | #Add test cpp file 9 | add_executable("${PROJECT_NAME}_test" ${test_sources}) 10 | target_include_directories("${PROJECT_NAME}_test" PRIVATE include) 11 | 12 | #Link test executable against gtest >est_main 13 | target_link_libraries("${PROJECT_NAME}_test" ${GTEST_BOTH_LIBRARIES}) 14 | target_link_libraries("${PROJECT_NAME}_test" ${CMAKE_PROJECT_NAME}) 15 | target_link_libraries( "${PROJECT_NAME}_test" pthread) 16 | 17 | add_test(NAME ${PROJECT_NAME} COMMAND "${PROJECT_NAME}_test") -------------------------------------------------------------------------------- /test/test_rls_filter.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Davide Pilastro on 2/27/21. 3 | // 4 | 5 | #include 6 | 7 | #include "RLSFilter.h" 8 | #include "gtest/gtest.h" 9 | 10 | using namespace rls_filter; 11 | 12 | constexpr long long int ITERATIONS = 1000000; 13 | 14 | TEST(RLSFilter_StaticCtor, StadyStateEstimation_LongDouble) { 15 | long double lower_bound = 0; 16 | long double upper_bound = 10000; 17 | std::uniform_real_distribution unif(lower_bound, upper_bound); 18 | std::default_random_engine re; 19 | 20 | RLSFilter rls_filter(0.99999, 1.0); 21 | Matrix w_real(4); 22 | Matrix w0(4); 23 | w_real << 4.0, 0.5, 3.0, 1.0; 24 | w0 << 1.0, 1.0, 1.0, 1.0; 25 | 26 | rls_filter.setEstimatedCoefficients(w0); 27 | 28 | for (auto i = 0; i < ITERATIONS; i++) { 29 | Matrix x(4); 30 | x << unif(re), unif(re), unif(re), unif(re); 31 | double y = x.transpose() * w_real + unif(re) * 0.01; 32 | rls_filter.update(x, y); 33 | } 34 | ASSERT_TRUE(rls_filter.estimatedCoefficients().isApprox(w_real, 1e-3)); 35 | } 36 | 37 | TEST(RLSFilter_DynamicCtor, StadyStateEstimation_LongDouble) { 38 | long double lower_bound = 0; 39 | long double upper_bound = 10000; 40 | std::uniform_real_distribution unif(lower_bound, upper_bound); 41 | std::default_random_engine re; 42 | 43 | RLSFilter rls_filter(4, 0.99999, 1.0); 44 | Matrix w_real(4); 45 | Matrix w0(4); 46 | w_real << 4.0, 0.5, 3.0, 1.0; 47 | w0 << 1.0, 1.0, 1.0, 1.0; 48 | 49 | rls_filter.setEstimatedCoefficients(w0); 50 | 51 | for (auto i = 0; i < ITERATIONS; i++) { 52 | Matrix x(4); 53 | x << unif(re), unif(re), unif(re), unif(re); 54 | double y = x.transpose() * w_real + unif(re) * 0.01; 55 | rls_filter.update(x, y); 56 | } 57 | ASSERT_TRUE(rls_filter.estimatedCoefficients().isApprox(w_real, 1e-3)); 58 | } 59 | 60 | TEST(RLSFilter_StaticCtor, StadyStateEstimation_Double) { 61 | double lower_bound = 0; 62 | double upper_bound = 10000; 63 | std::uniform_real_distribution unif(lower_bound, upper_bound); 64 | std::default_random_engine re; 65 | 66 | RLSFilter rls_filter(0.99999, 1.0); 67 | Matrix w_real(4); 68 | Matrix w0(4); 69 | w_real << 4.0, 0.5, 3.0, 1.0; 70 | w0 << 1.0, 1.0, 1.0, 1.0; 71 | 72 | rls_filter.setEstimatedCoefficients(w0); 73 | 74 | for (auto i = 0; i < ITERATIONS; i++) { 75 | Matrix x(4); 76 | x << unif(re), unif(re), unif(re), unif(re); 77 | double y = x.transpose() * w_real + unif(re) * 0.01; 78 | rls_filter.update(x, y); 79 | } 80 | ASSERT_TRUE(rls_filter.estimatedCoefficients().isApprox(w_real, 1e-3)); 81 | } 82 | 83 | TEST(RLSFilter_DynamicCtor, StadyStateEstimation_Double) { 84 | double lower_bound = 0; 85 | double upper_bound = 10000; 86 | std::uniform_real_distribution unif(lower_bound, upper_bound); 87 | std::default_random_engine re; 88 | 89 | RLSFilter rls_filter(4, 0.99999, 1.0); 90 | Matrix w_real(4); 91 | Matrix w0(4); 92 | w_real << 4.0, 0.5, 3.0, 1.0; 93 | w0 << 1.0, 1.0, 1.0, 1.0; 94 | 95 | rls_filter.setEstimatedCoefficients(w0); 96 | 97 | for (auto i = 0; i < ITERATIONS; i++) { 98 | Matrix x(4); 99 | x << unif(re), unif(re), unif(re), unif(re); 100 | double y = x.transpose() * w_real + unif(re) * 0.01; 101 | rls_filter.update(x, y); 102 | } 103 | ASSERT_TRUE(rls_filter.estimatedCoefficients().isApprox(w_real, 1e-3)); 104 | } 105 | 106 | TEST(RLSFilter_StaticCtor, StadyStateEstimation_Float) { 107 | float lower_bound = 0; 108 | float upper_bound = 10000; 109 | std::uniform_real_distribution unif(lower_bound, upper_bound); 110 | std::default_random_engine re; 111 | 112 | RLSFilter rls_filter(0.99999, 1.0); 113 | Matrix w_real(4); 114 | Matrix w0(4); 115 | w_real << 4.0, 0.5, 3.0, 1.0; 116 | w0 << 1.0, 1.0, 1.0, 1.0; 117 | 118 | rls_filter.setEstimatedCoefficients(w0); 119 | 120 | for (auto i = 0; i < ITERATIONS; i++) { 121 | Matrix x(4); 122 | x << unif(re), unif(re), unif(re), unif(re); 123 | float y = x.transpose() * w_real + unif(re) * 0.01; 124 | rls_filter.update(x, y); 125 | } 126 | ASSERT_TRUE(rls_filter.estimatedCoefficients().isApprox(w_real, 1e-3)); 127 | } 128 | 129 | TEST(RLSFilter_DynamicCtor, StadyStateEstimation_Float) { 130 | float lower_bound = 0; 131 | float upper_bound = 10000; 132 | std::uniform_real_distribution unif(lower_bound, upper_bound); 133 | std::default_random_engine re; 134 | 135 | RLSFilter rls_filter(4, 0.99999, 1.0); 136 | Matrix w_real(4); 137 | Matrix w0(4); 138 | w_real << 4.0, 0.5, 3.0, 1.0; 139 | w0 << 1.0, 1.0, 1.0, 1.0; 140 | 141 | rls_filter.setEstimatedCoefficients(w0); 142 | 143 | for (auto i = 0; i < ITERATIONS; i++) { 144 | Matrix x(4); 145 | x << unif(re), unif(re), unif(re), unif(re); 146 | float y = x.transpose() * w_real + unif(re) * 0.01; 147 | rls_filter.update(x, y); 148 | } 149 | ASSERT_TRUE(rls_filter.estimatedCoefficients().isApprox(w_real, 1e-3)); 150 | } 151 | 152 | TEST(RLSFilter_DynamicCtor, CreateRandomOrderFilter) { 153 | int order_lower_bound = 1; 154 | int order_upper_bound = 100; 155 | std::uniform_int_distribution unif(order_lower_bound, order_upper_bound); 156 | std::default_random_engine re; 157 | 158 | int random_order = unif(re); 159 | RLSFilter rls_filter(random_order, 0.99999, 1.0); 160 | VectorXf w0 = VectorXf::Zero(random_order); 161 | ASSERT_TRUE(rls_filter.estimatedCoefficients().isApprox(w0, 1e-3)); 162 | } 163 | 164 | TEST(RLSFilter_StaticCtor, StadyStateEstimation_Float_One_Dimension) { 165 | float lower_bound = 0; 166 | float upper_bound = 10000; 167 | std::uniform_real_distribution unif(lower_bound, upper_bound); 168 | std::default_random_engine re; 169 | 170 | RLSFilter rls_filter(0.99999, 1.0); 171 | Matrix w_real; 172 | Matrix w0; 173 | w_real << 1.0; 174 | w0 << 1.0; 175 | 176 | rls_filter.setEstimatedCoefficients(w0); 177 | 178 | for (auto i = 0; i < ITERATIONS; i++) { 179 | Matrix x; 180 | x << unif(re); 181 | float y = x.transpose() * w_real + unif(re) * 0.01; 182 | rls_filter.update(x, y); 183 | } 184 | std::cout << rls_filter.estimatedCoefficients() << std::endl; 185 | ASSERT_TRUE(rls_filter.estimatedCoefficients().isApprox(w_real, 1e-2)); 186 | } --------------------------------------------------------------------------------