INSTINCT Code Coverage Report


Directory: src/
File: Nodes/DataProvider/State/PosVelAttFile.cpp
Date: 2025-02-07 16:54:41
Exec Total Coverage
Lines: 13 249 5.2%
Functions: 4 16 25.0%
Branches: 11 788 1.4%

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 "PosVelAttFile.hpp"
10
11 #include "util/Logger.hpp"
12 #include "util/StringUtil.hpp"
13
14 #include "Navigation/Transformations/CoordinateFrames.hpp"
15 #include "Navigation/Transformations/Units.hpp"
16
17 #include "internal/NodeManager.hpp"
18 #include <Eigen/src/Core/DiagonalMatrix.h>
19 namespace nm = NAV::NodeManager;
20 #include "internal/FlowManager.hpp"
21
22 #include "NodeData/State/PosVelAtt.hpp"
23
24 112 NAV::PosVelAttFile::PosVelAttFile()
25
3/6
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 112 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 112 times.
✗ Branch 9 not taken.
112 : Node(typeStatic())
26 {
27 LOG_TRACE("{}: called", name);
28
29 112 _hasConfig = true;
30 112 _guiConfigDefaultWindowSize = { 488, 248 };
31
32
4/8
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 112 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 336 times.
✓ Branch 9 taken 112 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
560 nm::CreateOutputPin(this, "PosVelAtt", Pin::Type::Flow, { Pos::type(), PosVel::type(), PosVelAtt::type() }, &PosVelAttFile::pollData);
33
2/4
✓ Branch 2 taken 112 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 112 times.
✗ Branch 7 not taken.
224 nm::CreateOutputPin(this, "Header Columns", Pin::Type::Object, { "std::vector<std::string>" }, &_headerColumns);
34 224 }
35
36 224 NAV::PosVelAttFile::~PosVelAttFile()
37 {
38 LOG_TRACE("{}: called", nameId());
39 224 }
40
41 224 std::string NAV::PosVelAttFile::typeStatic()
42 {
43
1/2
✓ Branch 1 taken 224 times.
✗ Branch 2 not taken.
448 return "PosVelAttFile";
44 }
45
46 std::string NAV::PosVelAttFile::type() const
47 {
48 return typeStatic();
49 }
50
51 112 std::string NAV::PosVelAttFile::category()
52 {
53
1/2
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
224 return "Data Provider";
54 }
55
56 void NAV::PosVelAttFile::guiConfig()
57 {
58 if (auto res = FileReader::guiConfig(".csv,.*", { ".csv" }, size_t(id), nameId()))
59 {
60 LOG_DEBUG("{}: Path changed to {}", nameId(), _path);
61 flow::ApplyChanges();
62 if (res == FileReader::PATH_CHANGED)
63 {
64 doReinitialize();
65 }
66 else
67 {
68 doDeinitialize();
69 }
70 }
71
72 // Header info
73 if (ImGui::BeginTable(fmt::format("##PvaHeaders ({})", id.AsPointer()).c_str(), 4,
74 ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg))
75 {
76 ImGui::TableSetupColumn("Time", ImGuiTableColumnFlags_WidthFixed);
77 ImGui::TableSetupColumn("Position", ImGuiTableColumnFlags_WidthFixed);
78 ImGui::TableSetupColumn("Velocity", ImGuiTableColumnFlags_WidthFixed);
79 ImGui::TableSetupColumn("Attitude", ImGuiTableColumnFlags_WidthFixed);
80 ImGui::TableHeadersRow();
81
82 auto TextColoredIfExists = [this](int index, const char* text) {
83 ImGui::TableSetColumnIndex(index);
84 if (std::ranges::find(_headerColumns, text) != _headerColumns.end())
85 {
86 ImGui::TextUnformatted(text);
87 }
88 else
89 {
90 ImGui::TextDisabled("%s", text);
91 }
92 };
93
94 ImGui::TableNextRow();
95 TextColoredIfExists(0, "GpsCycle");
96 TextColoredIfExists(1, "Pos ECEF X [m]");
97 TextColoredIfExists(2, "Vel ECEF X [m/s]");
98 TextColoredIfExists(3, "n_Quat_b w");
99 ImGui::TableNextRow();
100 TextColoredIfExists(0, "GpsWeek");
101 TextColoredIfExists(1, "Pos ECEF Y [m]");
102 TextColoredIfExists(2, "Vel ECEF Y [m/s]");
103 TextColoredIfExists(3, "n_Quat_b x");
104 ImGui::TableNextRow();
105 TextColoredIfExists(0, "GpsToW [s]");
106 TextColoredIfExists(1, "Pos ECEF Z [m]");
107 TextColoredIfExists(2, "Vel ECEF Z [m/s]");
108 TextColoredIfExists(3, "n_Quat_b y");
109 ImGui::TableNextRow();
110 TextColoredIfExists(1, "Latitude [deg]");
111 TextColoredIfExists(2, "Vel N [m/s]");
112 TextColoredIfExists(3, "n_Quat_b z");
113 ImGui::TableNextRow();
114 TextColoredIfExists(1, "Longitude [deg]");
115 TextColoredIfExists(2, "Vel E [m/s]");
116 TextColoredIfExists(3, "Roll [deg]");
117 ImGui::TableNextRow();
118 TextColoredIfExists(1, "Altitude [m]");
119 TextColoredIfExists(2, "Vel D [m/s]");
120 TextColoredIfExists(3, "Pitch [deg]");
121 ImGui::TableNextRow();
122 TextColoredIfExists(3, "Yaw [deg]");
123
124 ImGui::EndTable();
125 }
126 }
127
128 [[nodiscard]] json NAV::PosVelAttFile::save() const
129 {
130 LOG_TRACE("{}: called", nameId());
131
132 json j;
133
134 j["FileReader"] = FileReader::save();
135 j["pinType"] = outputPins[OUTPUT_PORT_INDEX_PVA].dataIdentifier;
136
137 return j;
138 }
139
140 void NAV::PosVelAttFile::restore(json const& j)
141 {
142 LOG_TRACE("{}: called", nameId());
143
144 if (j.contains("FileReader"))
145 {
146 FileReader::restore(j.at("FileReader"));
147 }
148 if (j.contains("pinType"))
149 {
150 j.at("pinType").get_to(outputPins[OUTPUT_PORT_INDEX_PVA].dataIdentifier);
151 }
152 }
153
154 bool NAV::PosVelAttFile::initialize()
155 {
156 LOG_TRACE("{}: called", nameId());
157
158 if (FileReader::initialize())
159 {
160 for (auto& col : _headerColumns)
161 {
162 str::replace(col, "GpsTow [s]", "GpsToW [s]");
163 }
164
165 auto hasCol = [&](const char* text) {
166 return std::ranges::find(_headerColumns, text) != _headerColumns.end();
167 };
168
169 if (!hasCol("GpsCycle") || !hasCol("GpsWeek") || !hasCol("GpsToW [s]"))
170 {
171 LOG_ERROR("{}: A PosVelAtt File needs a time. Please add columns 'GpsCycle', 'GpsWeek' and 'GpsToW [s]'.", nameId());
172 return false;
173 }
174 if (!(hasCol("Pos ECEF X [m]") && hasCol("Pos ECEF Y [m]") && hasCol("Pos ECEF Z [m]"))
175 && !(hasCol("Latitude [deg]") && hasCol("Longitude [deg]") && hasCol("Altitude [m]")))
176 {
177 LOG_ERROR("{}: A PosVelAtt File needs a position. Please provide"
178 " either 'Pos ECEF X [m]', 'Pos ECEF Y [m]', 'Pos ECEF Z [m]'"
179 " or 'Latitude [deg]', 'Longitude [deg]', 'Altitude [m]'.",
180 nameId());
181 return false;
182 }
183 _fileContent = FileContent::Pos;
184 outputPins[OUTPUT_PORT_INDEX_PVA].dataIdentifier = std::vector{ Pos::type() };
185
186 if ((hasCol("Vel ECEF X [m/s]") && hasCol("Vel ECEF Y [m/s]") && hasCol("Vel ECEF Z [m/s]"))
187 || (hasCol("Vel N [m/s]") && hasCol("Vel E [m/s]") && hasCol("Vel D [m/s]")))
188 {
189 _fileContent = FileContent::PosVel;
190 outputPins[OUTPUT_PORT_INDEX_PVA].dataIdentifier = std::vector{ PosVel::type() };
191
192 if ((hasCol("n_Quat_b w") && hasCol("n_Quat_b x") && hasCol("n_Quat_b y") && hasCol("n_Quat_b z"))
193 || (hasCol("Roll [deg]") && hasCol("Pitch [deg]") && hasCol("Yaw [deg]")))
194 {
195 _fileContent = FileContent::PosVelAtt;
196 outputPins[OUTPUT_PORT_INDEX_PVA].dataIdentifier = std::vector{ PosVelAtt::type() };
197 }
198 }
199
200 for (auto& link : outputPins[OUTPUT_PORT_INDEX_PVA].links)
201 {
202 if (auto* pin = link.getConnectedPin())
203 {
204 outputPins[OUTPUT_PORT_INDEX_PVA].recreateLink(*pin);
205 }
206 }
207
208 return true;
209 }
210
211 outputPins[OUTPUT_PORT_INDEX_PVA].dataIdentifier = { Pos::type(), PosVel::type(), PosVelAtt::type() };
212 return false;
213 }
214
215 void NAV::PosVelAttFile::deinitialize()
216 {
217 LOG_TRACE("{}: called", nameId());
218
219 FileReader::deinitialize();
220 }
221
222 bool NAV::PosVelAttFile::resetNode()
223 {
224 FileReader::resetReader();
225
226 return true;
227 }
228
229 std::shared_ptr<const NAV::NodeData> NAV::PosVelAttFile::pollData()
230 {
231 std::shared_ptr<Pos> obs;
232 switch (_fileContent)
233 {
234 case FileContent::Pos:
235 obs = std::make_shared<Pos>();
236 break;
237 case FileContent::PosVel:
238 obs = std::make_shared<PosVel>();
239 break;
240 case FileContent::PosVelAtt:
241 obs = std::make_shared<PosVelAtt>();
242 break;
243 }
244
245 // Read line
246 std::string line;
247 getline(line);
248 // Remove any starting non text characters
249 line.erase(line.begin(), std::ranges::find_if(line, [](int ch) { return std::isgraph(ch); }));
250
251 if (line.empty())
252 {
253 return nullptr;
254 }
255
256 // Convert line into stream
257 std::stringstream lineStream(line);
258 std::string cell;
259
260 std::optional<uint16_t> gpsCycle = 0;
261 std::optional<uint16_t> gpsWeek;
262 std::optional<long double> gpsToW;
263 std::optional<double> e_position_x;
264 std::optional<double> e_position_y;
265 std::optional<double> e_position_z;
266 std::optional<double> e_positionStdDev_x;
267 std::optional<double> e_positionStdDev_y;
268 std::optional<double> e_positionStdDev_z;
269 std::optional<double> lla_position_x;
270 std::optional<double> lla_position_y;
271 std::optional<double> lla_position_z;
272 std::optional<double> n_positionStdDev_n;
273 std::optional<double> n_positionStdDev_e;
274 std::optional<double> n_positionStdDev_d;
275 std::optional<double> e_velocity_x;
276 std::optional<double> e_velocity_y;
277 std::optional<double> e_velocity_z;
278 std::optional<double> e_velocityStdDev_x;
279 std::optional<double> e_velocityStdDev_y;
280 std::optional<double> e_velocityStdDev_z;
281 std::optional<double> n_velocity_n;
282 std::optional<double> n_velocity_e;
283 std::optional<double> n_velocity_d;
284 std::optional<double> n_velocityStdDev_n;
285 std::optional<double> n_velocityStdDev_e;
286 std::optional<double> n_velocityStdDev_d;
287 std::optional<double> n_Quat_b_w;
288 std::optional<double> n_Quat_b_x;
289 std::optional<double> n_Quat_b_y;
290 std::optional<double> n_Quat_b_z;
291 std::optional<double> roll;
292 std::optional<double> pitch;
293 std::optional<double> yaw;
294
295 // Split line at comma
296 for (const auto& column : _headerColumns)
297 {
298 if (std::getline(lineStream, cell, ','))
299 {
300 // Remove any trailing non text characters
301 cell.erase(std::ranges::find_if(cell, [](int ch) { return std::iscntrl(ch); }), cell.end());
302 if (cell.empty())
303 {
304 continue;
305 }
306
307 if (column == "GpsCycle") { gpsCycle = static_cast<uint16_t>(std::stoul(cell)); }
308 else if (column == "GpsWeek") { gpsWeek = static_cast<uint16_t>(std::stoul(cell)); }
309 else if (column == "GpsToW [s]") { gpsToW = std::stold(cell); }
310
311 else if (column == "Pos ECEF X [m]") { e_position_x = std::stod(cell); }
312 else if (column == "Pos ECEF Y [m]") { e_position_y = std::stod(cell); }
313 else if (column == "Pos ECEF Z [m]") { e_position_z = std::stod(cell); }
314 else if (column == "Pos StdDev ECEF X [m]") { e_positionStdDev_x = std::stod(cell); }
315 else if (column == "Pos StdDev ECEF Y [m]") { e_positionStdDev_y = std::stod(cell); }
316 else if (column == "Pos StdDev ECEF Z [m]") { e_positionStdDev_z = std::stod(cell); }
317
318 else if (column == "Latitude [deg]") { lla_position_x = deg2rad(std::stod(cell)); }
319 else if (column == "Longitude [deg]") { lla_position_y = deg2rad(std::stod(cell)); }
320 else if (column == "Altitude [m]") { lla_position_z = std::stod(cell); }
321 else if (column == "Pos StdDev N [m]") { n_positionStdDev_n = deg2rad(std::stod(cell)); }
322 else if (column == "Pos StdDev E [m]") { n_positionStdDev_e = deg2rad(std::stod(cell)); }
323 else if (column == "Pos StdDev D [m]") { n_positionStdDev_d = std::stod(cell); }
324
325 else if (column == "Vel ECEF X [m/s]") { e_velocity_x = std::stod(cell); }
326 else if (column == "Vel ECEF Y [m/s]") { e_velocity_y = std::stod(cell); }
327 else if (column == "Vel ECEF Z [m/s]") { e_velocity_z = std::stod(cell); }
328 else if (column == "Vel StdDev ECEF X [m/s]") { e_velocityStdDev_x = std::stod(cell); }
329 else if (column == "Vel StdDev ECEF Y [m/s]") { e_velocityStdDev_y = std::stod(cell); }
330 else if (column == "Vel StdDev ECEF Z [m/s]") { e_velocityStdDev_z = std::stod(cell); }
331
332 else if (column == "Vel N [m/s]") { n_velocity_n = std::stod(cell); }
333 else if (column == "Vel E [m/s]") { n_velocity_e = std::stod(cell); }
334 else if (column == "Vel D [m/s]") { n_velocity_d = std::stod(cell); }
335 else if (column == "Vel StdDev N [m/s]") { n_velocityStdDev_n = std::stod(cell); }
336 else if (column == "Vel StdDev E [m/s]") { n_velocityStdDev_e = std::stod(cell); }
337 else if (column == "Vel StdDev D [m/s]") { n_velocityStdDev_d = std::stod(cell); }
338
339 else if (column == "n_Quat_b w") { n_Quat_b_w = std::stod(cell); }
340 else if (column == "n_Quat_b x") { n_Quat_b_x = std::stod(cell); }
341 else if (column == "n_Quat_b y") { n_Quat_b_y = std::stod(cell); }
342 else if (column == "n_Quat_b z") { n_Quat_b_z = std::stod(cell); }
343
344 else if (column == "Roll [deg]") { roll = deg2rad(std::stod(cell)); }
345 else if (column == "Pitch [deg]") { pitch = deg2rad(std::stod(cell)); }
346 else if (column == "Yaw [deg]") { yaw = deg2rad(std::stod(cell)); }
347 }
348 }
349
350 if (gpsCycle.has_value() && gpsWeek.has_value() && gpsToW.has_value())
351 {
352 obs->insTime = InsTime(gpsCycle.value(), gpsWeek.value(), gpsToW.value());
353 }
354 else
355 {
356 LOG_WARN("{}: A PosVelAtt File needs a time.", nameId());
357 return nullptr;
358 }
359
360 if (e_position_x.has_value() && e_position_y.has_value() && e_position_z.has_value())
361 {
362 if (e_positionStdDev_x.has_value() && e_positionStdDev_y.has_value() && e_positionStdDev_z.has_value())
363 {
364 obs->setPositionAndStdDev_e(Eigen::Vector3d{ e_position_x.value(), e_position_y.value(), e_position_z.value() },
365 Eigen::DiagonalMatrix<double, 3>{ e_positionStdDev_x.value(), e_positionStdDev_y.value(), e_positionStdDev_z.value() }.toDenseMatrix());
366 }
367 else
368 {
369 obs->setPosition_e(Eigen::Vector3d{ e_position_x.value(), e_position_y.value(), e_position_z.value() });
370 }
371 }
372 else if (lla_position_x.has_value() && lla_position_y.has_value() && lla_position_z.has_value())
373 {
374 if (n_positionStdDev_n.has_value() && n_positionStdDev_e.has_value() && n_positionStdDev_d.has_value())
375 {
376 obs->setPositionAndStdDev_lla(Eigen::Vector3d{ lla_position_x.value(), lla_position_y.value(), lla_position_z.value() },
377 Eigen::DiagonalMatrix<double, 3>{ n_positionStdDev_n.value(), n_positionStdDev_e.value(), n_positionStdDev_d.value() }.toDenseMatrix());
378 }
379 else
380 {
381 obs->setPosition_lla(Eigen::Vector3d{ lla_position_x.value(), lla_position_y.value(), lla_position_z.value() });
382 }
383 }
384 else
385 {
386 LOG_WARN("{}: A PosVelAtt File needs a position.", nameId());
387 return nullptr;
388 }
389
390 if (_fileContent == FileContent::PosVel || _fileContent == FileContent::PosVelAtt)
391 {
392 if (e_velocity_x.has_value() && e_velocity_y.has_value() && e_velocity_z.has_value())
393 {
394 if (auto posVel = std::reinterpret_pointer_cast<PosVel>(obs))
395 {
396 if (e_velocityStdDev_x.has_value() && e_velocityStdDev_y.has_value() && e_velocityStdDev_z.has_value())
397 {
398 posVel->setVelocityAndStdDev_e(Eigen::Vector3d{ e_velocity_x.value(), e_velocity_y.value(), e_velocity_z.value() },
399 Eigen::DiagonalMatrix<double, 3>{ e_velocityStdDev_x.value(), e_velocityStdDev_y.value(), e_velocityStdDev_z.value() }.toDenseMatrix());
400 }
401 else
402 {
403 posVel->setVelocity_e(Eigen::Vector3d{ e_velocity_x.value(), e_velocity_y.value(), e_velocity_z.value() });
404 }
405 }
406 }
407 else if (n_velocity_n.has_value() && n_velocity_e.has_value() && n_velocity_d.has_value())
408 {
409 if (auto posVel = std::reinterpret_pointer_cast<PosVel>(obs))
410 {
411 if (n_velocityStdDev_n.has_value() && n_velocityStdDev_e.has_value() && n_velocityStdDev_d.has_value())
412 {
413 posVel->setVelocityAndStdDev_n(Eigen::Vector3d{ n_velocity_n.value(), n_velocity_e.value(), n_velocity_d.value() },
414 Eigen::DiagonalMatrix<double, 3>{ n_velocityStdDev_n.value(), n_velocityStdDev_e.value(), n_velocityStdDev_d.value() }.toDenseMatrix());
415 }
416 else
417 {
418 posVel->setVelocity_n(Eigen::Vector3d{ n_velocity_n.value(), n_velocity_e.value(), n_velocity_d.value() });
419 }
420 }
421 }
422 }
423
424 if (_fileContent == FileContent::PosVelAtt)
425 {
426 if (n_Quat_b_w.has_value() && n_Quat_b_x.has_value() && n_Quat_b_y.has_value() && n_Quat_b_z.has_value())
427 {
428 if (auto posVelAtt = std::reinterpret_pointer_cast<PosVelAtt>(obs))
429 {
430 posVelAtt->setAttitude_n_Quat_b(Eigen::Quaterniond{ n_Quat_b_w.value(), n_Quat_b_x.value(), n_Quat_b_y.value(), n_Quat_b_z.value() });
431 }
432 }
433 else if (roll.has_value() && pitch.has_value() && yaw.has_value())
434 {
435 if (auto posVelAtt = std::reinterpret_pointer_cast<PosVelAtt>(obs))
436 {
437 posVelAtt->setAttitude_n_Quat_b(trafo::n_Quat_b(roll.value(), pitch.value(), yaw.value()));
438 }
439 }
440 }
441
442 invokeCallbacks(OUTPUT_PORT_INDEX_PVA, obs);
443 return obs;
444 }
445