| 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 InertialIntegrator.cpp | ||
| 10 | /// @brief Inertial Measurement Integrator | ||
| 11 | /// @author T. Topp (topp@ins.uni-stuttgart.de) | ||
| 12 | /// @date 2023-12-09 | ||
| 13 | |||
| 14 | #include "InertialIntegrator.hpp" | ||
| 15 | |||
| 16 | #include "internal/gui/NodeEditorApplication.hpp" | ||
| 17 | #include "internal/gui/widgets/HelpMarker.hpp" | ||
| 18 | |||
| 19 | #include "Navigation/Ellipsoid/Ellipsoid.hpp" | ||
| 20 | #include "Navigation/INS/Functions.hpp" | ||
| 21 | #include "Navigation/Math/Math.hpp" | ||
| 22 | |||
| 23 | namespace NAV | ||
| 24 | { | ||
| 25 | |||
| 26 | ✗ | InertialIntegrator::InertialIntegrator(IntegrationFrame integrationFrame) | |
| 27 | ✗ | : _integrationFrame(integrationFrame), _lockIntegrationFrame(true) {} | |
| 28 | |||
| 29 | 51 | void InertialIntegrator::reset() | |
| 30 | { | ||
| 31 | 51 | _states.clear(); | |
| 32 | 51 | _p_lastBiasAcceleration.setZero(); | |
| 33 | 51 | _p_lastBiasAngularRate.setZero(); | |
| 34 | 51 | setBufferSizes(); | |
| 35 | 51 | } | |
| 36 | |||
| 37 | 126826 | bool InertialIntegrator::hasInitialPosition() const | |
| 38 | { | ||
| 39 | 126826 | return !_states.empty(); | |
| 40 | } | ||
| 41 | |||
| 42 | 17 | void InertialIntegrator::setInitialState(const PosVelAtt& state, const char* nameId) | |
| 43 | { | ||
| 44 | 17 | _states.clear(); | |
| 45 | 17 | addState(state, nameId); | |
| 46 | 17 | } | |
| 47 | |||
| 48 | 17 | void InertialIntegrator::addState(const PosVelAtt& state, [[maybe_unused]] const char* nameId) | |
| 49 | { | ||
| 50 |
16/32✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 17 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 17 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 17 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 17 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 17 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 17 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 17 times.
✗ Branch 23 not taken.
✓ Branch 25 taken 17 times.
✗ Branch 26 not taken.
✓ Branch 28 taken 17 times.
✗ Branch 29 not taken.
✓ Branch 31 taken 17 times.
✗ Branch 32 not taken.
✓ Branch 34 taken 17 times.
✗ Branch 35 not taken.
✓ Branch 37 taken 17 times.
✗ Branch 38 not taken.
✓ Branch 40 taken 17 times.
✗ Branch 41 not taken.
✓ Branch 43 taken 17 times.
✗ Branch 44 not taken.
✓ Branch 46 taken 17 times.
✗ Branch 47 not taken.
|
85 | _states.push_back(State{ |
| 51 | .epoch = state.insTime, | ||
| 52 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 9 times.
|
17 | .position = _integrationFrame == IntegrationFrame::ECEF ? state.e_position() : state.lla_position(), |
| 53 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 9 times.
|
17 | .velocity = _integrationFrame == IntegrationFrame::ECEF ? state.e_velocity() : state.n_velocity(), |
| 54 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 9 times.
|
17 | .attitude = _integrationFrame == IntegrationFrame::ECEF ? state.e_Quat_b() : state.n_Quat_b(), |
| 55 | .m = Measurement{ | ||
| 56 | .averagedMeasurement = false, | ||
| 57 | .dt = 0.0, | ||
| 58 |
1/2✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
|
17 | .p_acceleration = Eigen::Vector3d::Zero(), |
| 59 |
1/2✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
|
17 | .p_angularRate = Eigen::Vector3d::Zero(), |
| 60 | } }); | ||
| 61 | LOG_DATA("{}: Adding state for [{}]. Now has {} states", nameId, state.insTime.toYMDHMS(GPST), _states.size()); | ||
| 62 | 17 | } | |
| 63 | |||
| 64 | 17 | void InertialIntegrator::setTotalSensorBiases(const Eigen::Vector3d& p_biasAcceleration, const Eigen::Vector3d& p_biasAngularRate) | |
| 65 | { | ||
| 66 | 17 | _p_lastBiasAcceleration = p_biasAcceleration; | |
| 67 | 17 | _p_lastBiasAngularRate = p_biasAngularRate; | |
| 68 | 17 | } | |
| 69 | |||
| 70 | 26813 | void InertialIntegrator::applySensorBiasesIncrements(const Eigen::Vector3d& p_deltaBiasAcceleration, const Eigen::Vector3d& p_deltaBiasAngularRate) | |
| 71 | { | ||
| 72 | 26813 | _p_lastBiasAcceleration += p_deltaBiasAcceleration; | |
| 73 | 26813 | _p_lastBiasAngularRate += p_deltaBiasAngularRate; | |
| 74 | 26813 | } | |
| 75 | |||
| 76 | 25517 | void InertialIntegrator::applyStateErrors_n(const Eigen::Vector3d& lla_positionError, const Eigen::Vector3d& n_velocityError, const Eigen::Vector3d& n_attitudeError_b) | |
| 77 | { | ||
| 78 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 25517 times.
|
25517 | INS_ASSERT_USER_ERROR(_integrationFrame == IntegrationFrame::NED, "You can only apply errors to the selected integration frame"); |
| 79 |
2/4✓ Branch 1 taken 25517 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 25517 times.
✗ Branch 4 not taken.
|
25517 | INS_ASSERT_USER_ERROR(!_states.empty(), "You can only apply errors if the states vector is not empty"); |
| 80 | |||
| 81 |
2/4✓ Branch 1 taken 25517 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 25517 times.
✗ Branch 5 not taken.
|
25517 | _states.back().position -= lla_positionError; |
| 82 |
2/4✓ Branch 1 taken 25517 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 25517 times.
✗ Branch 5 not taken.
|
25517 | _states.back().velocity -= n_velocityError; |
| 83 | // Attitude correction, see Titterton and Weston (2004), p. 407 eq. 13.15 | ||
| 84 |
7/14✓ Branch 1 taken 25517 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 25517 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 25517 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 25517 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 25517 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 25517 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 25517 times.
✗ Branch 20 not taken.
|
25517 | Eigen::Matrix3d n_Dcm_b = (Eigen::Matrix3d::Identity() - math::skewSymmetricMatrix(n_attitudeError_b)) * _states.back().attitude.toRotationMatrix(); |
| 85 |
3/6✓ Branch 1 taken 25517 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 25517 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 25517 times.
✗ Branch 8 not taken.
|
25517 | _states.back().attitude = Eigen::Quaterniond(n_Dcm_b).normalized(); |
| 86 | |||
| 87 | // TODO: Test this out again | ||
| 88 | // Attitude correction, see Titterton and Weston (2004), p. 407 eq. 13.16 | ||
| 89 | // Eigen::Quaterniond n_Quat_b = posVelAtt->n_Quat_b() | ||
| 90 | // * (Eigen::AngleAxisd(attError(0), Eigen::Vector3d::UnitX()) | ||
| 91 | // * Eigen::AngleAxisd(attError(1), Eigen::Vector3d::UnitY()) | ||
| 92 | // * Eigen::AngleAxisd(attError(2), Eigen::Vector3d::UnitZ())) | ||
| 93 | // .normalized(); | ||
| 94 | // posVelAttCorrected->setAttitude_n_Quat_b(n_Quat_b.normalized()); | ||
| 95 | |||
| 96 | // Eigen::Vector3d attError = pvaError->n_attitudeError(); | ||
| 97 | // const Eigen::Quaterniond& n_Quat_b = posVelAtt->n_Quat_b(); | ||
| 98 | // Eigen::Quaterniond n_Quat_b_c{ n_Quat_b.w() + 0.5 * (+attError(0) * n_Quat_b.x() + attError(1) * n_Quat_b.y() + attError(2) * n_Quat_b.z()), | ||
| 99 | // n_Quat_b.x() + 0.5 * (-attError(0) * n_Quat_b.w() + attError(1) * n_Quat_b.z() - attError(2) * n_Quat_b.y()), | ||
| 100 | // n_Quat_b.y() + 0.5 * (-attError(0) * n_Quat_b.z() - attError(1) * n_Quat_b.w() + attError(2) * n_Quat_b.x()), | ||
| 101 | // n_Quat_b.z() + 0.5 * (+attError(0) * n_Quat_b.y() - attError(1) * n_Quat_b.x() - attError(2) * n_Quat_b.w()) }; | ||
| 102 | // posVelAttCorrected->setAttitude_n_Quat_b(n_Quat_b_c.normalized()); | ||
| 103 | 25517 | } | |
| 104 | |||
| 105 | 1296 | void InertialIntegrator::applyStateErrors_e(const Eigen::Vector3d& e_positionError, const Eigen::Vector3d& e_velocityError, const Eigen::Vector3d& e_attitudeError_b) | |
| 106 | { | ||
| 107 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1296 times.
|
1296 | INS_ASSERT_USER_ERROR(_integrationFrame == IntegrationFrame::ECEF, "You can only apply errors to the selected integration frame"); |
| 108 |
2/4✓ Branch 1 taken 1296 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1296 times.
✗ Branch 4 not taken.
|
1296 | INS_ASSERT_USER_ERROR(!_states.empty(), "You can only apply errors if the states vector is not empty"); |
| 109 | |||
| 110 |
2/4✓ Branch 1 taken 1296 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1296 times.
✗ Branch 5 not taken.
|
1296 | _states.back().position -= e_positionError; |
| 111 |
2/4✓ Branch 1 taken 1296 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1296 times.
✗ Branch 5 not taken.
|
1296 | _states.back().velocity -= e_velocityError; |
| 112 | // Attitude correction, see Titterton and Weston (2004), p. 407 eq. 13.15 | ||
| 113 |
7/14✓ Branch 1 taken 1296 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1296 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1296 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 1296 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 1296 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 1296 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 1296 times.
✗ Branch 20 not taken.
|
1296 | Eigen::Matrix3d e_Dcm_b = (Eigen::Matrix3d::Identity() - math::skewSymmetricMatrix(e_attitudeError_b)) * _states.back().attitude.toRotationMatrix(); |
| 114 |
3/6✓ Branch 1 taken 1296 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1296 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1296 times.
✗ Branch 8 not taken.
|
1296 | _states.back().attitude = Eigen::Quaterniond(e_Dcm_b).normalized(); |
| 115 | 1296 | } | |
| 116 | |||
| 117 | 543633 | std::optional<std::reference_wrapper<const InertialIntegrator::State>> InertialIntegrator::getLatestState() const | |
| 118 | { | ||
| 119 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 543633 times.
|
543633 | if (_states.empty()) { return {}; } |
| 120 |
1/2✓ Branch 1 taken 543633 times.
✗ Branch 2 not taken.
|
543633 | return _states.back(); |
| 121 | } | ||
| 122 | |||
| 123 | 52589 | const Eigen::Vector3d& InertialIntegrator::p_getLastAccelerationBias() const | |
| 124 | { | ||
| 125 | 52589 | return _p_lastBiasAcceleration; | |
| 126 | } | ||
| 127 | |||
| 128 | 52589 | const Eigen::Vector3d& InertialIntegrator::p_getLastAngularRateBias() const | |
| 129 | { | ||
| 130 | 52589 | return _p_lastBiasAngularRate; | |
| 131 | } | ||
| 132 | |||
| 133 | 270250 | InertialIntegrator::IntegrationFrame InertialIntegrator::getIntegrationFrame() const | |
| 134 | { | ||
| 135 | 270250 | return _integrationFrame; | |
| 136 | } | ||
| 137 | |||
| 138 | ✗ | const PosVelAttDerivativeConstants& InertialIntegrator::getConstants() const | |
| 139 | { | ||
| 140 | ✗ | return _posVelAttDerivativeConstants; | |
| 141 | } | ||
| 142 | |||
| 143 | ✗ | bool InertialIntegrator::areAccelerationsAveragedMeasurements() const | |
| 144 | { | ||
| 145 | ✗ | return _accelerationsAreAveragedMeasurements; | |
| 146 | } | ||
| 147 | |||
| 148 | 49997 | std::optional<Eigen::Vector3d> InertialIntegrator::p_calcCurrentAcceleration() const | |
| 149 | { | ||
| 150 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 49997 times.
|
49997 | if (_states.empty()) { return {}; } |
| 151 | |||
| 152 |
4/8✓ Branch 1 taken 49997 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 49997 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 49997 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 49997 times.
✗ Branch 11 not taken.
|
49997 | return _states.back().m.p_acceleration + _states.back().p_biasAcceleration; |
| 153 | } | ||
| 154 | |||
| 155 | 26813 | std::optional<Eigen::Vector3d> InertialIntegrator::p_calcCurrentAngularRate() const | |
| 156 | { | ||
| 157 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 26813 times.
|
26813 | if (_states.empty()) { return {}; } |
| 158 | |||
| 159 |
4/8✓ Branch 1 taken 26813 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 26813 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 26813 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 26813 times.
✗ Branch 11 not taken.
|
26813 | return _states.back().m.p_angularRate + _states.back().p_biasAngularRate; |
| 160 | } | ||
| 161 | |||
| 162 | 49998 | void InertialIntegrator::addMeasurement(const InsTime& epoch, double dt, const Eigen::Vector3d& p_acceleration, const Eigen::Vector3d& p_angularRate, [[maybe_unused]] const char* nameId) | |
| 163 | { | ||
| 164 | LOG_DATA("{}: Adding measurement [{}]. Last state at [{}]", nameId, epoch.toYMDHMS(GPST), _states.back().epoch.toYMDHMS(GPST)); | ||
| 165 |
1/2✓ Branch 1 taken 49998 times.
✗ Branch 2 not taken.
|
49998 | INS_ASSERT_USER_ERROR(!_states.empty(), "You need to add an initial state first"); |
| 166 | |||
| 167 |
2/2✓ Branch 2 taken 1 times.
✓ Branch 3 taken 49997 times.
|
49998 | if (_states.back().epoch == epoch) |
| 168 | { | ||
| 169 | LOG_DATA("{}: Updating existing state", nameId); | ||
| 170 | 1 | _states.back().m.averagedMeasurement = _accelerationsAreAveragedMeasurements; | |
| 171 | 1 | _states.back().m.dt = dt; | |
| 172 | 1 | _states.back().m.p_acceleration = p_acceleration; | |
| 173 | 1 | _states.back().m.p_angularRate = p_angularRate; | |
| 174 | 1 | _states.back().p_biasAcceleration = _p_lastBiasAcceleration; | |
| 175 | 1 | _states.back().p_biasAngularRate = _p_lastBiasAngularRate; | |
| 176 | 1 | return; | |
| 177 | } | ||
| 178 | |||
| 179 | LOG_DATA("{}: Adding as new state", nameId); | ||
| 180 |
8/16✓ Branch 1 taken 49997 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 49997 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 49997 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 49997 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 49997 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 49997 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 49997 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 49997 times.
✗ Branch 23 not taken.
|
99994 | _states.push_back(State{ |
| 181 | .epoch = epoch, | ||
| 182 |
2/4✓ Branch 1 taken 49997 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 49997 times.
✗ Branch 5 not taken.
|
49997 | .position = _states.back().position, |
| 183 |
2/4✓ Branch 1 taken 49997 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 49997 times.
✗ Branch 5 not taken.
|
49997 | .velocity = _states.back().velocity, |
| 184 |
2/4✓ Branch 1 taken 49997 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 49997 times.
✗ Branch 5 not taken.
|
49997 | .attitude = _states.back().attitude, |
| 185 | .m = Measurement{ | ||
| 186 |
1/2✓ Branch 1 taken 49997 times.
✗ Branch 2 not taken.
|
49997 | .averagedMeasurement = _accelerationsAreAveragedMeasurements, |
| 187 | .dt = dt, | ||
| 188 | .p_acceleration = p_acceleration, | ||
| 189 | .p_angularRate = p_angularRate, | ||
| 190 | }, | ||
| 191 |
1/2✓ Branch 1 taken 49997 times.
✗ Branch 2 not taken.
|
49997 | .p_biasAcceleration = _p_lastBiasAcceleration, |
| 192 |
1/2✓ Branch 1 taken 49997 times.
✗ Branch 2 not taken.
|
49997 | .p_biasAngularRate = _p_lastBiasAngularRate, |
| 193 | }); | ||
| 194 | } | ||
| 195 | |||
| 196 | ✗ | void InertialIntegrator::addDeltaMeasurement(const InsTime& epoch, double dt, double deltaTime, const Eigen::Vector3d& p_deltaVelocity, const Eigen::Vector3d& p_deltaTheta, const char* nameId) | |
| 197 | { | ||
| 198 | LOG_DATA("{}: Adding delta measurement for [{}]", nameId, epoch.toYMDHMS(GPST)); | ||
| 199 | ✗ | Eigen::Vector3d p_acceleration = Eigen::Vector3d::Zero(); | |
| 200 | ✗ | Eigen::Vector3d p_angularRate = Eigen::Vector3d::Zero(); | |
| 201 | |||
| 202 | ✗ | if (deltaTime > 0.0) // dt given by sensor (should never be 0 or negative, but check here just in case) | |
| 203 | { | ||
| 204 | ✗ | p_acceleration = p_deltaVelocity / deltaTime; | |
| 205 | ✗ | p_angularRate = p_deltaTheta / deltaTime; | |
| 206 | } | ||
| 207 | ✗ | else if (std::abs(dt) > 0.0) // Time difference between messages (differs from dt if message lost) | |
| 208 | { | ||
| 209 | // Negative values of dTimeLastState should not happen, but algorithm can work with it to propagate backwards | ||
| 210 | ✗ | p_acceleration = p_deltaVelocity / dt; | |
| 211 | ✗ | p_angularRate = p_deltaTheta / dt; | |
| 212 | } | ||
| 213 | |||
| 214 | ✗ | addMeasurement(epoch, dt, p_acceleration, p_angularRate, nameId); | |
| 215 | ✗ | _states.back().m.averagedMeasurement = true; | |
| 216 | ✗ | } | |
| 217 | |||
| 218 | 1 | std::shared_ptr<PosVelAtt> InertialIntegrator::lastStateAsPosVelAtt() const | |
| 219 | { | ||
| 220 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | INS_ASSERT_USER_ERROR(!_states.empty(), "You need to add an initial state first"); |
| 221 | |||
| 222 | 1 | auto posVelAtt = std::make_shared<PosVelAtt>(); | |
| 223 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | posVelAtt->insTime = _states.back().epoch; |
| 224 |
1/3✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
|
1 | switch (_integrationFrame) |
| 225 | { | ||
| 226 | 1 | case IntegrationFrame::NED: | |
| 227 |
4/8✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 1 times.
✗ Branch 12 not taken.
|
1 | posVelAtt->setPosVelAtt_n(_states.back().position, _states.back().velocity, _states.back().attitude); |
| 228 | 1 | break; | |
| 229 | ✗ | case IntegrationFrame::ECEF: | |
| 230 | ✗ | posVelAtt->setPosVelAtt_e(_states.back().position, _states.back().velocity, _states.back().attitude); | |
| 231 | ✗ | break; | |
| 232 | } | ||
| 233 | |||
| 234 | 1 | return posVelAtt; | |
| 235 | ✗ | } | |
| 236 | |||
| 237 | 49998 | std::shared_ptr<PosVelAtt> InertialIntegrator::calcInertialSolution(const InsTime& obsTime, | |
| 238 | const Eigen::Vector3d& p_acceleration, const Eigen::Vector3d& p_angularRate, | ||
| 239 | const ImuPos& imuPos, const char* nameId) | ||
| 240 | { | ||
| 241 |
3/6✓ Branch 1 taken 49998 times.
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 49998 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 49998 times.
|
49998 | if (!hasInitialPosition() || obsTime < _states.back().epoch) { return nullptr; } |
| 242 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 49998 times.
|
49998 | if (_states.back().epoch.empty()) { _states.back().epoch = obsTime; } |
| 243 | |||
| 244 |
2/4✓ Branch 1 taken 49998 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 49998 times.
✗ Branch 5 not taken.
|
49998 | auto dt = static_cast<double>((obsTime - _states.back().epoch).count()); |
| 245 | 49998 | addMeasurement(obsTime, dt, p_acceleration, p_angularRate, nameId); | |
| 246 |
2/2✓ Branch 1 taken 1 times.
✓ Branch 2 taken 49997 times.
|
49998 | if (std::abs(dt) < 1e-8) |
| 247 | { | ||
| 248 | LOG_DATA("{}: Returning state [{}], as no time difference between measurement.", nameId, _states.back().epoch.toYMDHMS(GPST)); | ||
| 249 | 1 | return lastStateAsPosVelAtt(); | |
| 250 | } | ||
| 251 | |||
| 252 | 49997 | return calcInertialSolutionFromMeasurementBuffer(imuPos, nameId); | |
| 253 | } | ||
| 254 | |||
| 255 | ✗ | std::shared_ptr<PosVelAtt> InertialIntegrator::calcInertialSolutionDelta(const InsTime& obsTime, double deltaTime, | |
| 256 | const Eigen::Vector3d& p_deltaVelocity, const Eigen::Vector3d& p_deltaTheta, | ||
| 257 | const ImuPos& imuPos, const char* nameId) | ||
| 258 | { | ||
| 259 | ✗ | if (!hasInitialPosition() || obsTime < _states.back().epoch) { return nullptr; } | |
| 260 | ✗ | if (_states.back().epoch.empty()) { _states.back().epoch = obsTime; } | |
| 261 | |||
| 262 | ✗ | auto dt = static_cast<double>((obsTime - _states.back().epoch).count()); | |
| 263 | ✗ | addDeltaMeasurement(obsTime, dt, deltaTime, p_deltaVelocity, p_deltaTheta, nameId); | |
| 264 | ✗ | if (std::abs(dt) < 1e-8) | |
| 265 | { | ||
| 266 | LOG_DATA("{}: Returning state [{}], as no time difference between measurement.", nameId, _states.back().epoch.toYMDHMS(GPST)); | ||
| 267 | ✗ | return lastStateAsPosVelAtt(); | |
| 268 | } | ||
| 269 | |||
| 270 | ✗ | return calcInertialSolutionFromMeasurementBuffer(imuPos, nameId); | |
| 271 | } | ||
| 272 | |||
| 273 | 49997 | std::shared_ptr<PosVelAtt> InertialIntegrator::calcInertialSolutionFromMeasurementBuffer(const ImuPos& imuPos, const char* nameId) | |
| 274 | { | ||
| 275 |
2/4✓ Branch 1 taken 49997 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 49997 times.
✗ Branch 4 not taken.
|
49997 | INS_ASSERT_USER_ERROR(!_states.empty(), "You need to add an initial state first"); |
| 276 |
1/2✓ Branch 1 taken 49997 times.
✗ Branch 2 not taken.
|
49997 | INS_ASSERT_USER_ERROR(_states.size() >= 2, "You need to add an initial state and at least one measurement first"); |
| 277 | |||
| 278 | LOG_DATA("{}: Calculating inertial solution from buffer. States t0 = [{}], t1 = [{}]", | ||
| 279 | nameId, _states.back().epoch.toYMDHMS(GPST), _states.at(_states.size() - 2).epoch.toYMDHMS(GPST)); | ||
| 280 | |||
| 281 |
3/6✓ Branch 2 taken 49997 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 49997 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 49997 times.
✗ Branch 9 not taken.
|
49997 | auto y = calcInertialSolution(imuPos, _states.back(), _states.at(_states.size() - 2), nameId); |
| 282 | |||
| 283 |
1/2✓ Branch 1 taken 49997 times.
✗ Branch 2 not taken.
|
49997 | auto posVelAtt = std::make_shared<PosVelAtt>(); |
| 284 |
1/2✓ Branch 1 taken 49997 times.
✗ Branch 2 not taken.
|
49997 | posVelAtt->insTime = _states.back().epoch; |
| 285 |
2/3✓ Branch 0 taken 37109 times.
✓ Branch 1 taken 12888 times.
✗ Branch 2 not taken.
|
49997 | switch (_integrationFrame) |
| 286 | { | ||
| 287 | 37109 | case IntegrationFrame::NED: | |
| 288 |
5/10✓ Branch 2 taken 37109 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 37109 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 37109 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 37109 times.
✗ Branch 12 not taken.
✓ Branch 14 taken 37109 times.
✗ Branch 15 not taken.
|
37109 | posVelAtt->setPosVelAtt_n(y.segment<3>(0), y.segment<3>(3), Eigen::Quaterniond(y.segment<4>(6))); |
| 289 | 37109 | break; | |
| 290 | 12888 | case IntegrationFrame::ECEF: | |
| 291 |
5/10✓ Branch 2 taken 12888 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 12888 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 12888 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 12888 times.
✗ Branch 12 not taken.
✓ Branch 14 taken 12888 times.
✗ Branch 15 not taken.
|
12888 | posVelAtt->setPosVelAtt_e(y.segment<3>(0), y.segment<3>(3), Eigen::Quaterniond(y.segment<4>(6))); |
| 292 | 12888 | break; | |
| 293 | } | ||
| 294 | |||
| 295 |
2/3✓ Branch 0 taken 37109 times.
✓ Branch 1 taken 12888 times.
✗ Branch 2 not taken.
|
49997 | switch (_integrationFrame) |
| 296 | { | ||
| 297 | 37109 | case IntegrationFrame::NED: | |
| 298 |
2/4✓ Branch 3 taken 37109 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 37109 times.
✗ Branch 7 not taken.
|
37109 | _states.back().position = posVelAtt->lla_position(); |
| 299 |
2/4✓ Branch 3 taken 37109 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 37109 times.
✗ Branch 7 not taken.
|
37109 | _states.back().velocity = posVelAtt->n_velocity(); |
| 300 |
2/4✓ Branch 3 taken 37109 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 37109 times.
✗ Branch 7 not taken.
|
37109 | _states.back().attitude = posVelAtt->n_Quat_b(); |
| 301 | 37109 | break; | |
| 302 | 12888 | case IntegrationFrame::ECEF: | |
| 303 |
2/4✓ Branch 3 taken 12888 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 12888 times.
✗ Branch 7 not taken.
|
12888 | _states.back().position = posVelAtt->e_position(); |
| 304 |
2/4✓ Branch 3 taken 12888 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 12888 times.
✗ Branch 7 not taken.
|
12888 | _states.back().velocity = posVelAtt->e_velocity(); |
| 305 |
2/4✓ Branch 3 taken 12888 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 12888 times.
✗ Branch 7 not taken.
|
12888 | _states.back().attitude = posVelAtt->e_Quat_b(); |
| 306 | 12888 | break; | |
| 307 | } | ||
| 308 | |||
| 309 | 99994 | return posVelAtt; | |
| 310 | ✗ | } | |
| 311 | |||
| 312 | 69 | void InertialIntegrator::setBufferSizes() | |
| 313 | { | ||
| 314 |
1/4✓ Branch 0 taken 69 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
69 | switch (_integrationAlgorithm) |
| 315 | { | ||
| 316 | 69 | case IntegrationAlgorithm::SingleStepRungeKutta1: | |
| 317 | case IntegrationAlgorithm::SingleStepRungeKutta2: | ||
| 318 | case IntegrationAlgorithm::SingleStepHeun2: | ||
| 319 | case IntegrationAlgorithm::SingleStepRungeKutta3: | ||
| 320 | case IntegrationAlgorithm::SingleStepHeun3: | ||
| 321 | case IntegrationAlgorithm::SingleStepRungeKutta4: | ||
| 322 | 69 | _states.resize(2); | |
| 323 | 69 | break; | |
| 324 | ✗ | case IntegrationAlgorithm::MultiStepRK3: | |
| 325 | case IntegrationAlgorithm::MultiStepRK4: | ||
| 326 | ✗ | _states.resize(3); | |
| 327 | ✗ | break; | |
| 328 | ✗ | case IntegrationAlgorithm::COUNT: | |
| 329 | ✗ | break; | |
| 330 | } | ||
| 331 | 69 | } | |
| 332 | |||
| 333 | ✗ | bool InertialIntegratorGui(const char* label, InertialIntegrator& integrator, bool& preferAccelerationOverDeltaMeasurements, float width) | |
| 334 | { | ||
| 335 | ✗ | bool changed = false; | |
| 336 | |||
| 337 | ✗ | if (integrator._lockIntegrationFrame) { ImGui::BeginDisabled(); } | |
| 338 | ✗ | ImGui::SetNextItemWidth(width * gui::NodeEditorApplication::windowFontRatio()); | |
| 339 | ✗ | if (auto integrationFrame = static_cast<int>(integrator._integrationFrame); | |
| 340 | ✗ | ImGui::Combo(fmt::format("Integration Frame##{}", label).c_str(), &integrationFrame, "ECEF\0NED\0\0")) | |
| 341 | { | ||
| 342 | ✗ | integrator._integrationFrame = static_cast<decltype(integrator._integrationFrame)>(integrationFrame); | |
| 343 | ✗ | LOG_DEBUG("Integration Frame changed to {}", integrator._integrationFrame == InertialIntegrator::IntegrationFrame::NED ? "NED" : "ECEF"); | |
| 344 | ✗ | changed = true; | |
| 345 | } | ||
| 346 | ✗ | if (integrator._lockIntegrationFrame) { ImGui::EndDisabled(); } | |
| 347 | |||
| 348 | ✗ | ImGui::SetNextItemWidth(width * gui::NodeEditorApplication::windowFontRatio()); | |
| 349 | ✗ | if (ImGui::BeginCombo(fmt::format("Integration Algorithm##{}", label).c_str(), to_string(integrator._integrationAlgorithm))) | |
| 350 | { | ||
| 351 | ✗ | for (size_t i = 0; i < static_cast<size_t>(InertialIntegrator::IntegrationAlgorithm::MultiStepRK3); i++) // TODO: InertialIntegrator::IntegrationAlgorithm::COUNT | |
| 352 | { | ||
| 353 | ✗ | const bool is_selected = (static_cast<size_t>(integrator._integrationAlgorithm) == i); | |
| 354 | ✗ | if (ImGui::Selectable(to_string(static_cast<InertialIntegrator::IntegrationAlgorithm>(i)), is_selected)) | |
| 355 | { | ||
| 356 | ✗ | integrator._integrationAlgorithm = static_cast<InertialIntegrator::IntegrationAlgorithm>(i); | |
| 357 | ✗ | integrator.setBufferSizes(); | |
| 358 | ✗ | LOG_DEBUG("Integration Algorithm Attitude changed to {}", fmt::underlying(integrator._integrationAlgorithm)); | |
| 359 | ✗ | changed = true; | |
| 360 | } | ||
| 361 | |||
| 362 | // Set the initial focus when opening the combo (scrolling + keyboard navigation focus) | ||
| 363 | ✗ | if (is_selected) | |
| 364 | { | ||
| 365 | ✗ | ImGui::SetItemDefaultFocus(); | |
| 366 | } | ||
| 367 | } | ||
| 368 | |||
| 369 | ✗ | ImGui::EndCombo(); | |
| 370 | } | ||
| 371 | |||
| 372 | ✗ | changed |= ImGui::Checkbox(fmt::format("Prefer raw measurements over delta##{}", label).c_str(), &preferAccelerationOverDeltaMeasurements); | |
| 373 | |||
| 374 | ✗ | if (preferAccelerationOverDeltaMeasurements) | |
| 375 | { | ||
| 376 | ✗ | changed |= ImGui::Checkbox(fmt::format("Accumulated Acceleration##{}", label).c_str(), &integrator._accelerationsAreAveragedMeasurements); | |
| 377 | ✗ | ImGui::SameLine(); | |
| 378 | ✗ | gui::widgets::HelpMarker("Some IMUs operate at higher rates than their output rate. (e.g. internal rate 800Hz and output rate is 100Hz)\n" | |
| 379 | "- Such IMUs often output averaged accelerations between the current and last output.\n" | ||
| 380 | "- If the connected IMU (e.g. a VectorNavSensor node) is such as sensor, please check this.\n" | ||
| 381 | "- If delta measurements are used, this is automatically true"); | ||
| 382 | } | ||
| 383 | |||
| 384 | ✗ | ImGui::SetNextItemOpen(false, ImGuiCond_FirstUseEver); | |
| 385 | ✗ | if (ImGui::TreeNode(fmt::format("Compensation models##{}", label).c_str())) | |
| 386 | { | ||
| 387 | ✗ | ImGui::TextUnformatted("Acceleration compensation"); | |
| 388 | { | ||
| 389 | ✗ | ImGui::Indent(); | |
| 390 | ✗ | ImGui::SetNextItemWidth(230 * gui::NodeEditorApplication::windowFontRatio()); | |
| 391 | ✗ | if (ComboGravitationModel(fmt::format("Gravitation Model##{}", label).c_str(), integrator._posVelAttDerivativeConstants.gravitationModel)) | |
| 392 | { | ||
| 393 | ✗ | LOG_DEBUG("Gravity Model changed to {}", NAV::to_string(integrator._posVelAttDerivativeConstants.gravitationModel)); | |
| 394 | ✗ | changed = true; | |
| 395 | } | ||
| 396 | ✗ | if (ImGui::Checkbox(fmt::format("Coriolis acceleration ##{}", label).c_str(), &integrator._posVelAttDerivativeConstants.coriolisAccelerationCompensationEnabled)) | |
| 397 | { | ||
| 398 | ✗ | LOG_DEBUG("coriolisAccelerationCompensationEnabled changed to {}", integrator._posVelAttDerivativeConstants.coriolisAccelerationCompensationEnabled); | |
| 399 | ✗ | changed = true; | |
| 400 | } | ||
| 401 | ✗ | if (ImGui::Checkbox(fmt::format("Centrifugal acceleration##{}", label).c_str(), &integrator._posVelAttDerivativeConstants.centrifgalAccelerationCompensationEnabled)) | |
| 402 | { | ||
| 403 | ✗ | LOG_DEBUG("centrifgalAccelerationCompensationEnabled changed to {}", integrator._posVelAttDerivativeConstants.centrifgalAccelerationCompensationEnabled); | |
| 404 | ✗ | changed = true; | |
| 405 | } | ||
| 406 | ✗ | ImGui::Unindent(); | |
| 407 | } | ||
| 408 | ✗ | ImGui::TextUnformatted("Angular rate compensation"); | |
| 409 | { | ||
| 410 | ✗ | ImGui::Indent(); | |
| 411 | ✗ | if (ImGui::Checkbox(fmt::format("Earth rotation rate##{}", label).c_str(), &integrator._posVelAttDerivativeConstants.angularRateEarthRotationCompensationEnabled)) | |
| 412 | { | ||
| 413 | ✗ | LOG_DEBUG("angularRateEarthRotationCompensationEnabled changed to {}", integrator._posVelAttDerivativeConstants.angularRateEarthRotationCompensationEnabled); | |
| 414 | ✗ | changed = true; | |
| 415 | } | ||
| 416 | ✗ | if (integrator._integrationFrame == InertialIntegrator::IntegrationFrame::NED) | |
| 417 | { | ||
| 418 | ✗ | if (ImGui::Checkbox(fmt::format("Transport rate##{}", label).c_str(), &integrator._posVelAttDerivativeConstants.angularRateTransportRateCompensationEnabled)) | |
| 419 | { | ||
| 420 | ✗ | LOG_DEBUG("angularRateTransportRateCompensationEnabled changed to {}", integrator._posVelAttDerivativeConstants.angularRateTransportRateCompensationEnabled); | |
| 421 | ✗ | changed = true; | |
| 422 | } | ||
| 423 | } | ||
| 424 | ✗ | ImGui::Unindent(); | |
| 425 | } | ||
| 426 | |||
| 427 | ✗ | ImGui::TreePop(); | |
| 428 | } | ||
| 429 | |||
| 430 | ✗ | return changed; | |
| 431 | } | ||
| 432 | |||
| 433 | ✗ | void to_json(json& j, const InertialIntegrator& data) | |
| 434 | { | ||
| 435 | ✗ | j = json{ | |
| 436 | ✗ | { "integrationFrame", data._integrationFrame }, | |
| 437 | ✗ | { "lockIntegrationFrame", data._lockIntegrationFrame }, | |
| 438 | ✗ | { "integrationAlgorithm", data._integrationAlgorithm }, | |
| 439 | ✗ | { "accelerationsAreAveragedMeasurements", data._accelerationsAreAveragedMeasurements }, | |
| 440 | ✗ | { "posVelAttDerivativeConstants", data._posVelAttDerivativeConstants }, | |
| 441 | ✗ | }; | |
| 442 | ✗ | } | |
| 443 | |||
| 444 | 18 | void from_json(const json& j, InertialIntegrator& data) | |
| 445 | { | ||
| 446 |
1/2✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
|
18 | if (j.contains("integrationFrame")) { j.at("integrationFrame").get_to(data._integrationFrame); } |
| 447 |
1/2✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
|
18 | if (j.contains("lockIntegrationFrame")) { j.at("lockIntegrationFrame").get_to(data._lockIntegrationFrame); } |
| 448 |
1/2✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
|
18 | if (j.contains("integrationAlgorithm")) |
| 449 | { | ||
| 450 | 18 | j.at("integrationAlgorithm").get_to(data._integrationAlgorithm); | |
| 451 | 18 | data.setBufferSizes(); | |
| 452 | } | ||
| 453 |
1/2✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
|
18 | if (j.contains("accelerationsAreAveragedMeasurements")) { j.at("accelerationsAreAveragedMeasurements").get_to(data._accelerationsAreAveragedMeasurements); } |
| 454 |
1/2✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
|
18 | if (j.contains("posVelAttDerivativeConstants")) { j.at("posVelAttDerivativeConstants").get_to(data._posVelAttDerivativeConstants); } |
| 455 | 18 | } | |
| 456 | |||
| 457 | ✗ | const char* to_string(InertialIntegrator::IntegrationAlgorithm algorithm) | |
| 458 | { | ||
| 459 | ✗ | switch (algorithm) | |
| 460 | { | ||
| 461 | ✗ | case InertialIntegrator::IntegrationAlgorithm::SingleStepRungeKutta1: | |
| 462 | ✗ | return "Runge-Kutta 1st order (explicit) / (Forward) Euler method"; | |
| 463 | ✗ | case InertialIntegrator::IntegrationAlgorithm::SingleStepRungeKutta2: | |
| 464 | ✗ | return "Runge-Kutta 2nd order (explicit)"; | |
| 465 | ✗ | case InertialIntegrator::IntegrationAlgorithm::SingleStepHeun2: | |
| 466 | ✗ | return "Heun's method (2nd order) (explicit)"; | |
| 467 | ✗ | case InertialIntegrator::IntegrationAlgorithm::SingleStepRungeKutta3: | |
| 468 | ✗ | return "Runge-Kutta 3rd order (explicit) / Simpson's rule"; | |
| 469 | ✗ | case InertialIntegrator::IntegrationAlgorithm::SingleStepHeun3: | |
| 470 | ✗ | return "Heun's method (3nd order) (explicit)"; | |
| 471 | ✗ | case InertialIntegrator::IntegrationAlgorithm::SingleStepRungeKutta4: | |
| 472 | ✗ | return "Runge-Kutta 4th order (explicit)"; | |
| 473 | ✗ | case InertialIntegrator::IntegrationAlgorithm::MultiStepRK3: | |
| 474 | ✗ | return "Runge-Kutta 3rd order (explicit) / Simpson's rule (multistep)"; | |
| 475 | ✗ | case InertialIntegrator::IntegrationAlgorithm::MultiStepRK4: | |
| 476 | ✗ | return "Runge-Kutta 4th order (explicit) (multistep)"; | |
| 477 | ✗ | case InertialIntegrator::IntegrationAlgorithm::COUNT: | |
| 478 | ✗ | return ""; | |
| 479 | } | ||
| 480 | ✗ | return ""; | |
| 481 | } | ||
| 482 | |||
| 483 | ✗ | const char* to_string(InertialIntegrator::IntegrationFrame frame) | |
| 484 | { | ||
| 485 | ✗ | switch (frame) | |
| 486 | { | ||
| 487 | ✗ | case InertialIntegrator::IntegrationFrame::ECEF: | |
| 488 | ✗ | return "ECEF"; | |
| 489 | ✗ | case InertialIntegrator::IntegrationFrame::NED: | |
| 490 | ✗ | return "NED"; | |
| 491 | } | ||
| 492 | ✗ | return ""; | |
| 493 | } | ||
| 494 | |||
| 495 | } // namespace NAV | ||
| 496 |