INSTINCT Code Coverage Report


Directory: src/
File: Nodes/DataProcessor/Integrator/ImuIntegrator.cpp
Date: 2025-11-25 23:34:18
Exec Total Coverage
Lines: 21 106 19.8%
Functions: 5 13 38.5%
Branches: 23 196 11.7%

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 #include "ImuIntegrator.hpp"
10
11 #include <memory>
12 #include <algorithm>
13 #include <cstddef>
14
15 #include <imgui.h>
16 #include <imgui_internal.h>
17
18 #include "Navigation/INS/InertialPreIntegrator.hpp"
19 #include "internal/gui/widgets/HelpMarker.hpp"
20
21 #include "NodeRegistry.hpp"
22 #include <fmt/format.h>
23 #include "internal/FlowManager.hpp"
24
25 #include "Navigation/Constants.hpp"
26 #include "Navigation/Math/Math.hpp"
27
28 #include "util/Logger.hpp"
29 #include "util/Eigen.hpp"
30
31 115 NAV::ImuIntegrator::ImuIntegrator()
32
4/8
✓ Branch 1 taken 115 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 115 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 115 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 115 times.
✗ Branch 12 not taken.
115 : Node(typeStatic())
33 {
34 LOG_TRACE("{}: called", name);
35
36 115 _hasConfig = true;
37 115 _guiConfigDefaultWindowSize = { 422, 146 };
38
39
4/8
✓ Branch 2 taken 115 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 115 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 230 times.
✓ Branch 10 taken 115 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
575 CreateInputPin("ImuObs", Pin::Type::Flow, { NAV::ImuObs::type(), NAV::ImuObsWDelta::type() }, &ImuIntegrator::recvObservation,
40 ✗ [](const Node* node, const InputPin& inputPin) {
41 ✗ const auto* imuIntegrator = static_cast<const ImuIntegrator*>(node); // NOLINT(cppcoreguidelines-pro-type-static-cast-downcast)
42 ✗ return !inputPin.queue.empty() && imuIntegrator->_inertialIntegrator.hasInitialPosition();
43 });
44
4/8
✓ Branch 1 taken 115 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 115 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 115 times.
✓ Branch 9 taken 115 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
345 CreateInputPin("PosVelAttInit", Pin::Type::Flow, { NAV::PosVelAtt::type() }, &ImuIntegrator::recvPosVelAttInit, nullptr, 1);
45
46
4/8
✓ Branch 2 taken 115 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 115 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 115 times.
✓ Branch 10 taken 115 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
460 CreateOutputPin("PosVelAtt", Pin::Type::Flow, { NAV::PosVelAtt::type() });
47 460 }
48
49 232 NAV::ImuIntegrator::~ImuIntegrator()
50 {
51 LOG_TRACE("{}: called", nameId());
52 232 }
53
54 229 std::string NAV::ImuIntegrator::typeStatic()
55 {
56
1/2
✓ Branch 1 taken 229 times.
✗ Branch 2 not taken.
458 return "ImuIntegrator";
57 }
58
59 ✗ std::string NAV::ImuIntegrator::type() const
60 {
61 ✗ return typeStatic();
62 }
63
64 114 std::string NAV::ImuIntegrator::category()
65 {
66
1/2
✓ Branch 1 taken 114 times.
✗ Branch 2 not taken.
228 return "Data Processor";
67 }
68
69 ✗ void NAV::ImuIntegrator::guiConfig()
70 {
71 ✗ if (ImGui::Checkbox(fmt::format("IMU Preintegration##{}", size_t(id)).c_str(), &_imuPreintegration))
72 {
73 ✗ flow::ApplyChanges();
74 }
75
76 ✗ if (_imuPreintegration)
77 {
78 ✗ if (ImGui::Checkbox(fmt::format("Reset every epoch##{}", size_t(id)).c_str(), &_resetPreintegratorEveryEpoch))
79 {
80 ✗ flow::ApplyChanges();
81 }
82 ✗ ImGui::SameLine();
83 ✗ gui::widgets::HelpMarker("Resetting the preintegrator every epoch makes it behave like a normal integrator");
84
85 ✗ if (InertialPreIntegratorGui(std::to_string(size_t(id)).c_str(), _inertialPreintegrator))
86 {
87 ✗ flow::ApplyChanges();
88 }
89 }
90 else
91 {
92 ✗ if (InertialIntegratorGui(std::to_string(size_t(id)).c_str(), _inertialIntegrator, _preferAccelerationOverDeltaMeasurements))
93 {
94 ✗ flow::ApplyChanges();
95 }
96 }
97 ✗ }
98
99 ✗ [[nodiscard]] json NAV::ImuIntegrator::save() const
100 {
101 LOG_TRACE("{}: called", nameId());
102
103 return {
104 ✗ { "imuPreintegration", _imuPreintegration },
105 ✗ { "resetPreintegratorEveryEpoch", _resetPreintegratorEveryEpoch },
106 ✗ { "inertialIntegrator", _inertialIntegrator },
107 ✗ { "inertialPreintegrator", _inertialPreintegrator },
108 ✗ { "preferAccelerationOverDeltaMeasurements", _preferAccelerationOverDeltaMeasurements },
109 ✗ };
110 ✗ }
111
112 1 void NAV::ImuIntegrator::restore(json const& j)
113 {
114 LOG_TRACE("{}: called", nameId());
115
116
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (j.contains("imuPreintegration")) { j.at("imuPreintegration").get_to(_imuPreintegration); }
117
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (j.contains("resetPreintegratorEveryEpoch")) { j.at("resetPreintegratorEveryEpoch").get_to(_resetPreintegratorEveryEpoch); }
118
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 if (j.contains("inertialIntegrator")) { j.at("inertialIntegrator").get_to(_inertialIntegrator); }
119
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (j.contains("inertialPreintegrator")) { j.at("inertialPreintegrator").get_to(_inertialPreintegrator); }
120
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 if (j.contains("preferAccelerationOverDeltaMeasurements")) { j.at("preferAccelerationOverDeltaMeasurements").get_to(_preferAccelerationOverDeltaMeasurements); }
121 1 }
122
123 ✗ bool NAV::ImuIntegrator::initialize()
124 {
125 LOG_TRACE("{}: called", nameId());
126
127 ✗ _inertialIntegrator.reset();
128 ✗ _inertialPreintegrator.reset();
129 ✗ _lastImuObs.reset();
130 ✗ _lastPosVelAtt.reset();
131
132 ✗ LOG_DEBUG("ImuIntegrator initialized");
133
134 ✗ return true;
135 }
136
137 ✗ void NAV::ImuIntegrator::deinitialize()
138 {
139 LOG_TRACE("{}: called", nameId());
140 ✗ }
141
142 ✗ void NAV::ImuIntegrator::recvObservation(NAV::InputPin::NodeDataQueue& queue, size_t /* pinIdx */)
143 {
144 ✗ auto nodeData = queue.extract_front();
145 ✗ if (nodeData->insTime.empty())
146 {
147 ✗ LOG_ERROR("{}: Can't set new imuObs__t0 because the observation has no time tag (insTime)", nameId());
148 ✗ return;
149 }
150
151 ✗ std::shared_ptr<NAV::PosVelAtt> integratedPosVelAtt = nullptr;
152
153 ✗ if (_imuPreintegration)
154 {
155 ✗ auto obs = std::static_pointer_cast<const ImuObs>(nodeData);
156 LOG_DATA("{}: recvImuObs at time [{}] (preintegration)", nameId(), obs->insTime.toYMDHMS(GPST));
157 // LOG_DATA("{}: p_accel = {}", nameId(), obs->p_acceleration.transpose());
158 // LOG_DATA("{}: p_gyro = {}", nameId(), obs->p_angularRate.transpose());
159
160 ✗ if (!_lastImuObs)
161 {
162 ✗ _lastImuObs = obs;
163 LOG_DATA("{}: Skipping as first epoch", nameId());
164 ✗ return;
165 }
166
167 ✗ auto dt = static_cast<double>((obs->insTime - _lastImuObs->insTime).count());
168 // LOG_DATA("{}: dt(imu) = {}", nameId(), dt);
169 ✗ if (dt <= 1e-9)
170 {
171 LOG_DATA("{}: Skipping as dt is negative or zero", nameId());
172 ✗ return;
173 }
174 InertialPreIntegrator::Measurement meas{
175 .meas = InertialPreIntegrator::ImuMeasurement{ .dt = dt,
176 ✗ .p_acceleration = _lastImuObs->p_acceleration,
177 ✗ .p_angularRate = _lastImuObs->p_angularRate },
178 .imu = InertialPreIntegrator::ImuState<double>{}
179 ✗ };
180 InertialPreIntegrator::PVAState<double> pvaOld{
181 ✗ .position = _lastPosVelAtt->e_position(),
182 ✗ .velocity = _lastPosVelAtt->e_velocity(),
183 ✗ .attitude = _lastPosVelAtt->e_Quat_b()
184 ✗ };
185
186 ✗ _inertialPreintegrator.addInertialMeasurement(meas, obs->imuPos, nameId());
187
188 ✗ auto pvaNew = _inertialPreintegrator.calcIntegratedState(pvaOld, InertialPreIntegrator::ImuState<double>{}, obs->imuPos, nameId());
189
190 ✗ integratedPosVelAtt = std::make_shared<PosVelAtt>();
191 ✗ integratedPosVelAtt->insTime = obs->insTime;
192 ✗ integratedPosVelAtt->setPosVelAtt_e(pvaNew.position, pvaNew.velocity, pvaNew.attitude);
193
194 ✗ _lastImuObs = obs;
195
196 ✗ if (_resetPreintegratorEveryEpoch)
197 {
198 ✗ _lastPosVelAtt = integratedPosVelAtt;
199 ✗ _inertialPreintegrator.reset();
200 }
201 ✗ }
202 else
203 {
204 ✗ if (!_preferAccelerationOverDeltaMeasurements
205 ✗ && NAV::NodeRegistry::NodeDataTypeAnyIsChildOf(inputPins.at(INPUT_PORT_INDEX_IMU_OBS).link.getConnectedPin()->dataIdentifier, { ImuObsWDelta::type() }))
206 {
207 ✗ auto obs = std::static_pointer_cast<const ImuObsWDelta>(nodeData);
208 LOG_DATA("{}: recvImuObsWDelta at time [{}]", nameId(), obs->insTime.toYMDHMS(GPST));
209
210 ✗ integratedPosVelAtt = _inertialIntegrator.calcInertialSolutionDelta(obs->insTime, obs->dtime, obs->dvel, obs->dtheta, obs->imuPos, nameId().c_str());
211 ✗ }
212 else
213 {
214 ✗ auto obs = std::static_pointer_cast<const ImuObs>(nodeData);
215 LOG_DATA("{}: recvImuObs at time [{}]", nameId(), obs->insTime.toYMDHMS(GPST));
216
217 ✗ integratedPosVelAtt = _inertialIntegrator.calcInertialSolution(obs->insTime, obs->p_acceleration, obs->p_angularRate, obs->imuPos, nameId().c_str());
218 ✗ }
219 }
220
221 ✗ if (integratedPosVelAtt)
222 {
223 LOG_DATA("{}: e_position = {}", nameId(), integratedPosVelAtt->e_position().transpose());
224 LOG_DATA("{}: e_velocity = {}", nameId(), integratedPosVelAtt->e_velocity().transpose());
225 LOG_DATA("{}: rollPitchYaw = {}", nameId(), rad2deg(integratedPosVelAtt->rollPitchYaw()).transpose());
226 ✗ invokeCallbacks(OUTPUT_PORT_INDEX_INERTIAL_NAV_SOL, integratedPosVelAtt);
227 }
228 ✗ }
229
230 ✗ void NAV::ImuIntegrator::recvPosVelAttInit(NAV::InputPin::NodeDataQueue& queue, size_t /* pinIdx */)
231 {
232 ✗ auto posVelAtt = std::static_pointer_cast<const PosVelAtt>(queue.extract_front());
233 ✗ inputPins[INPUT_PORT_INDEX_POS_VEL_ATT_INIT].queueBlocked = true;
234 ✗ inputPins[INPUT_PORT_INDEX_POS_VEL_ATT_INIT].queue.clear();
235
236 LOG_DATA("{}: recvPosVelAttInit at time [{}]", nameId(), posVelAtt->insTime.toYMDHMS(GPST));
237
238 ✗ _lastPosVelAtt = posVelAtt;
239 ✗ _inertialIntegrator.setInitialState(*posVelAtt, nameId().c_str());
240 LOG_DATA("{}: e_position = {}", nameId(), posVelAtt->e_position().transpose());
241 LOG_DATA("{}: e_velocity = {}", nameId(), posVelAtt->e_velocity().transpose());
242 LOG_DATA("{}: rollPitchYaw = {}", nameId(), rad2deg(posVelAtt->rollPitchYaw()).transpose());
243
244 ✗ invokeCallbacks(OUTPUT_PORT_INDEX_INERTIAL_NAV_SOL, posVelAtt);
245 ✗ }
246