| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // This file is part of INSTINCT, the INS Toolkit for Integrated | ||
| 2 | // Navigation Concepts and Training by the Institute of Navigation of | ||
| 3 | // the University of Stuttgart, Germany. | ||
| 4 | // | ||
| 5 | // This Source Code Form is subject to the terms of the Mozilla Public | ||
| 6 | // License, v. 2.0. If a copy of the MPL was not distributed with this | ||
| 7 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
| 8 | |||
| 9 | /// @file Math.hpp | ||
| 10 | /// @brief Simple Math functions | ||
| 11 | /// @author T. Topp (topp@ins.uni-stuttgart.de) | ||
| 12 | /// @author N. Stahl (HiWi: Elliptical integral) | ||
| 13 | /// @date 2023-07-04 | ||
| 14 | |||
| 15 | #pragma once | ||
| 16 | |||
| 17 | #include <concepts> | ||
| 18 | #include <cstdint> | ||
| 19 | #include <optional> | ||
| 20 | #include <type_traits> | ||
| 21 | #include <Eigen/Core> | ||
| 22 | #include <Eigen/Dense> | ||
| 23 | #include <gcem.hpp> | ||
| 24 | #include <fmt/format.h> | ||
| 25 | |||
| 26 | #include "util/Assert.h" | ||
| 27 | |||
| 28 | namespace NAV::math | ||
| 29 | { | ||
| 30 | |||
| 31 | /// @brief Calculates the factorial of an unsigned integer | ||
| 32 | /// @param[in] n Unsigned integer | ||
| 33 | /// @return The factorial of 'n' | ||
| 34 | uint64_t factorial(uint64_t n); | ||
| 35 | |||
| 36 | /// @brief Round the number to the specified amount of digits | ||
| 37 | /// @param[in] value Value to round | ||
| 38 | /// @param[in] digits Amount of digits | ||
| 39 | /// @return The rounded value | ||
| 40 | template<std::floating_point T> | ||
| 41 | 372830 | constexpr T round(const T& value, size_t digits) | |
| 42 | { | ||
| 43 | 372830 | auto factor = std::pow(10, digits); | |
| 44 | 372830 | return std::round(value * factor) / factor; | |
| 45 | } | ||
| 46 | |||
| 47 | /// @brief Round the number to the specified amount of significant digits | ||
| 48 | /// @param[in] value Value to round | ||
| 49 | /// @param[in] digits Amount of digits | ||
| 50 | /// @return The rounded value | ||
| 51 | template<std::floating_point T> | ||
| 52 | 40 | constexpr T roundSignificantDigits(T value, size_t digits) | |
| 53 | { | ||
| 54 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 39 times.
|
40 | if (value == 0.0) { return 0.0; } |
| 55 | // LOG_DEBUG("value = {:.13e} --> Round to {} digits", value, digits); | ||
| 56 | 39 | auto absVal = gcem::abs(value); | |
| 57 | 39 | auto log10 = static_cast<int32_t>(gcem::log10(absVal)); | |
| 58 |
6/6✓ Branch 0 taken 33 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 18 times.
✓ Branch 3 taken 15 times.
✓ Branch 4 taken 6 times.
✓ Branch 5 taken 12 times.
|
39 | auto exp = log10 + (log10 > 0 || (log10 == 0 && absVal >= 1.0)); |
| 59 | 39 | auto fac = static_cast<T>(digits) - static_cast<T>(exp); | |
| 60 | // LOG_DEBUG(" log10 = {}, exp = {}, fac = {}", log10, exp, fac); | ||
| 61 | 39 | auto factor = static_cast<T>(gcem::pow(10.0, fac)); | |
| 62 | // LOG_DEBUG(" factor = {:.0e} --> value * factor = {}", factor, value * factor); | ||
| 63 | // LOG_DEBUG(" round = {} --> ... / factor = {}", gcem::round(value * factor), gcem::round(value * factor) / factor); | ||
| 64 | 39 | return static_cast<T>(gcem::round(value * factor) / factor); | |
| 65 | } | ||
| 66 | |||
| 67 | /// @brief Interprets the input integer with certain amount of Bits as Output type. Takes care of sign extension | ||
| 68 | /// @tparam Out Output type | ||
| 69 | /// @tparam Bits Size of the input data | ||
| 70 | /// @tparam In Input data type (needs to be bigger than the amount of Bits) | ||
| 71 | /// @param[in] in Number as two's complement, with the sign bit (+ or -) occupying the MSB | ||
| 72 | /// @return Output type | ||
| 73 | template<std::integral Out, size_t Bits, std::integral In> | ||
| 74 | 3099 | constexpr Out interpretAs(In in) | |
| 75 | { | ||
| 76 | static_assert(Bits < sizeof(In) * 8); | ||
| 77 | static_assert(Bits < sizeof(Out) * 8); | ||
| 78 | |||
| 79 | 3099 | constexpr size_t N = sizeof(Out) * 8 - Bits; | |
| 80 | 3099 | return static_cast<Out>(static_cast<Out>((in & static_cast<In>(gcem::pow(2, Bits) - 1)) << N) >> N); | |
| 81 | } | ||
| 82 | |||
| 83 | /// @brief Calculates the skew symmetric matrix of the given vector. | ||
| 84 | /// This is needed to perform the cross product with a scalar product operation | ||
| 85 | /// @tparam Derived Derived Eigen Type | ||
| 86 | /// @param[in] a The vector | ||
| 87 | /// @return Skew symmetric matrix | ||
| 88 | /// @note See Groves (2013) equation (2.50) | ||
| 89 | template<typename Derived> | ||
| 90 | 2983507 | Eigen::Matrix<typename Derived::Scalar, 3, 3> skewSymmetricMatrix(const Eigen::MatrixBase<Derived>& a) | |
| 91 | { | ||
| 92 |
1/2✓ Branch 1 taken 2928057 times.
✗ Branch 2 not taken.
|
2983507 | INS_ASSERT_USER_ERROR(a.cols() == 1, "Given Eigen Object must be a vector"); |
| 93 |
1/2✓ Branch 1 taken 2928848 times.
✗ Branch 2 not taken.
|
2981683 | INS_ASSERT_USER_ERROR(a.rows() == 3, "Given Vector must have 3 Rows"); |
| 94 | |||
| 95 | 2982474 | Eigen::Matrix<typename Derived::Scalar, 3, 3> skewMat; | |
| 96 |
5/10✓ Branch 1 taken 2926234 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2924313 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2928088 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 2926590 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 2930030 times.
✗ Branch 14 not taken.
|
2977983 | skewMat << 0, -a(2), a(1), |
| 97 |
5/10✓ Branch 1 taken 2927148 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2931628 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2930533 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 2927784 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 2931099 times.
✗ Branch 14 not taken.
|
2983656 | a(2), 0, -a(0), |
| 98 |
5/10✓ Branch 1 taken 2928573 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2931724 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2929457 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 2932529 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 2932283 times.
✗ Branch 14 not taken.
|
2984725 | -a(1), a(0), 0; |
| 99 | |||
| 100 | 2985034 | return skewMat; | |
| 101 | } | ||
| 102 | |||
| 103 | /// @brief Calculates the square of a skew symmetric matrix of the given vector. | ||
| 104 | /// @tparam Derived Derived Eigen Type | ||
| 105 | /// @param[in] a The vector | ||
| 106 | /// @return Square of skew symmetric matrix | ||
| 107 | template<typename Derived> | ||
| 108 | ✗ | Eigen::Matrix<typename Derived::Scalar, 3, 3> skewSymmetricMatrixSquared(const Eigen::MatrixBase<Derived>& a) | |
| 109 | { | ||
| 110 | ✗ | INS_ASSERT_USER_ERROR(a.cols() == 1, "Given Eigen Object must be a vector"); | |
| 111 | ✗ | INS_ASSERT_USER_ERROR(a.rows() == 3, "Given Vector must have 3 Rows"); | |
| 112 | |||
| 113 | ✗ | Eigen::Matrix<typename Derived::Scalar, 3, 3> skewMat2; | |
| 114 | ✗ | skewMat2 << std::pow(a(2), 2) + std::pow(a(1), 2), a(0) * a(1), a(0) * a(2), | |
| 115 | ✗ | a(0) * a(1), std::pow(a(2), 2) + std::pow(a(0), 2), a(1) * a(2), | |
| 116 | ✗ | a(0) * a(2), a(1) * a(2), std::pow(a(0), 2) + std::pow(a(1), 2); | |
| 117 | |||
| 118 | ✗ | return skewMat2; | |
| 119 | } | ||
| 120 | |||
| 121 | /// @brief Calculates the matrix exponential map of the given vector. | ||
| 122 | /// @tparam Derived Derived Eigen Type | ||
| 123 | /// @param[in] v The vector | ||
| 124 | /// @return The matrix exponential map | ||
| 125 | template<typename Derived> | ||
| 126 | Eigen::Matrix<typename Derived::Scalar, 3, 3> expMapMatrix(const Eigen::MatrixBase<Derived>& v) | ||
| 127 | { | ||
| 128 | INS_ASSERT_USER_ERROR(v.cols() == 1, "Given Eigen Object must be a vector"); | ||
| 129 | INS_ASSERT_USER_ERROR(v.rows() == 3, "Given Vector must have 3 Rows"); | ||
| 130 | |||
| 131 | return math::skewSymmetricMatrix(v).exp(); | ||
| 132 | } | ||
| 133 | |||
| 134 | /// @brief Calculates the quaternionic exponential map of the given vector. | ||
| 135 | /// @tparam Derived Derived Eigen Type | ||
| 136 | /// @param[in] v The vector | ||
| 137 | /// @return The quaternionic exponential map | ||
| 138 | template<typename Derived> | ||
| 139 | 299982 | Eigen::Quaternion<typename Derived::Scalar> expMapQuat(const Eigen::MatrixBase<Derived>& v) | |
| 140 | { | ||
| 141 |
1/2✓ Branch 1 taken 149991 times.
✗ Branch 2 not taken.
|
299982 | INS_ASSERT_USER_ERROR(v.cols() == 1, "Given Eigen Object must be a vector"); |
| 142 |
1/2✓ Branch 1 taken 149991 times.
✗ Branch 2 not taken.
|
299982 | INS_ASSERT_USER_ERROR(v.rows() == 3, "Given Vector must have 3 Rows"); |
| 143 | |||
| 144 |
2/4✓ Branch 1 taken 149991 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 149991 times.
✗ Branch 5 not taken.
|
299982 | Eigen::Vector3<typename Derived::Scalar> omega = 0.5 * v; |
| 145 |
1/2✓ Branch 1 taken 149991 times.
✗ Branch 2 not taken.
|
299982 | auto omegaNorm = omega.norm(); |
| 146 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 149991 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
299982 | if (omegaNorm < 1e-9) { return Eigen::Quaternion<typename Derived::Scalar>::Identity(); } |
| 147 |
3/6✓ Branch 1 taken 149991 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 149991 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 149991 times.
✗ Branch 8 not taken.
|
299982 | Eigen::Vector3<typename Derived::Scalar> quatVec = omega / omegaNorm * std::sin(omegaNorm); |
| 148 | |||
| 149 |
4/8✓ Branch 1 taken 149991 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 149991 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 149991 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 149991 times.
✗ Branch 11 not taken.
|
299982 | return { std::cos(omegaNorm), quatVec.x(), quatVec.y(), quatVec.z() }; |
| 150 | } | ||
| 151 | |||
| 152 | /// @brief Calculates the right Jacobian of SO(3) which relates additive increments in the tangent space to multiplicative increments applied on the right-hand side | ||
| 153 | /// @param[in] phi Vector applied on the right side | ||
| 154 | /// @return Right Jacobian J_r | ||
| 155 | template<typename Derived> | ||
| 156 | ✗ | [[nodiscard]] Eigen::Matrix3<typename Derived::Scalar> J_r(const Eigen::MatrixBase<Derived>& phi) | |
| 157 | { | ||
| 158 | ✗ | INS_ASSERT_USER_ERROR(phi.cols() == 1, "Given Eigen Object must be a vector"); | |
| 159 | ✗ | INS_ASSERT_USER_ERROR(phi.rows() == 3, "Given Vector must have 3 Rows"); | |
| 160 | |||
| 161 | ✗ | auto phiNorm = phi.norm(); | |
| 162 | ✗ | auto phiNorm2 = phiNorm * phiNorm; | |
| 163 | ✗ | auto phiNorm3 = phiNorm2 * phiNorm; | |
| 164 | return Eigen::Matrix3<typename Derived::Scalar>::Identity() | ||
| 165 | ✗ | - (1.0 - std::cos(phiNorm)) / phiNorm2 * skewSymmetricMatrix(phi) | |
| 166 | ✗ | + (phiNorm - std::sin(phiNorm)) / phiNorm3 * skewSymmetricMatrixSquared(phi); | |
| 167 | } | ||
| 168 | |||
| 169 | /// @brief Calculates the secant of a value (sec(x) = csc(pi/2 - x) = 1 / cos(x)) | ||
| 170 | template<std::floating_point T> | ||
| 171 | ✗ | T sec(const T& x) | |
| 172 | { | ||
| 173 | ✗ | return 1.0 / std::cos(x); | |
| 174 | } | ||
| 175 | |||
| 176 | /// @brief Calculates the cosecant of a value (csc(x) = sec(pi/2 - x) = 1 / sin(x)) | ||
| 177 | template<std::floating_point T> | ||
| 178 | 156269 | T csc(const T& x) | |
| 179 | { | ||
| 180 | 156269 | return 1.0 / std::sin(x); | |
| 181 | } | ||
| 182 | |||
| 183 | /// @brief Returns the sign of the given value | ||
| 184 | /// @param[in] val Value to get the sign from | ||
| 185 | /// @return Sign of the given value | ||
| 186 | template<typename T> | ||
| 187 | 1343753 | int sgn(const T& val) | |
| 188 | { | ||
| 189 | 1343753 | return (T(0) < val) - (val < T(0)); | |
| 190 | } | ||
| 191 | |||
| 192 | /// @brief Calculates the state transition matrix 𝚽 limited to specified order in 𝐅𝜏ₛ | ||
| 193 | /// @param[in] X Matrix | ||
| 194 | /// @param[in] order The order of the Taylor polynom to calculate | ||
| 195 | /// @note See \cite Groves2013 Groves, ch. 3.2.3, eq. 3.34, p. 98 | ||
| 196 | template<typename Derived> | ||
| 197 | typename Derived::PlainObject expm(const Eigen::MatrixBase<Derived>& X, size_t order) | ||
| 198 | { | ||
| 199 | INS_ASSERT_USER_ERROR(X.rows() == X.cols(), "Matrix exponential calculation only possible for n x n matrices"); | ||
| 200 | |||
| 201 | typename Derived::PlainObject e_X; | ||
| 202 | |||
| 203 | if constexpr (Derived::RowsAtCompileTime == Eigen::Dynamic) | ||
| 204 | { | ||
| 205 | e_X = Eigen::MatrixBase<Derived>::Identity(X.rows(), X.cols()); | ||
| 206 | } | ||
| 207 | else | ||
| 208 | { | ||
| 209 | e_X = Eigen::MatrixBase<Derived>::Identity(); | ||
| 210 | } | ||
| 211 | typename Derived::PlainObject Xpower = X; | ||
| 212 | for (size_t i = 1; i <= order; i++) | ||
| 213 | { | ||
| 214 | e_X += Xpower / static_cast<double>(math::factorial(i)); | ||
| 215 | |||
| 216 | if (i < order) | ||
| 217 | { | ||
| 218 | Xpower *= X; | ||
| 219 | } | ||
| 220 | } | ||
| 221 | |||
| 222 | return e_X; | ||
| 223 | } | ||
| 224 | |||
| 225 | /// @brief Find (L^T D L)-decomposition of Q-matrix via outer product method | ||
| 226 | /// @param[in] Qmatrix Symmetric positive definite matrix to be factored | ||
| 227 | /// @return L - Factor matrix (strict lower triangular) | ||
| 228 | /// @return D - Vector with entries of the diagonal matrix | ||
| 229 | /// @note See \cite deJonge1996 de Jonge 1996, Algorithm FMFAC5 | ||
| 230 | /// @attention Consider using NAV::math::LtDLdecomp_choleskyFact() because it is faster by up to a factor 10 | ||
| 231 | template<typename Derived> | ||
| 232 | std::optional<std::pair<Eigen::Matrix<typename Derived::Scalar, Derived::RowsAtCompileTime, Derived::ColsAtCompileTime>, | ||
| 233 | Eigen::Vector<typename Derived::Scalar, Derived::RowsAtCompileTime>>> | ||
| 234 | 1 | LtDLdecomp_outerProduct(const Eigen::MatrixBase<Derived>& Qmatrix) | |
| 235 | { | ||
| 236 | using Eigen::seq; | ||
| 237 | |||
| 238 | 1 | auto n = Qmatrix.rows(); | |
| 239 |
2/4✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
|
1 | Eigen::Matrix<typename Derived::Scalar, Derived::RowsAtCompileTime, Derived::ColsAtCompileTime> Q = Qmatrix.template triangularView<Eigen::Lower>(); |
| 240 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | Eigen::Matrix<typename Derived::Scalar, Derived::RowsAtCompileTime, Derived::ColsAtCompileTime> L; |
| 241 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | Eigen::Vector<typename Derived::Scalar, Derived::RowsAtCompileTime> D; |
| 242 | |||
| 243 | if constexpr (Derived::RowsAtCompileTime == Eigen::Dynamic) | ||
| 244 | { | ||
| 245 |
2/4✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
|
1 | L = Eigen::Matrix<typename Derived::Scalar, Eigen::Dynamic, Eigen::Dynamic>::Zero(n, n); |
| 246 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | D.setZero(n); |
| 247 | } | ||
| 248 | else | ||
| 249 | { | ||
| 250 | L = Eigen::Matrix<typename Derived::Scalar, Derived::RowsAtCompileTime, Derived::ColsAtCompileTime>::Zero(); | ||
| 251 | D.setZero(); | ||
| 252 | } | ||
| 253 | |||
| 254 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
|
4 | for (Eigen::Index i = n - 1; i >= 0; i--) |
| 255 | { | ||
| 256 |
2/4✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
|
3 | D(i) = Q(i, i); |
| 257 |
2/4✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
|
3 | if (Q(i, i) <= 0.0) { return {}; } |
| 258 |
7/14✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 3 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 3 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 3 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 3 times.
✗ Branch 20 not taken.
|
3 | L(i, seq(0, i)) = Q(i, seq(0, i)) / std::sqrt(Q(i, i)); |
| 259 | |||
| 260 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
|
6 | for (Eigen::Index j = 0; j <= i - 1; j++) |
| 261 | { | ||
| 262 |
7/14✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 3 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 3 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 3 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 3 times.
✗ Branch 20 not taken.
|
3 | Q(j, seq(0, j)) -= L(i, seq(0, j)) * L(i, j); |
| 263 | } | ||
| 264 |
4/8✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 3 times.
✗ Branch 11 not taken.
|
3 | L(i, seq(0, i)) /= L(i, i); |
| 265 | } | ||
| 266 | |||
| 267 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | return std::make_pair(L, D); |
| 268 | 1 | } | |
| 269 | |||
| 270 | /// @brief Find (L^T D L)-decomposition of Q-matrix via a backward Cholesky factorization in a bordering method formulation | ||
| 271 | /// @param[in] Q Symmetric positive definite matrix to be factored | ||
| 272 | /// @return L - Factor matrix (strict lower triangular) | ||
| 273 | /// @return D - Vector with entries of the diagonal matrix | ||
| 274 | /// @note See \cite deJonge1996 de Jonge 1996, Algorithm FMFAC6 | ||
| 275 | template<typename Derived> | ||
| 276 | std::optional<std::pair<Eigen::Matrix<typename Derived::Scalar, Derived::RowsAtCompileTime, Derived::ColsAtCompileTime>, | ||
| 277 | Eigen::Vector<typename Derived::Scalar, Derived::RowsAtCompileTime>>> | ||
| 278 | 2423 | LtDLdecomp_choleskyFact(const Eigen::MatrixBase<Derived>& Q) | |
| 279 | { | ||
| 280 | using Eigen::seq; | ||
| 281 | |||
| 282 | 2423 | auto n = Q.rows(); | |
| 283 |
2/4✓ Branch 1 taken 1213 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1213 times.
✗ Branch 5 not taken.
|
2423 | typename Derived::PlainObject L = Q.template triangularView<Eigen::Lower>(); |
| 284 |
1/2✓ Branch 1 taken 1213 times.
✗ Branch 2 not taken.
|
2423 | Eigen::Vector<typename Derived::Scalar, Derived::RowsAtCompileTime> D; |
| 285 | 2423 | double cmin = 1; | |
| 286 | |||
| 287 |
1/2✓ Branch 1 taken 1206 times.
✗ Branch 2 not taken.
|
2409 | if constexpr (Derived::RowsAtCompileTime == Eigen::Dynamic) { D.setZero(n); } |
| 288 |
1/2✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
|
14 | else { D.setZero(); } |
| 289 | |||
| 290 |
2/2✓ Branch 0 taken 32377 times.
✓ Branch 1 taken 1213 times.
|
67060 | for (Eigen::Index j = n - 1; j >= 0; j--) |
| 291 | { | ||
| 292 |
2/2✓ Branch 0 taken 448444 times.
✓ Branch 1 taken 32377 times.
|
959299 | for (Eigen::Index i = n - 1; i >= j + 1; i--) |
| 293 | { | ||
| 294 |
8/16✓ Branch 1 taken 448444 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 448444 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 448444 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 448444 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 448444 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 448444 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 448444 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 448444 times.
✗ Branch 23 not taken.
|
894662 | L(i, j) = (L(i, j) - L(seq(i + 1, n - 1), j).dot(L(seq(i + 1, n - 1), i))) / L(i, i); |
| 295 | } | ||
| 296 |
6/12✓ Branch 1 taken 32377 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 32377 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 32377 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 32377 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 32377 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 32377 times.
✗ Branch 17 not taken.
|
64637 | double t = L(j, j) - L(seq(j + 1, n - 1), j).dot(L(seq(j + 1, n - 1), j)); |
| 297 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 32377 times.
|
64637 | if (t <= 0.0) { return {}; } |
| 298 |
1/2✓ Branch 1 taken 32377 times.
✗ Branch 2 not taken.
|
64637 | double c = t / L(j, j); |
| 299 | 64637 | cmin = std::min(c, cmin); | |
| 300 |
1/2✓ Branch 1 taken 32377 times.
✗ Branch 2 not taken.
|
64637 | L(j, j) = std::sqrt(t); |
| 301 | } | ||
| 302 |
2/2✓ Branch 0 taken 32377 times.
✓ Branch 1 taken 1213 times.
|
67060 | for (Eigen::Index i = 0; i < n; i++) |
| 303 | { | ||
| 304 |
4/8✓ Branch 1 taken 32377 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 32377 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 32377 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 32377 times.
✗ Branch 11 not taken.
|
64637 | L.row(i).leftCols(i) /= L(i, i); |
| 305 |
2/4✓ Branch 1 taken 32377 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 32377 times.
✗ Branch 5 not taken.
|
64637 | D(i) = std::pow(L(i, i), 2.0); |
| 306 |
1/2✓ Branch 1 taken 32377 times.
✗ Branch 2 not taken.
|
64637 | L(i, i) = 1; |
| 307 | } | ||
| 308 | |||
| 309 |
1/2✓ Branch 1 taken 1213 times.
✗ Branch 2 not taken.
|
2423 | return std::make_pair(L, D); |
| 310 | 2409 | } | |
| 311 | |||
| 312 | /// @brief Calculates the squared norm of the vector and matrix | ||
| 313 | /// | ||
| 314 | /// \anchor eq-squaredNorm \f{equation}{ \label{eq:eq-squaredNorm} | ||
| 315 | /// ||\mathbf{\dots}||^2_{\mathbf{Q}} = (\dots)^T \mathbf{Q}^{-1} (\dots) | ||
| 316 | /// \f} | ||
| 317 | /// @param a Vector | ||
| 318 | /// @param Q Covariance matrix of the vector | ||
| 319 | /// @return Squared norm | ||
| 320 | template<typename DerivedA, typename DerivedQ> | ||
| 321 | 8 | typename DerivedA::Scalar squaredNormVectorMatrix(const Eigen::MatrixBase<DerivedA>& a, const Eigen::MatrixBase<DerivedQ>& Q) | |
| 322 | { | ||
| 323 | static_assert(DerivedA::ColsAtCompileTime == Eigen::Dynamic || DerivedA::ColsAtCompileTime == 1); | ||
| 324 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | INS_ASSERT_USER_ERROR(a.cols() == 1, "Parameter 'a' has to be a vector"); |
| 325 |
1/2✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | INS_ASSERT_USER_ERROR(a.rows() == Q.rows(), "Parameter 'a' and 'Q' need to have same size"); |
| 326 |
1/2✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | INS_ASSERT_USER_ERROR(Q.cols() == Q.rows(), "Parameter 'Q' needs to be quadratic"); |
| 327 | |||
| 328 |
5/10✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 8 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 8 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 8 times.
✗ Branch 14 not taken.
|
8 | return a.transpose() * Q.inverse() * a; |
| 329 | } | ||
| 330 | |||
| 331 | /// @brief Calculates the cumulative distribution function (CDF) of the standard normal distribution | ||
| 332 | /// | ||
| 333 | /// \anchor eq-normalDistCDF \f{equation}{ \label{eq:eq-normalDistCDF} | ||
| 334 | /// \Phi(x) = \int\displaylimits_{-\infty}^x \frac{1}{\sqrt{2\pi}} \exp{\left(-\frac{1}{2} v^2\right)} \text{d}v | ||
| 335 | /// \f} | ||
| 336 | /// which can be expressed with the error function | ||
| 337 | /// \anchor eq-normalDistCDF-erf \f{equation}{ \label{eq:eq-normalDistCDF-erf} | ||
| 338 | /// \Phi(x) = \frac{1}{2} \left[ 1 + \text{erf}{\left(\frac{x}{\sqrt{2}}\right)} \right] | ||
| 339 | /// \f} | ||
| 340 | /// Using the property | ||
| 341 | /// \anchor eq-erf-minus \f{equation}{ \label{eq:eq-erf-minus} | ||
| 342 | /// \text{erf}{\left( -x \right)} = -\text{erf}{\left( x \right)} | ||
| 343 | /// \f} | ||
| 344 | /// and the complementary error function | ||
| 345 | /// \anchor eq-erfc \f{equation}{ \label{eq:eq-erfc} | ||
| 346 | /// \text{erfc}{\left( x \right)} = 1 - \text{erf}{\left( x \right)} | ||
| 347 | /// \f} | ||
| 348 | /// we can simplify equation \eqref{eq-normalDistCDF-erf} to | ||
| 349 | /// \anchor eq-normalDistCDF-erfc \f{equation}{ \label{eq:eq-normalDistCDF-erfc} | ||
| 350 | /// \begin{aligned} | ||
| 351 | /// \Phi(x) &= \frac{1}{2} \left[ 1 - \text{erf}{\left(-\frac{x}{\sqrt{2}}\right)} \right] \\ | ||
| 352 | /// &= \frac{1}{2} \text{erfc}{\left(-\frac{x}{\sqrt{2}}\right)} | ||
| 353 | /// \end{aligned} | ||
| 354 | /// \f} | ||
| 355 | /// | ||
| 356 | /// @param value Value to calculate the CDF for | ||
| 357 | double normalCDF(double value); | ||
| 358 | |||
| 359 | /// @brief Returns the inverse square root of a matrix | ||
| 360 | /// @param matrix Matrix to use | ||
| 361 | template<typename Derived> | ||
| 362 | [[nodiscard]] typename Derived::PlainObject inverseSqrt(const Eigen::MatrixBase<Derived>& matrix) | ||
| 363 | { | ||
| 364 | INS_ASSERT_USER_ERROR(matrix.rows() == matrix.cols(), "Only square matrix supported"); | ||
| 365 | if constexpr (std::is_floating_point_v<typename Derived::Scalar>) | ||
| 366 | { | ||
| 367 | return matrix.inverse().sqrt(); // Eigen::SelfAdjointEigenSolver<Eigen::MatrixX<T>>{ covMatrix }.operatorInverseSqrt(); | ||
| 368 | } | ||
| 369 | else // Eigen gets problems with ceres::Jet in the .sqrt() function | ||
| 370 | { | ||
| 371 | Eigen::JacobiSVD<Eigen::MatrixX<typename Derived::Scalar>> svd(matrix.inverse(), Eigen::ComputeFullV); | ||
| 372 | typename Derived::PlainObject sqrtInverse = svd.matrixV() * svd.singularValues().cwiseSqrt().asDiagonal() * svd.matrixV().transpose(); | ||
| 373 | INS_ASSERT_USER_ERROR(!sqrtInverse.hasNaN(), "The matrix is not invertible"); | ||
| 374 | return sqrtInverse; | ||
| 375 | } | ||
| 376 | } | ||
| 377 | |||
| 378 | /// @brief Change the sign of x according to the value of y | ||
| 379 | /// @param[in] x input value | ||
| 380 | /// @param[in] y input value | ||
| 381 | /// @return -x or +x | ||
| 382 | template<typename T> | ||
| 383 | 2612 | T sign(const T& x, const T& y) | |
| 384 | { | ||
| 385 | // similar function 'sign' in fortran | ||
| 386 |
2/2✓ Branch 0 taken 498 times.
✓ Branch 1 taken 2114 times.
|
2612 | if (y >= 0.0) |
| 387 | { | ||
| 388 | 498 | return fabs(x); | |
| 389 | } | ||
| 390 | 2114 | return -1.0 * fabs(x); | |
| 391 | } | ||
| 392 | |||
| 393 | /// @brief Linear interpolation between vectors | ||
| 394 | /// @param a Left value | ||
| 395 | /// @param b Right value | ||
| 396 | /// @param t Multiplier. [0, 1] for interpolation | ||
| 397 | /// @return a + t * (b - a) | ||
| 398 | template<typename Derived> | ||
| 399 | ✗ | typename Derived::PlainObject lerp(const Eigen::MatrixBase<Derived>& a, const Eigen::MatrixBase<Derived>& b, auto t) | |
| 400 | { | ||
| 401 | ✗ | return a + t * (b - a); | |
| 402 | } | ||
| 403 | |||
| 404 | /// Lerp Search Result | ||
| 405 | struct LerpSearchResult | ||
| 406 | { | ||
| 407 | size_t l; ///< Lower bound index | ||
| 408 | size_t u; ///< Upper bound index (l + 1) | ||
| 409 | double t; ///< [0, 1] for Interpolation, otherwise Extrapolation | ||
| 410 | }; | ||
| 411 | |||
| 412 | /// @brief Searches the value in the data container | ||
| 413 | /// @param[in] data Data container | ||
| 414 | /// @param[in] value Value to search | ||
| 415 | 3 | LerpSearchResult lerpSearch(const auto& data, const auto& value) | |
| 416 | { | ||
| 417 | 3 | auto i = static_cast<size_t>(std::distance(data.begin(), std::upper_bound(data.begin(), data.end(), value))); | |
| 418 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | if (i > 0) { i--; } |
| 419 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | if (i == data.size() - 1) { i--; } |
| 420 | 3 | const auto& lb = data.at(i); | |
| 421 | 3 | const auto& ub = data.at(i + 1); | |
| 422 | 3 | double t = (value - lb) / (ub - lb); | |
| 423 | |||
| 424 | 3 | return { .l = i, .u = i + 1, .t = t }; | |
| 425 | } | ||
| 426 | |||
| 427 | /// @brief Bilinear interpolation | ||
| 428 | /// @param[in] tx Distance in x component to interpolate [0, 1] | ||
| 429 | /// @param[in] ty Distance in y component to interpolate [0, 1] | ||
| 430 | /// @param[in] c00 Value for tx = ty = 0 | ||
| 431 | /// @param[in] c10 Value for tx = 1 and ty = 0 | ||
| 432 | /// @param[in] c01 Value for tx = 0 and ty = 1 | ||
| 433 | /// @param[in] c11 Value for tx = ty = 1 | ||
| 434 | /// | ||
| 435 | /// c01 ------ c11 | ||
| 436 | /// | | | ||
| 437 | /// | | | ||
| 438 | /// | | | ||
| 439 | /// c00 ------ c10 | ||
| 440 | /// | ||
| 441 | /// @note See https://www.scratchapixel.com/lessons/mathematics-physics-for-computer-graphics/interpolation/bilinear-filtering.html | ||
| 442 | 9 | auto bilinearInterpolation(const auto& tx, const auto& ty, | |
| 443 | const auto& c00, const auto& c10, | ||
| 444 | const auto& c01, const auto& c11) | ||
| 445 | { | ||
| 446 | 9 | auto a = c00 * (1.0 - tx) + c10 * tx; | |
| 447 | 9 | auto b = c01 * (1.0 - tx) + c11 * tx; | |
| 448 | 9 | return a * (1.0 - ty) + b * ty; | |
| 449 | // Alternative implementation: | ||
| 450 | // return (1.0 - tx) * (1.0 - ty) * c00 + tx * (1.0 - ty) * c10 + (1.0 - tx) * ty * c01 + tx * ty * c11; | ||
| 451 | } | ||
| 452 | |||
| 453 | /// @brief Calculates the incomplete elliptical integral of the second kind | ||
| 454 | /// @param[in] phi Interval bound the integration uses from 0 to phi | ||
| 455 | /// @param[in] m Function parameter that is integrated 1-m*sin(t)^2 | ||
| 456 | /// @return Incomplete elliptical integral of the second kind | ||
| 457 | /// @note See http://www2.iap.fr/users/pichon/doc/html_xref/elliptic-es.html | ||
| 458 | double calcEllipticalIntegral(double phi, double m); | ||
| 459 | |||
| 460 | } // namespace NAV::math | ||
| 461 |