diff --git a/README.md b/README.md
index 84b1872130..4acce51eca 100644
--- a/README.md
+++ b/README.md
@@ -382,6 +382,8 @@ Thanks everyone!
[![License Scan](https://app.fossa.com/api/projects/git%2Bgithub.com%2FFrancoisCarouge%2FKalman.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2FFrancoisCarouge%2FKalman?ref=badge_shield)
+[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/8933/badge)](https://www.bestpractices.dev/projects/8933)
+
[![Deploy Doxygen](https://github.com/FrancoisCarouge/Kalman/actions/workflows/deploy_doxygen.yml/badge.svg)](https://github.com/FrancoisCarouge/Kalman/actions/workflows/deploy_doxygen.yml)
diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt
index 874634d98a..c0b7b65899 100644
--- a/include/CMakeLists.txt
+++ b/include/CMakeLists.txt
@@ -45,11 +45,16 @@ target_sources(
"HEADERS"
FILES
"fcarouge/algorithm.hpp"
+ "fcarouge/internal/factory.hpp"
"fcarouge/internal/format.hpp"
"fcarouge/internal/function.hpp"
- "fcarouge/internal/kalman.hpp"
"fcarouge/internal/kalman.tpp"
+ "fcarouge/internal/type.hpp"
"fcarouge/internal/utility.hpp"
+ "fcarouge/internal/x_z_p_q_r_us_ps.hpp"
+ "fcarouge/internal/x_z_u_p_q_r_f_g_ps.hpp"
+ "fcarouge/internal/x_z_u_q_r_us_ps.hpp"
+ "fcarouge/internal/x_z.hpp"
"fcarouge/kalman.hpp"
"fcarouge/utility.hpp")
install(
diff --git a/include/fcarouge/internal/factory.hpp b/include/fcarouge/internal/factory.hpp
new file mode 100644
index 0000000000..d64be60256
--- /dev/null
+++ b/include/fcarouge/internal/factory.hpp
@@ -0,0 +1,235 @@
+/* __ _ __ __ _ _
+| |/ / /\ | | | \/ | /\ | \ | |
+| ' / / \ | | | \ / | / \ | \| |
+| < / /\ \ | | | |\/| | / /\ \ | . ` |
+| . \ / ____ \| |____| | | |/ ____ \| |\ |
+|_|\_\/_/ \_\______|_| |_/_/ \_\_| \_|
+
+Kalman Filter
+Version 0.3.0
+https://github.com/FrancoisCarouge/Kalman
+
+SPDX-License-Identifier: Unlicense
+
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to */
+
+#ifndef FCAROUGE_INTERNAL_FACTORY_HPP
+#define FCAROUGE_INTERNAL_FACTORY_HPP
+
+#include "fcarouge/internal/type.hpp"
+#include "fcarouge/internal/x_z.hpp"
+#include "fcarouge/internal/x_z_p_q_r_us_ps.hpp"
+#include "fcarouge/internal/x_z_u_p_q_r_f_g_ps.hpp"
+#include "fcarouge/internal/x_z_u_q_r_us_ps.hpp"
+
+#include
+#include
+
+namespace fcarouge::internal {
+//! @todo Support arbritary order of configuration parameters?
+//! @todo Support user defined types by type name reflection? Case, naming
+//! convention insensitive?
+//! @todo Some of these overload should probably be removed to guide the user
+//! towards better initialization practices?
+template struct make_filter {
+ template
+ inline constexpr void
+ operator()([[maybe_unused]] Arguments... arguments) const
+ requires(std::same_as)
+ {
+ static_assert(false,
+ "This requested filter configuration is not yet supported. "
+ "Please, submit a pull request or feature request.");
+ }
+
+ //! @todo Rename, clean the concet: undeduced?
+ inline constexpr auto operator()() const -> x_z
+ requires(std::same_as);
+
+ inline constexpr auto operator()() const { return Filter{}; }
+
+ template
+ inline constexpr auto operator()(state x,
+ [[maybe_unused]] output_t z) const {
+ return x_z{x.value};
+ }
+
+ template
+ inline constexpr auto operator()(state x,
+ [[maybe_unused]] output_t z,
+ [[maybe_unused]] input_t u) const {
+ using kt = x_z_u_q_r_us_ps;
+ return kt{typename kt::state{x.value}};
+ }
+
+ template
+ //! @todo Simplify the require clause?
+ requires requires(ProcessUncertainty q) {
+ requires std::invocable;
+ }
+ inline constexpr auto
+ operator()(state x, [[maybe_unused]] output_t z,
+ [[maybe_unused]] input_t u,
+ estimate_uncertainty p,
+ [[maybe_unused]] process_uncertainty q,
+ [[maybe_unused]] output_uncertainty r,
+ [[maybe_unused]] state_transition f,
+ [[maybe_unused]] input_control g,
+ [[maybe_unused]] prediction_types_t pts) const {
+ using kt = x_z_u_p_q_r_f_g_ps>>;
+ return kt{typename kt::state{x.value},
+ typename kt::estimate_uncertainty{p.value},
+ typename kt::noise_process_function{q.value},
+ typename kt::output_uncertainty{r.value},
+ typename kt::transition_state_function{f.value},
+ typename kt::transition_control_function{g.value}};
+ }
+
+ template
+ inline constexpr auto
+ operator()(state x, [[maybe_unused]] output_t z,
+ [[maybe_unused]] input_t u,
+ [[maybe_unused]] update_types_t uts,
+ [[maybe_unused]] prediction_types_t pts) const {
+ using kt =
+ x_z_u_q_r_us_ps>,
+ repack_t>>;
+ return kt{typename kt::state{x.value}};
+ }
+
+ template
+ inline constexpr auto
+ operator()(state x, [[maybe_unused]] output_t z,
+ estimate_uncertainty p,
+ process_uncertainty q,
+ output_uncertainty r,
+ [[maybe_unused]] update_types_t uts,
+ [[maybe_unused]] prediction_types_t pts) const {
+ using kt = x_z_p_q_r_us_ps>,
+ repack_t>>;
+ return kt{typename kt::state{x.value},
+ typename kt::estimate_uncertainty{p.value},
+ typename kt::process_uncertainty{q.value},
+ typename kt::output_uncertainty{r.value}};
+ }
+
+ template
+ inline constexpr auto
+ operator()(state x, [[maybe_unused]] output_t z,
+ estimate_uncertainty p,
+ process_uncertainty q,
+ output_uncertainty r,
+ output_model h,
+ [[maybe_unused]] state_transition f) const {
+ using kt = x_z_p_q_r_us_ps;
+ return kt{typename kt::state{x.value},
+ typename kt::estimate_uncertainty{p.value},
+ typename kt::process_uncertainty{q.value},
+ typename kt::output_uncertainty{r.value},
+ typename kt::output_model{h.value},
+ typename kt::state_transition{f.value}};
+ }
+
+ template
+ inline constexpr auto
+ operator()(state x, [[maybe_unused]] output_t z,
+ estimate_uncertainty p,
+ output_uncertainty r) const {
+ using kt = x_z;
+ return kt{typename kt::state{x.value},
+ typename kt::estimate_uncertainty{p.value},
+ typename kt::output_uncertainty{r.value}};
+ }
+
+ template
+ inline constexpr auto
+ operator()(state x, [[maybe_unused]] output_t z,
+ estimate_uncertainty p,
+ process_uncertainty q,
+ output_uncertainty r) const {
+ using kt = x_z_p_q_r_us_ps;
+ return kt{typename kt::state{x.value},
+ typename kt::estimate_uncertainty{p.value},
+ typename kt::process_uncertainty{q.value},
+ typename kt::output_uncertainty{r.value}};
+ }
+
+ template
+ inline constexpr auto
+ operator()(state x, [[maybe_unused]] output_t z,
+ [[maybe_unused]] input_t u,
+ estimate_uncertainty p,
+ process_uncertainty q,
+ output_uncertainty r) const {
+ using kt = x_z_u_q_r_us_ps;
+ return kt{typename kt::state{x.value},
+ typename kt::estimate_uncertainty{p.value},
+ typename kt::process_uncertainty{q.value},
+ typename kt::output_uncertainty{r.value}};
+ }
+
+ template
+ inline constexpr auto
+ operator()(state x, [[maybe_unused]] output_t z,
+ [[maybe_unused]] input_t u,
+ estimate_uncertainty p,
+ process_uncertainty q,
+ output_uncertainty r,
+ [[maybe_unused]] update_types_t uts,
+ [[maybe_unused]] prediction_types_t pts) const {
+
+ using kt =
+ x_z_u_q_r_us_ps>,
+ repack_t>>;
+ return kt{typename kt::state{x.value},
+ typename kt::estimate_uncertainty{p.value},
+ typename kt::process_uncertainty{q.value},
+ typename kt::output_uncertainty{r.value}};
+ }
+};
+
+template inline constexpr make_filter filter{};
+
+template
+using filter_t = std::invoke_result_t, Arguments...>;
+} // namespace fcarouge::internal
+
+#endif // FCAROUGE_INTERNAL_FACTORY_HPP
diff --git a/include/fcarouge/internal/format.hpp b/include/fcarouge/internal/format.hpp
index cd77062a90..0bd6342696 100644
--- a/include/fcarouge/internal/format.hpp
+++ b/include/fcarouge/internal/format.hpp
@@ -45,40 +45,34 @@ For more information, please refer to */
#include
namespace fcarouge {
-template class kalman;
+template class kalman;
} // namespace fcarouge
-template
+template
// It is allowed to add template specializations for any standard library class
// template to the namespace std only if the declaration depends on at least one
// program-defined type and the specialization satisfies all requirements for
// the original template, except where such specializations are prohibited.
// NOLINTNEXTLINE(cert-dcl58-cpp)
-struct std::formatter<
- fcarouge::kalman,
- Char> {
- using kalman =
- fcarouge::kalman;
-
+struct std::formatter, Char> {
constexpr auto parse(std::basic_format_parse_context &parse_context) {
return parse_context.begin();
}
//! @todo P2585 may be useful in simplifying and standardizing the support.
template
- auto format(const kalman &filter,
+ auto format(const fcarouge::kalman &filter,
std::basic_format_context &format_context) const
-> OutputIt {
format_context.advance_to(
format_to(format_context.out(), R"({{"f": {}, )", filter.f()));
- if constexpr (fcarouge::internal::has_input_control_method) {
+ if constexpr (fcarouge::has_input_control) {
format_context.advance_to(
format_to(format_context.out(), R"("g": {}, )", filter.g()));
}
- if constexpr (fcarouge::internal::has_output_model_method) {
+ if constexpr (fcarouge::has_output_model) {
format_context.advance_to(
format_to(format_context.out(), R"("h": {}, )", filter.h()));
}
@@ -86,8 +80,9 @@ struct std::formatter<
format_context.advance_to(format_to(
format_context.out(), R"("k": {}, "p": {}, )", filter.k(), filter.p()));
- {
- constexpr auto end{fcarouge::internal::repack_s};
+ if constexpr (fcarouge::internal::has_prediction_types) {
+ constexpr auto end{
+ fcarouge::internal::repack_s};
constexpr decltype(end) begin{0};
constexpr decltype(end) next{1};
fcarouge::internal::for_constexpr(
@@ -98,19 +93,24 @@ struct std::formatter<
});
}
- format_context.advance_to(format_to(format_context.out(),
- R"("q": {}, "r": {}, "s": {}, )",
- filter.q(), filter.r(), filter.s()));
+ if constexpr (fcarouge::has_process_uncertainty) {
+ format_context.advance_to(
+ format_to(format_context.out(), R"("q": {}, )", filter.q()));
+ }
+
+ format_context.advance_to(format_to(
+ format_context.out(), R"("r": {}, "s": {}, )", filter.r(), filter.s()));
//! @todo Generalize out internal method concept when MSVC has better
//! if-constexpr-requires support.
- if constexpr (fcarouge::internal::has_input_method) {
+ if constexpr (fcarouge::has_input) {
format_context.advance_to(
format_to(format_context.out(), R"("u": {}, )", filter.u()));
}
- {
- constexpr auto end{fcarouge::internal::repack_s};
+ if constexpr (fcarouge::internal::has_update_types) {
+ constexpr auto end{
+ fcarouge::internal::repack_s};
constexpr decltype(end) begin{0};
constexpr decltype(end) next{1};
fcarouge::internal::for_constexpr(
diff --git a/include/fcarouge/internal/kalman.tpp b/include/fcarouge/internal/kalman.tpp
index 2ee63a1758..5f590709ca 100644
--- a/include/fcarouge/internal/kalman.tpp
+++ b/include/fcarouge/internal/kalman.tpp
@@ -40,101 +40,95 @@ For more information, please refer to */
#define FCAROUGE_INTERNAL_KALMAN_TPP
namespace fcarouge {
-template
+
+template
+template
+inline constexpr kalman::kalman(Arguments... arguments)
+ : filter(internal::filter(arguments...)) {}
+
+template
[[nodiscard("The returned state estimate column vector X is unexpectedly "
"discarded.")]] inline constexpr auto
-kalman::x() const
- -> const state & {
+kalman::x() const -> const state & {
return filter.x;
}
-template
+template
[[nodiscard("The returned state estimate column vector X is unexpectedly "
"discarded.")]] inline constexpr auto
-kalman::x() -> state & {
+kalman::x() -> state & {
return filter.x;
}
-template
-inline constexpr void
-kalman::x(
- const auto &value, const auto &...values) {
+template
+inline constexpr void kalman::x(const auto &value,
+ const auto &...values) {
filter.x = std::move(state{value, values...});
}
-template
+template
[[nodiscard("The returned observation column vector Z is unexpectedly "
"discarded.")]] inline constexpr auto
-kalman::z() const
- -> const output & {
+kalman::z() const -> const output & {
return filter.z;
}
-template
+template
[[nodiscard("The returned control column vector U is unexpectedly "
"discarded.")]] inline constexpr const auto &
-kalman::u() const
- requires(has_input)
+kalman::u() const
+ requires(has_input)
{
return filter.u;
}
-template
+template
[[nodiscard("The returned estimated covariance matrix P is unexpectedly "
"discarded.")]] inline constexpr auto
-kalman::p() const
- -> const estimate_uncertainty & {
+kalman::p() const -> const estimate_uncertainty & {
return filter.p;
}
-template
+template
[[nodiscard("The returned estimated covariance matrix P is unexpectedly "
"discarded.")]] inline constexpr auto
-kalman::p()
- -> estimate_uncertainty & {
+kalman::p() -> estimate_uncertainty & {
return filter.p;
}
-template
-inline constexpr void
-kalman::p(
- const auto &value, const auto &...values) {
+template
+inline constexpr void kalman::p(const auto &value,
+ const auto &...values) {
filter.p = std::move(estimate_uncertainty{value, values...});
}
-template
+template
[[nodiscard("The returned process noise covariance matrix Q is unexpectedly "
- "discarded.")]] inline constexpr auto
-kalman::q() const
- -> const process_uncertainty & {
+ "discarded.")]] inline constexpr const auto &
+kalman::q() const
+ requires(has_process_uncertainty)
+{
return filter.q;
}
-template
+template
[[nodiscard("The returned process noise covariance matrix Q is unexpectedly "
- "discarded.")]] inline constexpr auto
-kalman::q()
- -> process_uncertainty & {
+ "discarded.")]] inline constexpr auto &
+kalman::q()
+ requires(has_process_uncertainty)
+{
return filter.q;
}
-template
-inline constexpr void
-kalman::q(
- const auto &value, const auto &...values) {
- if constexpr (std::is_convertible_v) {
- filter.q = std::move(process_uncertainty{value, values...});
+template
+inline constexpr void kalman::q(const auto &value,
+ const auto &...values)
+ requires(has_process_uncertainty)
+{
+ if constexpr (std::is_convertible_v) {
+ filter.q =
+ std::move(typename Filter::process_uncertainty{value, values...});
} else {
using noise_process_function = decltype(filter.noise_process_q);
filter.noise_process_q =
@@ -142,31 +136,26 @@ kalman::q(
}
}
-template
+template
[[nodiscard("The returned observation noise covariance matrix R is "
"unexpectedly discarded.")]] inline constexpr auto
-kalman::r() const
- -> const output_uncertainty & {
+kalman::r() const -> const output_uncertainty & {
return filter.r;
}
-template
+template
[[nodiscard("The returned observation noise covariance matrix R is "
"unexpectedly discarded.")]] inline constexpr auto
-kalman::r()
- -> output_uncertainty & {
+kalman::r() -> output_uncertainty & {
return filter.r;
}
-template
-inline constexpr void
-kalman::r(
- const auto &value, const auto &...values) {
- if constexpr (std::is_convertible_v) {
- filter.r = std::move(output_uncertainty{value, values...});
+template
+inline constexpr void kalman::r(const auto &value,
+ const auto &...values) {
+ if constexpr (std::is_convertible_v) {
+ filter.r = std::move(typename Filter::output_uncertainty{value, values...});
} else {
using noise_observation_function = decltype(filter.noise_observation_r);
filter.noise_observation_r =
@@ -174,31 +163,26 @@ kalman::r(
}
}
-template
+template
[[nodiscard("The returned state transition matrix F is unexpectedly "
"discarded.")]] inline constexpr auto
-kalman::f() const
- -> const state_transition & {
+kalman::f() const -> const state_transition & {
return filter.f;
}
-template
+template
[[nodiscard("The returned state transition matrix F is unexpectedly "
"discarded.")]] inline constexpr auto
-kalman::f()
- -> state_transition & {
+kalman::f() -> state_transition & {
return filter.f;
}
-template
-inline constexpr void
-kalman::f(
- const auto &value, const auto &...values) {
- if constexpr (std::is_convertible_v) {
- filter.f = std::move(state_transition{value, values...});
+template
+inline constexpr void kalman::f(const auto &value,
+ const auto &...values) {
+ if constexpr (std::is_convertible_v) {
+ filter.f = std::move(typename Filter::state_transition{value, values...});
} else {
using transition_state_function = decltype(filter.transition_state_f);
filter.transition_state_f =
@@ -206,40 +190,32 @@ kalman::f(
}
}
-template
+template
[[nodiscard("The returned observation transition matrix H is unexpectedly "
"discarded.")]] inline constexpr const auto &
-kalman::h() const
- requires(has_output_model)
+kalman::h() const
+ requires(has_output_model)
{
return filter.h;
}
-template
+template
[[nodiscard("The returned observation transition matrix H is unexpectedly "
"discarded.")]] inline constexpr auto &
-kalman::h()
- requires(has_output_model)
+kalman::h()
+ requires(has_output_model)
{
return filter.h;
}
-template
-inline constexpr void
-kalman::h(
- const auto &value, const auto &...values)
- requires(has_output_model)
+template
+inline constexpr void kalman::h(const auto &value,
+ const auto &...values)
+ requires(has_output_model)
{
- if constexpr (std::is_convertible_v<
- decltype(value),
- typename kalman::output_model>) {
- filter.h = std::move(
- typename kalman::output_model{value, values...});
+ if constexpr (std::is_convertible_v) {
+ filter.h = std::move(typename Filter::output_model{value, values...});
} else {
using observation_state_function = decltype(filter.observation_state_h);
filter.observation_state_h =
@@ -247,112 +223,88 @@ kalman::h(
}
}
-template
+template
[[nodiscard("The returned control transition matrix G is unexpectedly "
"discarded.")]] inline constexpr const auto &
-kalman::g() const
- requires(has_input_control)
+kalman::g() const
+ requires(has_input_control)
{
return filter.g;
}
-template
+template
[[nodiscard("The returned control transition matrix G is unexpectedly "
"discarded.")]] inline constexpr auto &
-kalman::g()
- requires(has_input_control)
+kalman::g()
+ requires(has_input_control)
{
return filter.g;
}
-template
-inline constexpr void
-kalman::g(
- const auto &value, const auto &...values)
- requires(has_input_control)
+template
+inline constexpr void kalman::g(const auto &value,
+ const auto &...values)
+ requires(has_input_control)
{
using transition_control_function = decltype(filter.transition_control_g);
filter.transition_control_g =
std::move(transition_control_function{value, values...});
}
-template
+template
[[nodiscard("The returned gain matrix K is unexpectedly "
"discarded.")]] inline constexpr auto
-kalman::k() const
- -> const gain & {
+kalman::k() const -> const gain & {
return filter.k;
}
-template
+template
[[nodiscard("The returned innovation column vector Y is unexpectedly "
"discarded.")]] inline constexpr auto
-kalman::y() const
- -> const innovation & {
+kalman::y() const -> const innovation & {
return filter.y;
}
-template
+template
[[nodiscard("The returned innovation uncertainty matrix S is unexpectedly "
"discarded.")]] inline constexpr auto
-kalman::s() const
- -> const innovation_uncertainty & {
+kalman::s() const -> const innovation_uncertainty & {
return filter.s;
}
-template