Skip to content

Commit ac614f3

Browse files
[facade] encapsulate implementation details behind facade (#23)
1 parent 36b60df commit ac614f3

12 files changed

+934
-283
lines changed

.github/workflows/verify_code_static_analysis_cppcheck.yml

+11-3
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,17 @@ jobs:
1212
steps:
1313
- name: Checkout
1414
uses: actions/checkout@v3
15-
- name: Update
16-
run: sudo apt update
1715
- name: Install
18-
run: sudo apt install cppcheck
16+
run: |
17+
( cd /tmp ; mkdir cppcheck ;
18+
git clone --depth 1 https://github.com/danmar/cppcheck.git ;
19+
( cd cppcheck ;
20+
mkdir build ) ;
21+
( cd cppcheck/build ;
22+
cmake .. ;
23+
cmake --build . ;
24+
sudo make install ) )
25+
- name: Version
26+
run: cppcheck --version
1927
- name: Check
2028
run: cppcheck --verbose --suppressions-list=.cppcheck --error-exitcode=1 --enable=all -I include .

README.md

+92-21
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,9 @@ using kalman = fcarouge::kalman<double>;
5454

5555
kalman k;
5656

57-
k.state_x = 60;
58-
k.estimate_uncertainty_p = 225;
59-
k.transition_observation_h = [] { return kalman::observation{ 1 }; };
60-
k.noise_observation_r = [] {
61-
return kalman::observation_noise_uncertainty{ 25 };
62-
};
57+
k.x(60.);
58+
k.p(225.);
59+
k.r(25.);
6360

6461
k.observe(48.54);
6562
```
@@ -76,35 +73,50 @@ const double gravitational_acceleration{ -9.8 }; // m.s^-2
7673
const std::chrono::milliseconds delta_time{ 250 };
7774
kalman k;
7875

79-
k.state_x = { 0, 0 };
80-
k.estimate_uncertainty_p =
81-
kalman::estimate_uncertainty{ { 500, 0 }, { 0, 500 } };
82-
k.transition_state_f = [](const std::chrono::milliseconds &delta_time) {
76+
k.x(0, 0);
77+
k.p(kalman::estimate_uncertainty{ { 500, 0 }, { 0, 500 } });
78+
k.f([](const std::chrono::milliseconds &delta_time) {
8379
const auto dt{ std::chrono::duration<double>(delta_time).count() };
8480
return kalman::state_transition{ { 1, dt }, { 0, 1 } };
85-
};
86-
k.noise_process_q = [](const std::chrono::milliseconds &delta_time) {
81+
});
82+
k.q([](const std::chrono::milliseconds &delta_time) {
8783
const auto dt{ std::chrono::duration<double>(delta_time).count() };
88-
return kalman::process_noise_uncertainty{
84+
return kalman::process_uncertainty{
8985
{ 0.1 * 0.1 * dt * dt * dt * dt / 4, 0.1 * 0.1 * dt * dt * dt / 2 },
9086
{ 0.1 * 0.1 * dt * dt * dt / 2, 0.1 * 0.1 * dt * dt }
9187
};
92-
};
93-
k.transition_control_g = [](const std::chrono::milliseconds &delta_time) {
88+
});
89+
k.g([](const std::chrono::milliseconds &delta_time) {
9490
const auto dt{ std::chrono::duration<double>(delta_time).count() };
95-
return kalman::control{ 0.0313, dt };
96-
};
97-
k.transition_observation_h = [] { return kalman::observation{ { 1, 0 } }; };
98-
k.noise_observation_r = [] {
99-
return kalman::observation_noise_uncertainty{ 400 };
100-
};
91+
return kalman::input_control{ 0.0313, dt };
92+
});
93+
k.h(1, 0);
94+
k.r(400);
10195

10296
k.predict(delta_time, gravitational_acceleration);
10397
k.observe(-32.40 );
10498
k.predict(delta_time, 39.72);
10599
k.observe(-11.1);
106100
```
107101

102+
# Library
103+
104+
- [Kalman Filter for C++](#kalman-filter-for-c)
105+
- [Continuous Integration & Deployment Actions](#continuous-integration--deployment-actions)
106+
- [Examples](#examples)
107+
- [One-Dimensional](#one-dimensional)
108+
- [Multi-Dimensional](#multi-dimensional)
109+
- [Library](#library)
110+
- [Motivation](#motivation)
111+
- [Resources](#resources)
112+
- [Class fcarouge::kalman](#class-fcarougekalman)
113+
- [Template Parameters](#template-parameters)
114+
- [Member Types](#member-types)
115+
- [Member Functions](#member-functions)
116+
- [Characteristics](#characteristics)
117+
- [Modifiers](#modifiers)
118+
- [License](#license)
119+
108120
# Motivation
109121

110122
Kalman filters can be difficult to learn, use, and implement. Users often need fair algebra, domain, and software knowledge. Inadequacy leads to incorrectness, underperformance, and a big ball of mud.
@@ -121,6 +133,65 @@ Awesome resources to learn about Kalman filters:
121133
- [KalmanFilter.NET](https://www.kalmanfilter.net) by Alex Becker.
122134
- [Kalman and Bayesian Filters in Python](https://github.com/rlabbe/Kalman-and-Bayesian-Filters-in-Python) by Roger Labbe.
123135

136+
# Class fcarouge::kalman
137+
138+
Defined in header [fcarouge/kalman.hpp](include/fcarouge/kalman.hpp)
139+
140+
```cpp
141+
template <typename State, typename Output = State, typename Input = State,
142+
template <typename> typename Transpose = internal::transpose,
143+
template <typename> typename Symmetrize = internal::symmetrize,
144+
template <typename, typename> typename Divide = internal::divide,
145+
template <typename> typename Identity = internal::identity,
146+
typename... PredictionArguments>
147+
class kalman
148+
```
149+
150+
## Template Parameters
151+
152+
## Member Types
153+
154+
| Member Type | Definition |
155+
| --- | --- |
156+
| `state` | Type of the state estimate vector X. |
157+
| `output` | Type of the observation vector Z, also known as Y. |
158+
| `input` | Type of the control vector U. |
159+
| `estimate_uncertainty` | Type of the estimated covariance matrix P, also known as Σ. |
160+
| `process_uncertainty` | Type of the process noise covariance matrix Q. |
161+
| `output_uncertainty` | Type of the observation, measurement noise covariance matrix R. |
162+
| `state_transition` | Type of the state transition matrix F, also known as Φ or A. |
163+
| `output_model` | Type of the observation transition matrix H, also known as C. |
164+
| `input_control` | Type of the control transition matrix G, also known as B. |
165+
166+
## Member Functions
167+
168+
| Member Function | Definition |
169+
| --- | --- |
170+
| `(constructor)` | Constructs the filter. |
171+
| `(destructor)` | Destructs the filter. |
172+
| `operator=` | Assigns values to the filter. |
173+
174+
### Characteristics
175+
176+
| Modifier | Definition |
177+
| --- | --- |
178+
| `x` | Manages the state estimate vector. |
179+
| `z` | Manages the observation vector. |
180+
| `u` | Manages the control vector. |
181+
| `p` | Manages the estimated covariance matrix. |
182+
| `q` | Manages the process noise covariance matrix. |
183+
| `r` | Manages the observation, measurement noise covariance matrix. |
184+
| `f` | Manages the state transition matrix. |
185+
| `h` | Manages the observation transition matrix. |
186+
| `g` | Manages the control transition matrix. |
187+
188+
### Modifiers
189+
190+
| Modifier | Definition |
191+
| --- | --- |
192+
| `observe` | Updates the estimates with the outcome of a measurement. |
193+
| `predict` | Produces estimates of the state variables and uncertainties. |
194+
124195
# License
125196
126197
<img align="right" src="http://opensource.org/trademarks/opensource/OSI-Approved-License-100x137.png">

include/fcarouge/internal/kalman.hpp

+210
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
/*_ __ _ __ __ _ _
2+
| |/ / /\ | | | \/ | /\ | \ | |
3+
| ' / / \ | | | \ / | / \ | \| |
4+
| < / /\ \ | | | |\/| | / /\ \ | . ` |
5+
| . \ / ____ \| |____| | | |/ ____ \| |\ |
6+
|_|\_\/_/ \_\______|_| |_/_/ \_\_| \_|
7+
8+
Kalman Filter for C++
9+
Version 0.1.0
10+
https://github.com/FrancoisCarouge/Kalman
11+
12+
SPDX-License-Identifier: Unlicense
13+
14+
This is free and unencumbered software released into the public domain.
15+
16+
Anyone is free to copy, modify, publish, use, compile, sell, or
17+
distribute this software, either in source code form or as a compiled
18+
binary, for any purpose, commercial or non-commercial, and by any
19+
means.
20+
21+
In jurisdictions that recognize copyright laws, the author or authors
22+
of this software dedicate any and all copyright interest in the
23+
software to the public domain. We make this dedication for the benefit
24+
of the public at large and to the detriment of our heirs and
25+
successors. We intend this dedication to be an overt act of
26+
relinquishment in perpetuity of all present and future rights to this
27+
software under copyright law.
28+
29+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
30+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
31+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
32+
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
33+
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
34+
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
35+
OTHER DEALINGS IN THE SOFTWARE.
36+
37+
For more information, please refer to <https://unlicense.org> */
38+
39+
#ifndef FCAROUGE_INTERNAL_KALMAN_HPP
40+
#define FCAROUGE_INTERNAL_KALMAN_HPP
41+
42+
//! @file
43+
//! @brief The main Kalman filter class.
44+
45+
#include "kalman_equation.hpp"
46+
#include "kalman_operator.hpp"
47+
48+
#include <functional>
49+
#include <type_traits>
50+
51+
namespace fcarouge::internal
52+
{
53+
template <typename State, typename Output = State, typename Input = State,
54+
template <typename> typename Transpose = transpose,
55+
template <typename> typename Symmetrize = symmetrize,
56+
template <typename, typename> typename Divide = divide,
57+
template <typename> typename Identity = identity,
58+
typename... PredictionArguments>
59+
struct kalman {
60+
//! @name Public Member Types
61+
//! @{
62+
63+
//! @brief Type of the state estimate vector X.
64+
using state = State;
65+
66+
//! @brief Type of the observation vector Z.
67+
//!
68+
//! @details Also known as Y.
69+
using output = Output;
70+
71+
//! @brief Type of the control vector U.
72+
using input = Input;
73+
74+
//! @brief Type of the estimated covariance matrix P.
75+
//!
76+
//! @details Also known as Σ.
77+
using estimate_uncertainty =
78+
std::invoke_result_t<Divide<State, State>, State, State>;
79+
80+
//! @brief Type of the process noise covariance matrix Q.
81+
using process_uncertainty =
82+
std::invoke_result_t<Divide<State, State>, State, State>;
83+
84+
//! @brief Type of the observation, measurement noise covariance matrix R.
85+
using output_uncertainty =
86+
std::invoke_result_t<Divide<Output, Output>, Output, Output>;
87+
88+
//! @brief Type of the state transition matrix F.
89+
//!
90+
//! @details Also known as Φ or A.
91+
using state_transition =
92+
std::invoke_result_t<Divide<State, State>, State, State>;
93+
94+
//! @brief Type of the observation transition matrix H.
95+
//!
96+
//! @details Also known as C.
97+
using output_model =
98+
std::invoke_result_t<Divide<Output, State>, Output, State>;
99+
100+
//! @brief Type of the control transition matrix G.
101+
//!
102+
//! @details Also known as B.
103+
using input_control =
104+
std::invoke_result_t<Divide<State, Input>, State, Input>;
105+
106+
//! @}
107+
108+
//! @name Public Member Variables
109+
//! @{
110+
111+
//! @brief The state estimate vector x.
112+
state x{};
113+
114+
//! @brief The estimate uncertainty, covariance matrix P.
115+
//!
116+
//! @details The estimate uncertainty, covariance is also known as Σ.
117+
estimate_uncertainty p{ Identity<estimate_uncertainty>()() };
118+
119+
output_model h{ Identity<output_model>()() };
120+
121+
output_uncertainty r{ Identity<output_uncertainty>()() };
122+
123+
state_transition f{ Identity<state_transition>()() };
124+
125+
process_uncertainty q{};
126+
127+
input_control g{};
128+
129+
//! @}
130+
131+
//! @name Public Member Function Objects
132+
//! @{
133+
134+
//! @brief Compute observation transition H matrix.
135+
//!
136+
//! @details The observation transition H is also known as C.
137+
std::function<output_model()> transition_observation_h{ [this] {
138+
return h;
139+
} };
140+
141+
//! @brief Compute observation noise R matrix.
142+
std::function<output_uncertainty()> noise_observation_r{ [this] {
143+
return r;
144+
} };
145+
146+
//! @brief Compute state transition F matrix.
147+
//!
148+
//! @details The state transition F matrix is also known as Φ or A.
149+
//! For non-linear system, or extended filter, F is the Jacobian of the state
150+
//! transition function. F = ∂fj/∂xi that is each row i contains the the
151+
//! derivatives of the state transition function for every element j in the
152+
//! state vector x.
153+
std::function<state_transition(const PredictionArguments &...)>
154+
transition_state_f{ [this](const PredictionArguments &...arguments) {
155+
static_cast<void>((arguments, ...));
156+
return f;
157+
} };
158+
159+
//! @brief Compute process noise Q matrix.
160+
std::function<process_uncertainty(const PredictionArguments &...)>
161+
noise_process_q{ [this](const PredictionArguments &...arguments) {
162+
static_cast<void>((arguments, ...));
163+
return q;
164+
} };
165+
166+
//! @brief Compute control transition G matrix.
167+
std::function<input_control(const PredictionArguments &...)>
168+
transition_control_g{ [this](const PredictionArguments &...arguments) {
169+
static_cast<void>((arguments, ...));
170+
return g;
171+
} };
172+
173+
//! @brief State transition function.
174+
//!
175+
//! @details
176+
// Add prediction arguments?
177+
std::function<state(const state &, const state_transition &)> predict_state =
178+
[](const state &x, const state_transition &f) { return state{ f * x }; };
179+
180+
//! @}
181+
182+
//! @name Public Member Functions
183+
//! @{
184+
185+
inline constexpr void observe(const auto &...output_z)
186+
{
187+
h = transition_observation_h();
188+
r = noise_observation_r();
189+
const auto z{ output{ output_z... } };
190+
internal::observe<Transpose, Symmetrize, Divide, Identity>(x, p, h, r, z);
191+
}
192+
193+
inline constexpr void predict(const PredictionArguments &...arguments,
194+
const auto &...input_u)
195+
{
196+
// use member variables
197+
const auto ff{ predict_state };
198+
f = transition_state_f(arguments...);
199+
q = noise_process_q(arguments...);
200+
g = transition_control_g(arguments...);
201+
const auto u{ input{ input_u... } };
202+
internal::predict<Transpose, Symmetrize>(x, p, ff, f, q, g, u);
203+
}
204+
205+
//! @}
206+
};
207+
208+
} // namespace fcarouge::internal
209+
210+
#endif // FCAROUGE_INTERNAL_KALMAN_HPP

0 commit comments

Comments
 (0)