0.5.1
Loading...
Searching...
No Matches
UlogFile.cpp
Go to the documentation of this file.
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 "UlogFile.hpp"
10
11#include "util/Logger.hpp"
12
13#include <exception>
14
16
18
23
24#include <ctime>
25
26// ----------------------------------------------------------- Basic Node Functions --------------------------------------------------------------
27
29 : Imu(typeStatic())
30{
31 LOG_TRACE("{}: called", name);
32
33 _hasConfig = true;
34 _guiConfigDefaultWindowSize = { 589, 257 };
35
36 // All message types are polled from the first output pin, but then send out on the correct pin over invokeCallbacks
40}
41
43{
44 LOG_TRACE("{}: called", nameId());
45}
46
48{
49 return "UlogFile";
50}
51
52std::string NAV::UlogFile::type() const
53{
54 return typeStatic();
55}
56
58{
59 return "Data Provider";
60}
61
63{
64 if (auto res = FileReader::guiConfig(".ulg,.*", { ".ulg" }, size_t(id), nameId()))
65 {
66 LOG_DEBUG("{}: Path changed to {}", nameId(), _path);
68 if (res == FileReader::PATH_CHANGED)
69 {
71 }
72 else
73 {
75 }
76 }
77
79}
80
81[[nodiscard]] json NAV::UlogFile::save() const
82{
83 LOG_TRACE("{}: called", nameId());
84
85 json j;
86
87 j["FileReader"] = FileReader::save();
88 j["Imu"] = Imu::save();
89
90 return j;
91}
92
94{
95 LOG_TRACE("{}: called", nameId());
96
97 if (j.contains("FileReader"))
98 {
99 FileReader::restore(j.at("FileReader"));
100 }
101 if (j.contains("Imu"))
102 {
103 Imu::restore(j.at("Imu"));
104 }
105}
106
108{
109 LOG_TRACE("{}: called", nameId());
110
111 return FileReader::initialize();
112}
113
115{
116 LOG_TRACE("{}: called", nameId());
117
119}
120
122{
123 LOG_TRACE("{}: called", nameId());
124
126
127 lastGnssTime.timeSinceStartup = 0;
128 _epochData.clear();
129 _subscribedMessages.clear();
130
131 return true;
132}
133
134// ------------------------------------------------------------ File Reading ---------------------------------------------------------------
135
137{
138 LOG_TRACE("{}: called", nameId());
139
140 std::filesystem::path filepath = getFilepath();
141
142 auto filestream = std::ifstream(filepath);
143
144 constexpr uint16_t BUFFER_SIZE = 10; // TODO: validate size
145
146 std::array<char, BUFFER_SIZE> buffer{};
147 if (filestream.good())
148 {
149 filestream.read(buffer.data(), BUFFER_SIZE);
150 filestream.close();
151 LOG_DEBUG("{} has the file type: CSV", nameId());
152 return FileType::BINARY;
153 }
154 filestream.close();
155
156 LOG_ERROR("{} could not open file", nameId(), filepath);
157 return FileType::NONE;
158}
159
161{
162 LOG_TRACE("{}: called", nameId());
163
165 {
166 union
167 {
168 std::array<char, 16> data{};
170 } ulogHeader{};
171
172 read(ulogHeader.data.data(), ulogHeader.data.size());
173
174 // Check "ULog" at beginning of file
175 if ((ulogHeader.header.fileMagic[0] != 'U') || (ulogHeader.header.fileMagic[1] != 'L') || (ulogHeader.header.fileMagic[2] != 'o') || (ulogHeader.header.fileMagic[3] != 'g'))
176 {
177 LOG_WARN("{}: FileType is binary, but not ULog", nameId());
178 }
179
180 LOG_DEBUG("{}: version: {}", nameId(), static_cast<int>(ulogHeader.header.version));
181
182 LOG_DEBUG("{}: time stamp [µs]: {}", nameId(), ulogHeader.header.timeStamp);
183
184 // Read message header
185 union
186 {
187 std::array<char, 3> data{};
189 } ulogMsgHeader{};
190
191 while ((ulogMsgHeader.msgHeader.msg_type != 'A') && (ulogMsgHeader.msgHeader.msg_type != 'L'))
192 {
193 read(ulogMsgHeader.data.data(), ulogMsgHeader.data.size());
194
195 LOG_DATA("{}: msgSize: {}, msgType: {}", nameId(), ulogMsgHeader.msgHeader.msg_size, ulogMsgHeader.msgHeader.msg_type);
196
197 // Read definition message
198 // Flag bitset message
199 if (ulogMsgHeader.msgHeader.msg_type == 'B')
200 {
201 if (ulogMsgHeader.msgHeader.msg_size > 40)
202 {
203 LOG_WARN("{}: Exceeding bytes in 'flag bitset message' are ignored. Check for ULog file format update.", nameId());
204 }
205
206 union
207 {
208 std::array<char, 40> data{};
210 } ulogMsgFlagBits{};
211
212 read(ulogMsgFlagBits.data.data(), ulogMsgFlagBits.data.size() * sizeof(char)); // 'sizeof' is optional here, but it is the solution in general, since data types can be larger than one byte
213 }
214
215 // Format definition for a single (composite) type that can be logged or used in another definition as a nested type
216 else if (ulogMsgHeader.msgHeader.msg_type == 'F')
217 {
219 messageFormat.header = ulogMsgHeader.msgHeader;
220
221 LOG_DATA("{}: messageFormat.header.msg_size: {}", nameId(), messageFormat.header.msg_size);
222
223 messageFormat.format.resize(messageFormat.header.msg_size);
224 read(messageFormat.format.data(), ulogMsgHeader.msgHeader.msg_size);
225 LOG_DATA("{}: messageFormat.format.data(): {}", nameId(), messageFormat.format.data());
226
227 std::string msgName = messageFormat.format.substr(0, messageFormat.format.find(':'));
228
229 // Decoding data format fields
230 std::stringstream lineStream(messageFormat.format.substr(messageFormat.format.find(':') + 1));
231 std::string cell;
232
233 std::vector<DataField> msgDataFields;
234
235 while (std::getline(lineStream, cell, ';'))
236 {
237 DataField data_field{ .type = cell.substr(0, cell.find(' ')), .name = cell.substr(cell.find(' ') + 1) };
238 msgDataFields.push_back(data_field);
239 }
240
241 if (msgName == "sensor_accel")
242 {
243 _messageFormats.insert_or_assign(msgName, msgDataFields);
244 }
245 else if (msgName == "sensor_gyro")
246 {
247 _messageFormats.insert_or_assign(msgName, msgDataFields);
248 }
249 else if (msgName == "sensor_mag")
250 {
251 _messageFormats.insert_or_assign(msgName, msgDataFields);
252 }
253 else if (msgName == "vehicle_gps_position")
254 {
255 _messageFormats.insert_or_assign(msgName, msgDataFields);
256 }
257 else if (msgName == "vehicle_attitude")
258 {
259 _messageFormats.insert_or_assign(msgName, msgDataFields);
260 }
261 else if (msgName == "vehicle_air_data")
262 {
263 _messageFormats.insert_or_assign(msgName, msgDataFields);
264 }
265 else if (msgName == "vehicle_control_mode")
266 {
267 _messageFormats.insert_or_assign(msgName, msgDataFields);
268 }
269 else if (msgName == "vehicle_status")
270 {
271 _messageFormats.insert_or_assign(msgName, msgDataFields);
272 }
273 else if (msgName == "cpuload")
274 {
275 _messageFormats.insert_or_assign(msgName, msgDataFields);
276 }
277 else
278 {
279 LOG_ERROR("{}: Data format '{}' could not be decoded", nameId(), msgName);
280 }
281 }
282
283 // Information message
284 else if (ulogMsgHeader.msgHeader.msg_type == 'I')
285 {
286 readInformationMessage(ulogMsgHeader.msgHeader.msg_size, ulogMsgHeader.msgHeader.msg_type);
287 }
288
289 // Information message multi
290 else if (ulogMsgHeader.msgHeader.msg_type == 'M')
291 {
292 readInformationMessageMulti(ulogMsgHeader.msgHeader.msg_size, ulogMsgHeader.msgHeader.msg_type);
293 }
294
295 // Parameter message
296 else if (ulogMsgHeader.msgHeader.msg_type == 'P')
297 {
298 readParameterMessage(ulogMsgHeader.msgHeader.msg_size, ulogMsgHeader.msgHeader.msg_type);
299 }
300
301 // Parameter message default
302 else if (ulogMsgHeader.msgHeader.msg_type == 'Q')
303 {
304 readParameterMessageDefault(ulogMsgHeader.msgHeader.msg_size, ulogMsgHeader.msgHeader.msg_type);
305 }
306 }
307 seekg(-3, std::ios_base::cur); // 'msg_size' + 'msg_type' = 3 Byte
308 LOG_DEBUG("{}: Read 'Definitions Section' completed", nameId());
309 }
310}
311
312std::shared_ptr<const NAV::NodeData> NAV::UlogFile::pollData()
313{
314 // Read message header
315 union
316 {
317 std::array<char, 3> data{};
319 } ulogMsgHeader{};
320
321 while (true)
322 {
323 read(ulogMsgHeader.data.data(), ulogMsgHeader.data.size());
324
325 LOG_DATA("{}: msgSize: {}", nameId(), ulogMsgHeader.msgHeader.msg_size);
326 LOG_DATA("{}: msgType: {}", nameId(), ulogMsgHeader.msgHeader.msg_type);
327
328 if (ulogMsgHeader.msgHeader.msg_type == 'A')
329 {
331 messageAddLog.header = ulogMsgHeader.msgHeader;
332 read(reinterpret_cast<char*>(&messageAddLog.multi_id), sizeof(messageAddLog.multi_id));
333 LOG_DATA("{}: multi_id: {}", nameId(), messageAddLog.multi_id);
334 read(reinterpret_cast<char*>(&messageAddLog.msg_id), sizeof(messageAddLog.msg_id));
335 LOG_DATA("{}: msg_id: {}", nameId(), messageAddLog.msg_id);
336
337 messageAddLog.msg_name.resize(messageAddLog.header.msg_size - 3);
338 read(messageAddLog.msg_name.data(), messageAddLog.header.msg_size - 3);
339 LOG_DATA("{}: messageAddLog.msg_name: {}", nameId(), messageAddLog.msg_name);
340
341 /// Combines (sensor-)message name with an ID that indicates a possible multiple of a sensor
342 _subscribedMessages.insert_or_assign(messageAddLog.msg_id, SubscriptionData{ .multi_id = messageAddLog.multi_id, .message_name = messageAddLog.msg_name });
343 }
344 else if (ulogMsgHeader.msgHeader.msg_type == 'R')
345 {
347 messageRemoveLog.header = ulogMsgHeader.msgHeader;
348 read(reinterpret_cast<char*>(&messageRemoveLog.msg_id), sizeof(messageRemoveLog.msg_id));
349 LOG_DATA("{}: Removed message with 'msg_id': {}", nameId(), messageRemoveLog.msg_id);
350
351 _subscribedMessages.erase(messageRemoveLog.msg_id);
352 }
353 else if (ulogMsgHeader.msgHeader.msg_type == 'D')
354 {
356 messageData.header = ulogMsgHeader.msgHeader;
357 read(reinterpret_cast<char*>(&messageData.msg_id), sizeof(messageData.msg_id));
358 LOG_DATA("{}: msg_id: {}", nameId(), messageData.msg_id);
359
360 messageData.data.resize(messageData.header.msg_size - 2);
361 read(messageData.data.data(), messageData.header.msg_size - 2);
362 LOG_DATA("{}: messageData.header.msg_size: {}", nameId(), messageData.header.msg_size);
363
364 const auto& messageFormat = _messageFormats.at(_subscribedMessages.at(messageData.msg_id).message_name);
365
366 size_t currentExtractLocation = 0;
367 if (_subscribedMessages.at(messageData.msg_id).message_name == "sensor_accel")
368 {
369 SensorAccel sensorAccel{};
370 for (const auto& dataField : messageFormat)
371 {
372 char* currentData = messageData.data.data() + currentExtractLocation;
373 if (dataField.name == "timestamp")
374 {
375 std::memcpy(&sensorAccel.timestamp, currentData, sizeof(sensorAccel.timestamp));
376 LOG_DATA("{}: sensorAccel.timestamp: {}", nameId(), sensorAccel.timestamp);
377 currentExtractLocation += sizeof(sensorAccel.timestamp);
378 }
379 else if (dataField.name == "timestamp_sample")
380 {
381 std::memcpy(&sensorAccel.timestamp_sample, currentData, sizeof(sensorAccel.timestamp_sample));
382 LOG_DATA("{}: sensorAccel.timestamp_sample: {}", nameId(), sensorAccel.timestamp_sample);
383 currentExtractLocation += sizeof(sensorAccel.timestamp_sample);
384 }
385 else if (dataField.name == "device_id")
386 {
387 std::memcpy(&sensorAccel.device_id, currentData, sizeof(sensorAccel.device_id));
388 LOG_DATA("{}: sensorAccel.device_id: {}", nameId(), sensorAccel.device_id);
389 currentExtractLocation += sizeof(sensorAccel.device_id);
390 }
391 else if (dataField.name == "error_count")
392 {
393 std::memcpy(&sensorAccel.error_count, currentData, sizeof(sensorAccel.error_count));
394 LOG_DATA("{}: sensorAccel.error_count: {}", nameId(), sensorAccel.error_count);
395 currentExtractLocation += sizeof(sensorAccel.error_count);
396 }
397 else if (dataField.name == "x")
398 {
399 std::memcpy(&sensorAccel.x, currentData, sizeof(sensorAccel.x));
400 LOG_DATA("{}: sensorAccel.x: {}", nameId(), sensorAccel.x);
401 currentExtractLocation += sizeof(sensorAccel.x);
402 }
403 else if (dataField.name == "y")
404 {
405 std::memcpy(&sensorAccel.y, currentData, sizeof(sensorAccel.y));
406 LOG_DATA("{}: sensorAccel.y: {}", nameId(), sensorAccel.y);
407 currentExtractLocation += sizeof(sensorAccel.y);
408 }
409 else if (dataField.name == "z")
410 {
411 std::memcpy(&sensorAccel.z, currentData, sizeof(sensorAccel.z));
412 LOG_DATA("{}: sensorAccel.z: {}", nameId(), sensorAccel.z);
413 currentExtractLocation += sizeof(sensorAccel.z);
414 }
415 else if (dataField.name == "temperature")
416 {
417 std::memcpy(&sensorAccel.temperature, currentData, sizeof(sensorAccel.temperature));
418 LOG_DATA("{}: sensorAccel.temperature: {}", nameId(), sensorAccel.temperature);
419 currentExtractLocation += sizeof(sensorAccel.temperature);
420 }
421 else if (dataField.name == "clip_counter")
422 {
423 std::memcpy(sensorAccel.clip_counter.data(), currentData, sensorAccel.clip_counter.size());
424 LOG_DATA("{}: sensorAccel.clip_counter: {}", nameId(), fmt::join(sensorAccel.clip_counter, ", "));
425 currentExtractLocation += sensorAccel.clip_counter.size();
426 }
427 else if (dataField.name.compare(0, 7, "_padding")) // e.g. '_padding0', '_padding1'
428 {
429 currentExtractLocation += SensorAccel::padding; // Extraction Location should be moved to account for multiple padding
430 LOG_DATA("{}: sensorAccel: padding", nameId());
431 }
432 else
433 {
434 // FIXME: move 'currentExtractLocation', if yes, how far?
435 LOG_WARN("{}: dataField.name = '{}' or dataField.type = '{}' is unknown", nameId(), dataField.name, dataField.type);
436 }
437 }
438
439 LOG_DATA("{}: Inserting [{}] {}: {}", nameId(), sensorAccel.timestamp,
440 _subscribedMessages.at(messageData.msg_id).multi_id,
441 _subscribedMessages.at(messageData.msg_id).message_name);
442
443 _epochData.insert(std::make_pair(sensorAccel.timestamp,
444 MeasurementData{ .multi_id = _subscribedMessages.at(messageData.msg_id).multi_id,
445 .message_name = _subscribedMessages.at(messageData.msg_id).message_name,
446 .data = sensorAccel }));
447 }
448 else if (_subscribedMessages.at(messageData.msg_id).message_name == "sensor_gyro")
449 {
450 SensorGyro sensorGyro{};
451
452 for (const auto& dataField : messageFormat)
453 {
454 char* currentData = messageData.data.data() + currentExtractLocation;
455 if (dataField.name == "timestamp")
456 {
457 std::memcpy(&sensorGyro.timestamp, currentData, sizeof(sensorGyro.timestamp));
458 LOG_DATA("{}: sensorGyro.timestamp: {}", nameId(), sensorGyro.timestamp);
459 currentExtractLocation += sizeof(sensorGyro.timestamp);
460 }
461 else if (dataField.name == "timestamp_sample")
462 {
463 std::memcpy(&sensorGyro.timestamp_sample, currentData, sizeof(sensorGyro.timestamp_sample));
464 LOG_DATA("{}: sensorGyro.timestamp_sample: {}", nameId(), sensorGyro.timestamp_sample);
465 currentExtractLocation += sizeof(sensorGyro.timestamp_sample);
466 }
467 else if (dataField.name == "device_id")
468 {
469 std::memcpy(&sensorGyro.device_id, currentData, sizeof(sensorGyro.device_id));
470 LOG_DATA("{}: sensorGyro.device_id: {}", nameId(), sensorGyro.device_id);
471 currentExtractLocation += sizeof(sensorGyro.device_id);
472 }
473 else if (dataField.name == "x")
474 {
475 std::memcpy(&sensorGyro.x, currentData, sizeof(sensorGyro.x));
476 LOG_DATA("{}: sensorGyro.x: {}", nameId(), sensorGyro.x);
477 currentExtractLocation += sizeof(sensorGyro.x);
478 }
479 else if (dataField.name == "y")
480 {
481 std::memcpy(&sensorGyro.y, currentData, sizeof(sensorGyro.y));
482 LOG_DATA("{}: sensorGyro.y: {}", nameId(), sensorGyro.y);
483 currentExtractLocation += sizeof(sensorGyro.y);
484 }
485 else if (dataField.name == "z")
486 {
487 std::memcpy(&sensorGyro.z, currentData, sizeof(sensorGyro.z));
488 LOG_DATA("{}: sensorGyro.z: {}", nameId(), sensorGyro.z);
489 currentExtractLocation += sizeof(sensorGyro.z);
490 }
491 else if (dataField.name == "temperature")
492 {
493 std::memcpy(&sensorGyro.temperature, currentData, sizeof(sensorGyro.temperature));
494 LOG_DATA("{}: sensorGyro.temperature: {}", nameId(), sensorGyro.temperature);
495 currentExtractLocation += sizeof(sensorGyro.temperature);
496 }
497 else if (dataField.name == "error_count")
498 {
499 std::memcpy(&sensorGyro.error_count, currentData, sizeof(sensorGyro.error_count));
500 LOG_DATA("{}: sensorGyro.error_count: {}", nameId(), sensorGyro.error_count);
501 currentExtractLocation += sizeof(sensorGyro.error_count);
502 }
503 else
504 {
505 // FIXME: move 'currentExtractLocation', if yes, how far?
506 LOG_WARN("{}: dataField.name = '{}' or dataField.type = '{}' is unknown", nameId(), dataField.name, dataField.type);
507 }
508 }
509
510 LOG_DATA("{}: Inserting [{}] {}: {}", nameId(), sensorGyro.timestamp,
511 _subscribedMessages.at(messageData.msg_id).multi_id,
512 _subscribedMessages.at(messageData.msg_id).message_name);
513
514 _epochData.insert(std::make_pair(sensorGyro.timestamp,
515 MeasurementData{ .multi_id = _subscribedMessages.at(messageData.msg_id).multi_id,
516 .message_name = _subscribedMessages.at(messageData.msg_id).message_name,
517 .data = sensorGyro }));
518 }
519 else if (_subscribedMessages.at(messageData.msg_id).message_name == "sensor_mag")
520 {
521 SensorMag sensorMag{};
522 for (const auto& dataField : messageFormat)
523 {
524 char* currentData = messageData.data.data() + currentExtractLocation;
525 if (dataField.name == "timestamp")
526 {
527 std::memcpy(&sensorMag.timestamp, currentData, sizeof(sensorMag.timestamp));
528 LOG_DATA("{}: sensorMag.timestamp: {}", nameId(), sensorMag.timestamp);
529 currentExtractLocation += sizeof(sensorMag.timestamp);
530 }
531 else if (dataField.name == "timestamp_sample")
532 {
533 std::memcpy(&sensorMag.timestamp_sample, currentData, sizeof(sensorMag.timestamp_sample));
534 LOG_DATA("{}: sensorMag.timestamp_sample: {}", nameId(), sensorMag.timestamp_sample);
535 currentExtractLocation += sizeof(sensorMag.timestamp_sample);
536 }
537 else if (dataField.name == "device_id")
538 {
539 std::memcpy(&sensorMag.device_id, currentData, sizeof(sensorMag.device_id));
540 LOG_DATA("{}: sensorMag.device_id: {}", nameId(), sensorMag.device_id);
541 currentExtractLocation += sizeof(sensorMag.device_id);
542 }
543 else if (dataField.name == "x")
544 {
545 std::memcpy(&sensorMag.x, currentData, sizeof(sensorMag.x));
546 LOG_DATA("{}: sensorMag.x: {}", nameId(), sensorMag.x);
547 currentExtractLocation += sizeof(sensorMag.x);
548 }
549 else if (dataField.name == "y")
550 {
551 std::memcpy(&sensorMag.y, currentData, sizeof(sensorMag.y));
552 LOG_DATA("{}: sensorMag.y: {}", nameId(), sensorMag.y);
553 currentExtractLocation += sizeof(sensorMag.y);
554 }
555 else if (dataField.name == "z")
556 {
557 std::memcpy(&sensorMag.z, currentData, sizeof(sensorMag.z));
558 LOG_DATA("{}: sensorMag.z: {}", nameId(), sensorMag.z);
559 currentExtractLocation += sizeof(sensorMag.z);
560 }
561 else if (dataField.name == "temperature")
562 {
563 std::memcpy(&sensorMag.temperature, currentData, sizeof(sensorMag.temperature));
564 LOG_DATA("{}: sensorMag.temperature: {}", nameId(), sensorMag.temperature);
565 currentExtractLocation += sizeof(sensorMag.temperature);
566 }
567 else if (dataField.name == "error_count")
568 {
569 std::memcpy(&sensorMag.error_count, currentData, sizeof(sensorMag.error_count));
570 LOG_DATA("{}: sensorMag.error_count: {}", nameId(), sensorMag.error_count);
571 currentExtractLocation += sizeof(sensorMag.error_count);
572 }
573 else if (dataField.name == "is_external")
574 {
575 std::memcpy(&sensorMag.is_external, currentData, sizeof(sensorMag.is_external));
576 LOG_DATA("{}: sensorMag.is_external: {}", nameId(), sensorMag.is_external);
577 currentExtractLocation += sizeof(sensorMag.is_external);
578 }
579 else if (dataField.name.compare(0, 7, "_padding")) // e.g. '_padding0', '_padding1'
580 {
581 currentExtractLocation += SensorMag::padding; // Extraction Location should be moved to account for multiple padding
582 LOG_DATA("{}: sensorMag: padding", nameId());
583 }
584 else
585 {
586 // FIXME: move 'currentExtractLocation', if yes, how far?
587 LOG_WARN("{}: dataField.name = '{}' or dataField.type = '{}' is unknown", nameId(), dataField.name, dataField.type);
588 }
589 }
590
591 LOG_DATA("{}: Inserting [{}] {}: {}", nameId(), sensorMag.timestamp,
592 _subscribedMessages.at(messageData.msg_id).multi_id,
593 _subscribedMessages.at(messageData.msg_id).message_name);
594
595 _epochData.insert(std::make_pair(sensorMag.timestamp,
596 MeasurementData{ .multi_id = _subscribedMessages.at(messageData.msg_id).multi_id,
597 .message_name = _subscribedMessages.at(messageData.msg_id).message_name,
598 .data = sensorMag }));
599 }
600 else if (_subscribedMessages.at(messageData.msg_id).message_name == "vehicle_gps_position")
601 {
602 VehicleGpsPosition vehicleGpsPosition{};
603 for (const auto& dataField : messageFormat)
604 {
605 char* currentData = messageData.data.data() + currentExtractLocation;
606 if (dataField.name == "timestamp")
607 {
608 std::memcpy(&vehicleGpsPosition.timestamp, currentData, sizeof(vehicleGpsPosition.timestamp));
609 LOG_DATA("{}: vehicleGpsPosition.timestamp: {}", nameId(), vehicleGpsPosition.timestamp);
610 currentExtractLocation += sizeof(vehicleGpsPosition.timestamp);
611 }
612 else if (dataField.name == "time_utc_usec")
613 {
614 std::memcpy(&vehicleGpsPosition.time_utc_usec, currentData, sizeof(vehicleGpsPosition.time_utc_usec));
615 LOG_DATA("{}: vehicleGpsPosition.time_utc_usec: {}", nameId(), vehicleGpsPosition.time_utc_usec);
616 currentExtractLocation += sizeof(vehicleGpsPosition.time_utc_usec);
617 }
618 else if (dataField.name == "lat")
619 {
620 std::memcpy(&vehicleGpsPosition.lat, currentData, sizeof(vehicleGpsPosition.lat));
621 LOG_DATA("{}: vehicleGpsPosition.lat: {}", nameId(), vehicleGpsPosition.lat);
622 currentExtractLocation += sizeof(vehicleGpsPosition.lat);
623 }
624 else if (dataField.name == "lon")
625 {
626 std::memcpy(&vehicleGpsPosition.lon, currentData, sizeof(vehicleGpsPosition.lon));
627 LOG_DATA("{}: vehicleGpsPosition.lon: {}", nameId(), vehicleGpsPosition.lon);
628 currentExtractLocation += sizeof(vehicleGpsPosition.lon);
629 }
630 else if (dataField.name == "alt")
631 {
632 std::memcpy(&vehicleGpsPosition.alt, currentData, sizeof(vehicleGpsPosition.alt));
633 LOG_DATA("{}: vehicleGpsPosition.alt: {}", nameId(), vehicleGpsPosition.alt);
634 currentExtractLocation += sizeof(vehicleGpsPosition.alt);
635 }
636 else if (dataField.name == "alt_ellipsoid")
637 {
638 std::memcpy(&vehicleGpsPosition.alt_ellipsoid, currentData, sizeof(vehicleGpsPosition.alt_ellipsoid));
639 LOG_DATA("{}: vehicleGpsPosition.alt_ellipsoid: {}", nameId(), vehicleGpsPosition.alt_ellipsoid);
640 currentExtractLocation += sizeof(vehicleGpsPosition.alt_ellipsoid);
641 }
642 else if (dataField.name == "s_variance_m_s")
643 {
644 std::memcpy(&vehicleGpsPosition.s_variance_m_s, currentData, sizeof(vehicleGpsPosition.s_variance_m_s));
645 LOG_DATA("{}: vehicleGpsPosition.s_variance_m_s: {}", nameId(), vehicleGpsPosition.s_variance_m_s);
646 currentExtractLocation += sizeof(vehicleGpsPosition.s_variance_m_s);
647 }
648 else if (dataField.name == "c_variance_rad")
649 {
650 std::memcpy(&vehicleGpsPosition.c_variance_rad, currentData, sizeof(vehicleGpsPosition.c_variance_rad));
651 LOG_DATA("{}: vehicleGpsPosition.c_variance_rad: {}", nameId(), vehicleGpsPosition.c_variance_rad);
652 currentExtractLocation += sizeof(vehicleGpsPosition.c_variance_rad);
653 }
654 else if (dataField.name == "eph")
655 {
656 std::memcpy(&vehicleGpsPosition.eph, currentData, sizeof(vehicleGpsPosition.eph));
657 LOG_DATA("{}: vehicleGpsPosition.eph: {}", nameId(), vehicleGpsPosition.eph);
658 currentExtractLocation += sizeof(vehicleGpsPosition.eph);
659 }
660 else if (dataField.name == "epv")
661 {
662 std::memcpy(&vehicleGpsPosition.epv, currentData, sizeof(vehicleGpsPosition.epv));
663 LOG_DATA("{}: vehicleGpsPosition.epv: {}", nameId(), vehicleGpsPosition.epv);
664 currentExtractLocation += sizeof(vehicleGpsPosition.epv);
665 }
666 else if (dataField.name == "hdop")
667 {
668 std::memcpy(&vehicleGpsPosition.hdop, currentData, sizeof(vehicleGpsPosition.hdop));
669 LOG_DATA("{}: vehicleGpsPosition.hdop: {}", nameId(), vehicleGpsPosition.hdop);
670 currentExtractLocation += sizeof(vehicleGpsPosition.hdop);
671 }
672 else if (dataField.name == "vdop")
673 {
674 std::memcpy(&vehicleGpsPosition.vdop, currentData, sizeof(vehicleGpsPosition.lat));
675 LOG_DATA("{}: vehicleGpsPosition.vdop: {}", nameId(), vehicleGpsPosition.vdop);
676 currentExtractLocation += sizeof(vehicleGpsPosition.vdop);
677 }
678 else if (dataField.name == "noise_per_ms")
679 {
680 std::memcpy(&vehicleGpsPosition.noise_per_ms, currentData, sizeof(vehicleGpsPosition.noise_per_ms));
681 LOG_DATA("{}: vehicleGpsPosition.noise_per_ms: {}", nameId(), vehicleGpsPosition.noise_per_ms);
682 currentExtractLocation += sizeof(vehicleGpsPosition.noise_per_ms);
683 }
684 else if (dataField.name == "jamming_indicator")
685 {
686 std::memcpy(&vehicleGpsPosition.jamming_indicator, currentData, sizeof(vehicleGpsPosition.jamming_indicator));
687 LOG_DATA("{}: vehicleGpsPosition.jamming_indicator: {}", nameId(), vehicleGpsPosition.jamming_indicator);
688 currentExtractLocation += sizeof(vehicleGpsPosition.jamming_indicator);
689 }
690 else if (dataField.name == "vel_m_s")
691 {
692 std::memcpy(&vehicleGpsPosition.vel_m_s, currentData, sizeof(vehicleGpsPosition.vel_m_s));
693 LOG_DATA("{}: vehicleGpsPosition.vel_m_s: {}", nameId(), vehicleGpsPosition.vel_m_s);
694 currentExtractLocation += sizeof(vehicleGpsPosition.vel_m_s);
695 }
696 else if (dataField.name == "vel_n_m_s")
697 {
698 std::memcpy(&vehicleGpsPosition.vel_n_m_s, currentData, sizeof(vehicleGpsPosition.vel_n_m_s));
699 LOG_DATA("{}: vehicleGpsPosition.vel_n_m_s: {}", nameId(), vehicleGpsPosition.vel_n_m_s);
700 currentExtractLocation += sizeof(vehicleGpsPosition.vel_n_m_s);
701 }
702 else if (dataField.name == "vel_e_m_s")
703 {
704 std::memcpy(&vehicleGpsPosition.vel_e_m_s, currentData, sizeof(vehicleGpsPosition.vel_e_m_s));
705 LOG_DATA("{}: vehicleGpsPosition.vel_e_m_s: {}", nameId(), vehicleGpsPosition.vel_e_m_s);
706 currentExtractLocation += sizeof(vehicleGpsPosition.vel_e_m_s);
707 }
708 else if (dataField.name == "vel_d_m_s")
709 {
710 std::memcpy(&vehicleGpsPosition.vel_d_m_s, currentData, sizeof(vehicleGpsPosition.vel_d_m_s));
711 LOG_DATA("{}: vehicleGpsPosition.vel_d_m_s: {}", nameId(), vehicleGpsPosition.vel_d_m_s);
712 currentExtractLocation += sizeof(vehicleGpsPosition.vel_d_m_s);
713 }
714 else if (dataField.name == "cog_rad")
715 {
716 std::memcpy(&vehicleGpsPosition.cog_rad, currentData, sizeof(vehicleGpsPosition.cog_rad));
717 LOG_DATA("{}: vehicleGpsPosition.cog_rad: {}", nameId(), vehicleGpsPosition.cog_rad);
718 currentExtractLocation += sizeof(vehicleGpsPosition.cog_rad);
719 }
720 else if (dataField.name == "timestamp_time_relative")
721 {
722 std::memcpy(&vehicleGpsPosition.timestamp_time_relative, currentData, sizeof(vehicleGpsPosition.timestamp_time_relative));
723 LOG_DATA("{}: vehicleGpsPosition.timestamp_time_relative: {}", nameId(), vehicleGpsPosition.timestamp_time_relative);
724 currentExtractLocation += sizeof(vehicleGpsPosition.timestamp_time_relative);
725 }
726 else if (dataField.name == "heading")
727 {
728 std::memcpy(&vehicleGpsPosition.heading, currentData, sizeof(vehicleGpsPosition.heading));
729 LOG_DATA("{}: vehicleGpsPosition.heading: {}", nameId(), vehicleGpsPosition.heading);
730 currentExtractLocation += sizeof(vehicleGpsPosition.heading);
731 }
732 else if (dataField.name == "heading_offset")
733 {
734 std::memcpy(&vehicleGpsPosition.heading_offset, currentData, sizeof(vehicleGpsPosition.heading_offset));
735 LOG_DATA("{}: vehicleGpsPosition.heading_offset: {}", nameId(), vehicleGpsPosition.heading_offset);
736 currentExtractLocation += sizeof(vehicleGpsPosition.heading_offset);
737 }
738 else if (dataField.name == "fix_type")
739 {
740 std::memcpy(&vehicleGpsPosition.fix_type, currentData, sizeof(vehicleGpsPosition.fix_type));
741 LOG_DATA("{}: vehicleGpsPosition.fix_type: {}", nameId(), vehicleGpsPosition.fix_type);
742 currentExtractLocation += sizeof(vehicleGpsPosition.fix_type);
743 }
744 else if (dataField.name == "vel_ned_valid")
745 {
746 std::memcpy(&vehicleGpsPosition.vel_ned_valid, currentData, sizeof(vehicleGpsPosition.vel_ned_valid));
747 LOG_DATA("{}: vehicleGpsPosition.vel_ned_valid: {}", nameId(), vehicleGpsPosition.vel_ned_valid);
748 currentExtractLocation += sizeof(vehicleGpsPosition.vel_ned_valid);
749 }
750 else if (dataField.name == "satellites_used")
751 {
752 std::memcpy(&vehicleGpsPosition.satellites_used, currentData, sizeof(vehicleGpsPosition.satellites_used));
753 LOG_DATA("{}: vehicleGpsPosition.satellites_used: {}", nameId(), vehicleGpsPosition.satellites_used);
754 currentExtractLocation += sizeof(vehicleGpsPosition.satellites_used);
755 }
756 else if (dataField.name.compare(0, 7, "_padding")) // e.g. '_padding0', '_padding1'
757 {
758 currentExtractLocation += VehicleGpsPosition::padding; // Extraction Location should be moved to account for multiple padding
759 LOG_DATA("{}: vehicleGpsPosition: padding", nameId());
760 }
761 else
762 {
763 // FIXME: move 'currentExtractLocation', if yes, how far?
764 LOG_WARN("{}: dataField.name = '{}' or dataField.type = '{}' is unknown", nameId(), dataField.name, dataField.type);
765 }
766 }
767
768 if (lastGnssTime.timeSinceStartup)
769 {
770 [[maybe_unused]] auto newGnssTime = InsTime(1970, 1, 1, 0, 0, vehicleGpsPosition.time_utc_usec * 1e-6L);
771 LOG_DATA("{}: Updating GnssTime from {} to {} (Diff {} sec)", nameId(), lastGnssTime.gnssTime.toYMDHMS(), newGnssTime.toYMDHMS(), static_cast<double>((newGnssTime - lastGnssTime.gnssTime).count()));
772 LOG_DATA("{}: Updating tStartup from {} to {} (Diff {} sec)", nameId(), lastGnssTime.timeSinceStartup, vehicleGpsPosition.timestamp, (vehicleGpsPosition.timestamp - lastGnssTime.timeSinceStartup) * 1e-6L);
773 }
774
775 lastGnssTime.gnssTime = InsTime(1970, 1, 1, 0, 0, vehicleGpsPosition.time_utc_usec * 1e-6L);
776 lastGnssTime.timeSinceStartup = vehicleGpsPosition.timestamp;
777
778 while (true) // Delete all old VehicleGpsPosition entries
779 {
780 auto iter = std::ranges::find_if(_epochData, [](const std::pair<uint64_t, MeasurementData>& v) {
781 return std::holds_alternative<UlogFile::VehicleGpsPosition>(v.second.data);
782 });
783 if (iter == _epochData.end())
784 {
785 break;
786 }
787 _epochData.erase(iter);
788 }
789
790 LOG_DATA("{}: Inserting [{}] {}: {}", nameId(), vehicleGpsPosition.timestamp,
791 _subscribedMessages.at(messageData.msg_id).multi_id,
792 _subscribedMessages.at(messageData.msg_id).message_name);
793
794 _epochData.insert(std::make_pair(vehicleGpsPosition.timestamp,
795 MeasurementData{ .multi_id = _subscribedMessages.at(messageData.msg_id).multi_id,
796 .message_name = _subscribedMessages.at(messageData.msg_id).message_name,
797 .data = vehicleGpsPosition }));
798 }
799 else if (_subscribedMessages.at(messageData.msg_id).message_name == "vehicle_attitude")
800 {
801 VehicleAttitude vehicleAttitude{};
802 for (const auto& dataField : messageFormat)
803 {
804 char* currentData = messageData.data.data() + currentExtractLocation;
805 if (dataField.name == "timestamp")
806 {
807 std::memcpy(&vehicleAttitude.timestamp, currentData, sizeof(vehicleAttitude.timestamp));
808 LOG_DATA("{}: vehicleAttitude.timestamp: {}", nameId(), vehicleAttitude.timestamp);
809 currentExtractLocation += sizeof(vehicleAttitude.timestamp);
810 }
811 else if (dataField.name == "q")
812 {
813 std::memcpy(vehicleAttitude.q.data(), currentData, vehicleAttitude.q.size());
814 LOG_DATA("{}: vehicleAttitude.q: {}", nameId(), fmt::join(vehicleAttitude.q, ", "));
815 currentExtractLocation += vehicleAttitude.q.size();
816 }
817 else if (dataField.name == "delta_q_reset")
818 {
819 std::memcpy(vehicleAttitude.delta_q_reset.data(), currentData, vehicleAttitude.delta_q_reset.size());
820 LOG_DATA("{}: vehicleAttitude.delta_q_reset: {}", nameId(), fmt::join(vehicleAttitude.delta_q_reset, ", "));
821 currentExtractLocation += vehicleAttitude.delta_q_reset.size();
822 }
823 if (dataField.name == "quat_reset_counter")
824 {
825 std::memcpy(&vehicleAttitude.quat_reset_counter, currentData, sizeof(vehicleAttitude.quat_reset_counter));
826 LOG_DATA("{}: vehicleAttitude.quat_reset_counter: {}", nameId(), vehicleAttitude.quat_reset_counter);
827 currentExtractLocation += sizeof(vehicleAttitude.quat_reset_counter);
828 }
829 else if (dataField.name.compare(0, 7, "_padding")) // e.g. '_padding0', '_padding1'
830 {
831 currentExtractLocation += VehicleAttitude::padding; // Extraction Location should be moved to account for multiple padding
832 LOG_DATA("{}: VehicleAttitude: padding", nameId());
833 }
834 else
835 {
836 // FIXME: move 'currentExtractLocation', if yes, how far?
837 LOG_WARN("{}: dataField.name = '{}' or dataField.type = '{}' is unknown", nameId(), dataField.name, dataField.type);
838 }
839 }
840
841 while (true) // Delete all old VehicleAttitude entries
842 {
843 auto iter = std::ranges::find_if(_epochData, [](const std::pair<uint64_t, MeasurementData>& v) {
844 return std::holds_alternative<UlogFile::VehicleAttitude>(v.second.data);
845 });
846 if (iter == _epochData.end())
847 {
848 break;
849 }
850 _epochData.erase(iter);
851 }
852
853 LOG_DATA("{}: Inserting [{}] {}: {}", nameId(), vehicleAttitude.timestamp,
854 _subscribedMessages.at(messageData.msg_id).multi_id,
855 _subscribedMessages.at(messageData.msg_id).message_name);
856
857 _epochData.insert(std::make_pair(vehicleAttitude.timestamp,
858 MeasurementData{ .multi_id = _subscribedMessages.at(messageData.msg_id).multi_id,
859 .message_name = _subscribedMessages.at(messageData.msg_id).message_name,
860 .data = vehicleAttitude }));
861 }
862 else if (_subscribedMessages.at(messageData.msg_id).message_name == "vehicle_control_mode")
863 {
864 VehicleControlMode vehicleControlMode{};
865 for (const auto& dataField : messageFormat)
866 {
867 char* currentData = messageData.data.data() + currentExtractLocation;
868 if (dataField.name == "timestamp")
869 {
870 std::memcpy(&vehicleControlMode.timestamp, currentData, sizeof(vehicleControlMode.timestamp));
871 LOG_DATA("{}: vehicleControlMode.timestamp: {}", nameId(), vehicleControlMode.timestamp);
872 currentExtractLocation += sizeof(vehicleControlMode.timestamp);
873 }
874 else if (dataField.name == "flag_armed")
875 {
876 std::memcpy(&vehicleControlMode.flag_armed, currentData, sizeof(vehicleControlMode.flag_armed));
877 LOG_DATA("{}: vehicleControlMode.flag_armed: {}", nameId(), vehicleControlMode.flag_armed);
878 currentExtractLocation += sizeof(vehicleControlMode.flag_armed);
879 }
880 else if (dataField.name == "flag_external_manual_override_ok")
881 {
882 std::memcpy(&vehicleControlMode.flag_external_manual_override_ok, currentData, sizeof(vehicleControlMode.flag_external_manual_override_ok));
883 LOG_DATA("{}: vehicleControlMode.flag_external_manual_override_ok: {}", nameId(), vehicleControlMode.flag_external_manual_override_ok);
884 currentExtractLocation += sizeof(vehicleControlMode.flag_external_manual_override_ok);
885 }
886 else if (dataField.name == "flag_control_manual_enabled")
887 {
888 std::memcpy(&vehicleControlMode.flag_control_manual_enabled, currentData, sizeof(vehicleControlMode.flag_control_manual_enabled));
889 LOG_DATA("{}: vehicleControlMode.flag_control_manual_enabled: {}", nameId(), vehicleControlMode.flag_control_manual_enabled);
890 currentExtractLocation += sizeof(vehicleControlMode.flag_control_manual_enabled);
891 }
892 else if (dataField.name == "flag_control_auto_enabled")
893 {
894 std::memcpy(&vehicleControlMode.flag_control_auto_enabled, currentData, sizeof(vehicleControlMode.flag_control_auto_enabled));
895 LOG_DATA("{}: vehicleControlMode.flag_control_auto_enabled: {}", nameId(), vehicleControlMode.flag_control_auto_enabled);
896 currentExtractLocation += sizeof(vehicleControlMode.flag_control_auto_enabled);
897 }
898 else if (dataField.name == "flag_control_offboard_enabled")
899 {
900 std::memcpy(&vehicleControlMode.flag_control_offboard_enabled, currentData, sizeof(vehicleControlMode.flag_control_offboard_enabled));
901 LOG_DATA("{}: vehicleControlMode.flag_control_offboard_enabled: {}", nameId(), vehicleControlMode.flag_control_offboard_enabled);
902 currentExtractLocation += sizeof(vehicleControlMode.flag_control_offboard_enabled);
903 }
904 else if (dataField.name == "flag_control_rates_enabled")
905 {
906 std::memcpy(&vehicleControlMode.flag_control_rates_enabled, currentData, sizeof(vehicleControlMode.flag_control_rates_enabled));
907 LOG_DATA("{}: vehicleControlMode.flag_control_rates_enabled: {}", nameId(), vehicleControlMode.flag_control_rates_enabled);
908 currentExtractLocation += sizeof(vehicleControlMode.flag_control_rates_enabled);
909 }
910 else if (dataField.name == "flag_control_attitude_enabled")
911 {
912 std::memcpy(&vehicleControlMode.flag_control_attitude_enabled, currentData, sizeof(vehicleControlMode.flag_control_attitude_enabled));
913 LOG_DATA("{}: vehicleControlMode.flag_control_attitude_enabled: {}", nameId(), vehicleControlMode.flag_control_attitude_enabled);
914 currentExtractLocation += sizeof(vehicleControlMode.flag_control_attitude_enabled);
915 }
916 else if (dataField.name == "flag_control_yawrate_override_enabled")
917 {
918 std::memcpy(&vehicleControlMode.flag_control_yawrate_override_enabled, currentData, sizeof(vehicleControlMode.flag_control_yawrate_override_enabled));
919 LOG_DATA("{}: vehicleControlMode.flag_control_yawrate_override_enabled: {}", nameId(), vehicleControlMode.flag_control_yawrate_override_enabled);
920 currentExtractLocation += sizeof(vehicleControlMode.flag_control_yawrate_override_enabled);
921 }
922 else if (dataField.name == "flag_control_rattitude_enabled")
923 {
924 std::memcpy(&vehicleControlMode.flag_control_rattitude_enabled, currentData, sizeof(vehicleControlMode.flag_control_rattitude_enabled));
925 LOG_DATA("{}: vehicleControlMode.flag_control_rattitude_enabled: {}", nameId(), vehicleControlMode.flag_control_rattitude_enabled);
926 currentExtractLocation += sizeof(vehicleControlMode.flag_control_rattitude_enabled);
927 }
928 else if (dataField.name == "flag_control_force_enabled")
929 {
930 std::memcpy(&vehicleControlMode.flag_control_force_enabled, currentData, sizeof(vehicleControlMode.flag_control_force_enabled));
931 LOG_DATA("{}: vehicleControlMode.flag_control_force_enabled: {}", nameId(), vehicleControlMode.flag_control_force_enabled);
932 currentExtractLocation += sizeof(vehicleControlMode.flag_control_force_enabled);
933 }
934 else if (dataField.name == "flag_control_acceleration_enabled")
935 {
936 std::memcpy(&vehicleControlMode.flag_control_acceleration_enabled, currentData, sizeof(vehicleControlMode.flag_control_acceleration_enabled));
937 LOG_DATA("{}: vehicleControlMode.flag_control_acceleration_enabled: {}", nameId(), vehicleControlMode.flag_control_acceleration_enabled);
938 currentExtractLocation += sizeof(vehicleControlMode.flag_control_acceleration_enabled);
939 }
940 else if (dataField.name == "flag_control_velocity_enabled")
941 {
942 std::memcpy(&vehicleControlMode.flag_control_velocity_enabled, currentData, sizeof(vehicleControlMode.flag_control_velocity_enabled));
943 LOG_DATA("{}: vehicleControlMode.flag_control_velocity_enabled: {}", nameId(), vehicleControlMode.flag_control_velocity_enabled);
944 currentExtractLocation += sizeof(vehicleControlMode.flag_control_velocity_enabled);
945 }
946 else if (dataField.name == "flag_control_position_enabled")
947 {
948 std::memcpy(&vehicleControlMode.flag_control_position_enabled, currentData, sizeof(vehicleControlMode.flag_control_position_enabled));
949 LOG_DATA("{}: vehicleControlMode.flag_control_position_enabled: {}", nameId(), vehicleControlMode.flag_control_position_enabled);
950 currentExtractLocation += sizeof(vehicleControlMode.flag_control_position_enabled);
951 }
952 else if (dataField.name == "flag_control_altitude_enabled")
953 {
954 std::memcpy(&vehicleControlMode.flag_control_altitude_enabled, currentData, sizeof(vehicleControlMode.flag_control_altitude_enabled));
955 LOG_DATA("{}: vehicleControlMode.flag_control_altitude_enabled: {}", nameId(), vehicleControlMode.flag_control_altitude_enabled);
956 currentExtractLocation += sizeof(vehicleControlMode.flag_control_altitude_enabled);
957 }
958 else if (dataField.name == "flag_control_climb_rate_enabled")
959 {
960 std::memcpy(&vehicleControlMode.flag_control_climb_rate_enabled, currentData, sizeof(vehicleControlMode.flag_control_climb_rate_enabled));
961 LOG_DATA("{}: vehicleControlMode.flag_control_climb_rate_enabled: {}", nameId(), vehicleControlMode.flag_control_climb_rate_enabled);
962 currentExtractLocation += sizeof(vehicleControlMode.flag_control_climb_rate_enabled);
963 }
964 else if (dataField.name == "flag_control_termination_enabled")
965 {
966 std::memcpy(&vehicleControlMode.flag_control_termination_enabled, currentData, sizeof(vehicleControlMode.flag_control_termination_enabled));
967 LOG_DATA("{}: vehicleControlMode.flag_control_termination_enabled: {}", nameId(), vehicleControlMode.flag_control_termination_enabled);
968 currentExtractLocation += sizeof(vehicleControlMode.flag_control_termination_enabled);
969 }
970 else if (dataField.name == "flag_control_fixed_hdg_enabled")
971 {
972 std::memcpy(&vehicleControlMode.flag_control_fixed_hdg_enabled, currentData, sizeof(vehicleControlMode.flag_control_fixed_hdg_enabled));
973 LOG_DATA("{}: vehicleControlMode.flag_control_fixed_hdg_enabled: {}", nameId(), vehicleControlMode.flag_control_fixed_hdg_enabled);
974 currentExtractLocation += sizeof(vehicleControlMode.flag_control_fixed_hdg_enabled);
975 }
976 else if (dataField.name.compare(0, 7, "_padding")) // e.g. '_padding0', '_padding1'
977 {
978 currentExtractLocation += VehicleControlMode::padding; // Extraction Location should be moved to account for multiple padding
979 LOG_DATA("{}: VehicleControlMode: padding", nameId());
980 }
981 else
982 {
983 // FIXME: move 'currentExtractLocation', if yes, how far?
984 LOG_WARN("{}: dataField.name = '{}' or dataField.type = '{}' is unknown", nameId(), dataField.name, dataField.type);
985 }
986 }
987 // TODO: insert to _epochData necessary for 'vehicleControlMode'?
988 }
989 else if (_subscribedMessages.at(messageData.msg_id).message_name == "vehicle_air_data")
990 {
991 VehicleAirData vehicleAirData{};
992 for (const auto& dataField : messageFormat)
993 {
994 char* currentData = messageData.data.data() + currentExtractLocation;
995 if (dataField.name == "timestamp")
996 {
997 std::memcpy(&vehicleAirData.timestamp, currentData, sizeof(vehicleAirData.timestamp));
998 LOG_DATA("{}: vehicleAirData.timestamp: {}", nameId(), vehicleAirData.timestamp);
999 currentExtractLocation += sizeof(vehicleAirData.timestamp);
1000 }
1001 else if (dataField.name == "timestamp_sample")
1002 {
1003 std::memcpy(&vehicleAirData.timestamp_sample, currentData, sizeof(vehicleAirData.timestamp_sample));
1004 LOG_DATA("{}: vehicleAirData.timestamp_sample: {}", nameId(), vehicleAirData.timestamp_sample);
1005 currentExtractLocation += sizeof(vehicleAirData.timestamp_sample);
1006 }
1007 else if (dataField.name == "baro_device_id")
1008 {
1009 std::memcpy(&vehicleAirData.baro_device_id, currentData, sizeof(vehicleAirData.baro_device_id));
1010 LOG_DATA("{}: vehicleAirData.baro_device_id: {}", nameId(), vehicleAirData.baro_device_id);
1011 currentExtractLocation += sizeof(vehicleAirData.baro_device_id);
1012 }
1013 else if (dataField.name == "baro_alt_meter")
1014 {
1015 std::memcpy(&vehicleAirData.baro_alt_meter, currentData, sizeof(vehicleAirData.baro_alt_meter));
1016 LOG_DATA("{}: vehicleAirData.baro_alt_meter: {}", nameId(), vehicleAirData.baro_alt_meter);
1017 currentExtractLocation += sizeof(vehicleAirData.baro_alt_meter);
1018 }
1019 else if (dataField.name == "baro_temp_celcius")
1020 {
1021 std::memcpy(&vehicleAirData.baro_temp_celcius, currentData, sizeof(vehicleAirData.baro_temp_celcius));
1022 LOG_DATA("{}: vehicleAirData.baro_temp_celcius: {}", nameId(), vehicleAirData.baro_temp_celcius);
1023 currentExtractLocation += sizeof(vehicleAirData.baro_temp_celcius);
1024 }
1025 else if (dataField.name == "baro_pressure_pa")
1026 {
1027 std::memcpy(&vehicleAirData.baro_pressure_pa, currentData, sizeof(vehicleAirData.baro_pressure_pa));
1028 LOG_DATA("{}: vehicleAirData.baro_pressure_pa: {}", nameId(), vehicleAirData.baro_pressure_pa);
1029 currentExtractLocation += sizeof(vehicleAirData.baro_pressure_pa);
1030 }
1031 else if (dataField.name == "rho")
1032 {
1033 std::memcpy(&vehicleAirData.rho, currentData, sizeof(vehicleAirData.rho));
1034 LOG_DATA("{}: vehicleAirData.rho: {}", nameId(), vehicleAirData.rho);
1035 currentExtractLocation += sizeof(vehicleAirData.rho);
1036 }
1037 else if (dataField.name.compare(0, 7, "_padding")) // e.g. '_padding0', '_padding1'
1038 {
1039 currentExtractLocation += VehicleAirData::padding; // Extraction Location should be moved to account for multiple padding
1040 LOG_DATA("{}: VehicleControlMode: padding", nameId());
1041 }
1042 else
1043 {
1044 // FIXME: move 'currentExtractLocation', if yes, how far?
1045 LOG_WARN("{}: dataField.name = '{}' or dataField.type = '{}' is unknown", nameId(), dataField.name, dataField.type);
1046 }
1047 }
1048 // TODO: insert to _epochData necessary for 'vehicleAirData'?
1049 }
1050 else
1051 {
1052 LOG_DATA("{}: UKNOWN: _subscribedMessages.at(messageData.msg_id).message_name = {}", nameId(), _subscribedMessages.at(messageData.msg_id).message_name);
1053 }
1054
1055 // #########################################################################################################################################
1056 // Callbacks
1057 // #########################################################################################################################################
1058 // This is the hasEnoughData check for an ImuObs
1059 if (auto multi_id = enoughImuDataAvailable();
1060 multi_id >= 0)
1061 {
1062 LOG_DATA("{}: Construct ImuObs and invoke callback", nameId());
1063
1064 auto obs = std::make_shared<ImuObs>(this->_imuPos);
1065
1066 uint64_t timeSinceStartupNew{};
1067
1068 for ([[maybe_unused]] const auto& [timestamp, measurement] : _epochData)
1069 {
1070 LOG_DATA("{}: [{}] {}: {}", nameId(), timestamp, measurement.multi_id, measurement.message_name);
1071 }
1072
1073 bool accelFound = false;
1074 bool angularRateFound = false;
1075 // Construct ImuObs
1076 for (auto it = _epochData.begin(); it != _epochData.end();)
1077 {
1078 // Add accel data to ImuObs
1079 if (std::holds_alternative<SensorAccel>(it->second.data) && (it->second.multi_id == static_cast<uint8_t>(multi_id))
1080 && !accelFound)
1081 {
1082 accelFound = true;
1083 timeSinceStartupNew = it->first;
1084 float accelX = std::get<SensorAccel>(it->second.data).x;
1085 float accelY = std::get<SensorAccel>(it->second.data).y;
1086 float accelZ = std::get<SensorAccel>(it->second.data).z;
1087 obs->p_acceleration = { accelX, accelY, accelZ };
1088 auto delIt = it;
1089 ++it;
1090 _epochData.erase(delIt);
1091 LOG_DATA("{}: accelX = {}, accelY = {}, accelZ = {}", nameId(), accelX, accelY, accelZ);
1092 }
1093 // Add gyro data to ImuObs
1094 else if (std::holds_alternative<SensorGyro>(it->second.data) && (it->second.multi_id == static_cast<uint8_t>(multi_id))
1095 && !angularRateFound)
1096 {
1097 angularRateFound = true;
1098 timeSinceStartupNew = it->first;
1099 float gyroX = std::get<SensorGyro>(it->second.data).x;
1100 float gyroY = std::get<SensorGyro>(it->second.data).y;
1101 float gyroZ = std::get<SensorGyro>(it->second.data).z;
1102 obs->p_angularRate = { gyroX, gyroY, gyroZ };
1103 auto delIt = it;
1104 ++it;
1105 _epochData.erase(delIt);
1106 LOG_DATA("{}: gyroX = {}, gyroY = {}, gyroZ = {}", nameId(), gyroX, gyroY, gyroZ);
1107 }
1108 // Add mag data to ImuObs
1109 else if (std::holds_alternative<SensorMag>(it->second.data) && (it->second.multi_id == static_cast<uint8_t>(multi_id))
1110 && !obs->p_magneticField.has_value()) // TODO: Find out what is multi_id = 1. Px4 Mini is supposed to have only one magnetometer
1111 {
1112 float magX = std::get<SensorMag>(it->second.data).x;
1113 float magY = std::get<SensorMag>(it->second.data).y;
1114 float magZ = std::get<SensorMag>(it->second.data).z;
1115 obs->p_magneticField.emplace(magX, magY, magZ);
1116 auto delIt = it;
1117 ++it;
1118 _epochData.erase(delIt);
1119 LOG_DATA("{}: magX = {}, magY = {}, magZ = {}", nameId(), magX, magY, magZ);
1120 }
1121 else
1122 {
1123 ++it;
1124 }
1125 }
1126
1127 obs->insTime = lastGnssTime.gnssTime + std::chrono::microseconds(static_cast<int64_t>(timeSinceStartupNew) - static_cast<int64_t>(lastGnssTime.timeSinceStartup));
1128
1129 LOG_DATA("{}: Sending out ImuObs {}: {}", nameId(), multi_id, obs->insTime.toYMDHMS());
1130 if (multi_id == 0)
1131 {
1133 }
1134 else if (multi_id == 1)
1135 {
1137 }
1138 else
1139 {
1140 LOG_ERROR("{}: multi_id = {} is invalid", nameId(), multi_id);
1141 }
1142 return obs;
1143 }
1144 if (auto [gpsIter, attIter] = findPosVelAttData();
1145 gpsIter != _epochData.end() && attIter != _epochData.end())
1146 {
1147 LOG_DATA("{}: Construct PosVelAtt and invoke callback", nameId());
1148
1149 auto obs = std::make_shared<NAV::PosVelAtt>();
1150
1151 const auto& vehicleGpsPosition = std::get<VehicleGpsPosition>(gpsIter->second.data);
1152 const auto& vehicleAttitude = std::get<VehicleAttitude>(attIter->second.data);
1153
1154 obs->insTime = InsTime(0, 0, 0, 0, 0, vehicleGpsPosition.time_utc_usec * 1e-6L);
1155 obs->setPosVelAtt_n(Eigen::Vector3d{ deg2rad(1e-7 * static_cast<double>(vehicleGpsPosition.lat)), deg2rad(1e-7 * static_cast<double>(vehicleGpsPosition.lon)), 1e-3 * (static_cast<double>(vehicleGpsPosition.alt_ellipsoid)) },
1156 Eigen::Vector3d{ vehicleGpsPosition.vel_n_m_s, vehicleGpsPosition.vel_e_m_s, vehicleGpsPosition.vel_d_m_s },
1157 Eigen::Quaterniond{ vehicleAttitude.q.at(0), vehicleAttitude.q.at(1), vehicleAttitude.q.at(2), vehicleAttitude.q.at(3) });
1158 // TODO: Check order of w,x,y,z
1159 // TODO: Check if this is quaternion_nb
1160
1161 // Above it is ensured that only one gps and att element is present.
1162 // Here we still need to delete the used elements, otherwise the next iteration would find the elements again.
1163 _epochData.erase(gpsIter);
1164 _epochData.erase(attIter);
1165
1166 LOG_DATA("{}: Sending out PosVelAtt: {}", nameId(), obs->insTime.toYMDHMS());
1168 return obs;
1169 }
1170 }
1171 else if (ulogMsgHeader.msgHeader.msg_type == 'L')
1172 {
1174 messageLog.header = ulogMsgHeader.msgHeader;
1175 read(reinterpret_cast<char*>(&messageLog.log_level), sizeof(messageLog.log_level));
1176
1177 if (messageLog.log_level == 48) // '0'
1178 {
1179 LOG_DATA("{}: Log-level: EMERG - System is unusable", nameId());
1180 }
1181 else if (messageLog.log_level == 49) // '1'
1182 {
1183 LOG_DATA("{}: Log-level: ALERT - Action must be taken immediately", nameId());
1184 }
1185 else if (messageLog.log_level == 50) // '2'
1186 {
1187 LOG_DATA("{}: Log-level: CRIT - Critical conditions", nameId());
1188 }
1189 else if (messageLog.log_level == 51) // '3'
1190 {
1191 LOG_DATA("{}: Log-level: ERR - Error conditions", nameId());
1192 }
1193 else if (messageLog.log_level == 52) // '4'
1194 {
1195 LOG_DATA("{}: Log-level: WARNING - Warning conditions", nameId());
1196 }
1197 else if (messageLog.log_level == 53) // '5'
1198 {
1199 LOG_DATA("{}: Log-level: NOTICE - Normal but significant condition", nameId());
1200 }
1201 else if (messageLog.log_level == 54) // '6'
1202 {
1203 LOG_DATA("{}: Log-level: INFO - Informational", nameId());
1204 }
1205 else if (messageLog.log_level == 55) // '7'
1206 {
1207 LOG_DATA("{}: Log-level: DEBUG - Debug-level messages", nameId());
1208 }
1209 else
1210 {
1211 LOG_WARN("{}: Log-level is out of scope ({}) - possible data loss", nameId(), messageLog.log_level);
1212 }
1213
1214 read(reinterpret_cast<char*>(&messageLog.timestamp), sizeof(messageLog.timestamp));
1215 LOG_DATA("{}: messageLog.timestamp [µs]: {}", nameId(), messageLog.timestamp);
1216
1217 messageLog.message.resize(messageLog.header.msg_size - 9);
1218 read(messageLog.message.data(), messageLog.header.msg_size - 9);
1219 LOG_DATA("{}: messageLog.message: {}", nameId(), messageLog.message);
1220 }
1221 else if (ulogMsgHeader.msgHeader.msg_type == 'C')
1222 {
1224 messageLogTagged.header = ulogMsgHeader.msgHeader;
1225 read(reinterpret_cast<char*>(&messageLogTagged.log_level), sizeof(messageLogTagged.log_level));
1226
1227 if (messageLogTagged.log_level == 48) // '0'
1228 {
1229 LOG_DATA("{}: Log-level: EMERG - System is unusable", nameId());
1230 }
1231 else if (messageLogTagged.log_level == 49) // '1'
1232 {
1233 LOG_DATA("{}: Log-level: ALERT - Action must be taken immediately", nameId());
1234 }
1235 else if (messageLogTagged.log_level == 50) // '2'
1236 {
1237 LOG_DATA("{}: Log-level: CRIT - Critical conditions", nameId());
1238 }
1239 else if (messageLogTagged.log_level == 51) // '3'
1240 {
1241 LOG_DATA("{}: Log-level: ERR - Error conditions", nameId());
1242 }
1243 else if (messageLogTagged.log_level == 52) // '4'
1244 {
1245 LOG_DATA("{}: Log-level: WARNING - Warning conditions", nameId());
1246 }
1247 else if (messageLogTagged.log_level == 53) // '5'
1248 {
1249 LOG_DATA("{}: Log-level: NOTICE - Normal but significant condition", nameId());
1250 }
1251 else if (messageLogTagged.log_level == 54) // '6'
1252 {
1253 LOG_DATA("{}: Log-level: INFO - Informational", nameId());
1254 }
1255 else if (messageLogTagged.log_level == 55) // '7'
1256 {
1257 LOG_DATA("{}: Log-level: DEBUG - Debug-level messages", nameId());
1258 }
1259 else
1260 {
1261 LOG_WARN("{}: Log-level is out of scope ({}) - possible data loss", nameId(), messageLogTagged.log_level);
1262 }
1263
1264 read(reinterpret_cast<char*>(&messageLogTagged.tag), sizeof(messageLogTagged.tag));
1265 LOG_DATA("{}: messageLogTagged.tag: {}", nameId(), messageLogTagged.tag);
1266 read(reinterpret_cast<char*>(&messageLogTagged.timestamp), sizeof(messageLogTagged.timestamp));
1267 LOG_DATA("{}: messageLogTagged.timestamp [µs]: {}", nameId(), messageLogTagged.timestamp);
1268
1269 messageLogTagged.message.resize(messageLogTagged.header.msg_size - 11);
1270 read(messageLogTagged.message.data(), messageLogTagged.header.msg_size - 11);
1271 LOG_DATA("{}: messageLogTagged.header.msg_size: {}", nameId(), messageLogTagged.header.msg_size);
1272 }
1273 else if (ulogMsgHeader.msgHeader.msg_type == 'S')
1274 {
1276 messageSync.header = ulogMsgHeader.msgHeader;
1277 read(reinterpret_cast<char*>(messageSync.syncMsg.data()), sizeof(messageSync.syncMsg));
1278 LOG_DATA("{}: messageSync.syncMsg[0]: {}", nameId(), messageSync.syncMsg[0]);
1279 }
1280 else if (ulogMsgHeader.msgHeader.msg_type == 'O')
1281 {
1283 messageDropout.header = ulogMsgHeader.msgHeader;
1284 read(reinterpret_cast<char*>(&messageDropout.duration), sizeof(messageDropout.duration));
1285 LOG_WARN("{}: Dropout of duration: {} ms", nameId(), messageDropout.duration);
1286 }
1287 else if (ulogMsgHeader.msgHeader.msg_type == 'I')
1288 {
1289 readInformationMessage(ulogMsgHeader.msgHeader.msg_size, ulogMsgHeader.msgHeader.msg_type);
1290 }
1291 else if (ulogMsgHeader.msgHeader.msg_type == 'M')
1292 {
1293 readInformationMessageMulti(ulogMsgHeader.msgHeader.msg_size, ulogMsgHeader.msgHeader.msg_type);
1294 }
1295 else if (ulogMsgHeader.msgHeader.msg_type == 'P')
1296 {
1297 readParameterMessage(ulogMsgHeader.msgHeader.msg_size, ulogMsgHeader.msgHeader.msg_type);
1298 }
1299 else if (ulogMsgHeader.msgHeader.msg_type == 'Q')
1300 {
1301 readParameterMessageDefault(ulogMsgHeader.msgHeader.msg_size, ulogMsgHeader.msgHeader.msg_type);
1302 }
1303 else
1304 {
1305 std::string nextChars;
1306 [[maybe_unused]] auto unidentifiedPos = static_cast<uint64_t>(tellg());
1307 nextChars.resize(100);
1308 read(nextChars.data(), 100);
1309 LOG_WARN("{}: Message type not identified. Position: {}, The next 100 chars: {}", nameId(), unidentifiedPos, nextChars);
1310
1311 // Reset read cursor
1312 seekg(-100, std::ios_base::cur);
1313 }
1314
1315 if (!good() || eof())
1316 {
1317 break;
1318 }
1319 }
1320 return nullptr;
1321}
1322
1323void NAV::UlogFile::readInformationMessage(uint16_t msgSize, char msgType)
1324{
1325 // Read msg size (2B) and type (1B)
1327 messageInfo.header.msg_size = msgSize;
1328 messageInfo.header.msg_type = msgType;
1329 read(reinterpret_cast<char*>(&messageInfo.key_len), sizeof(messageInfo.key_len));
1330
1331 // Read 'key' identifier ('keylength' byte) and its associated 'value'
1332 messageInfo.key.resize(messageInfo.key_len);
1333 read(messageInfo.key.data(), messageInfo.key_len);
1334 // if (!good() || eof())
1335 // {
1336 // return false;
1337 // }
1338
1339 messageInfo.value.resize(static_cast<size_t>(messageInfo.header.msg_size - 1 - messageInfo.key_len)); // 'msg_size' contains key and value, but not header
1340 read(messageInfo.value.data(), messageInfo.header.msg_size - 1 - messageInfo.key_len);
1341 LOG_DATA("{}: Information message - key: {}", nameId(), messageInfo.key);
1342 LOG_DATA("{}: Information message - value: {}", nameId(), messageInfo.value);
1343}
1344
1345void NAV::UlogFile::readInformationMessageMulti(uint16_t msgSize, char msgType)
1346{
1347 // Read msg size (2B) and type (1B)
1349 messageInfoMulti.header.msg_size = msgSize;
1350 messageInfoMulti.header.msg_type = msgType;
1351 read(reinterpret_cast<char*>(&messageInfoMulti.is_continued), sizeof(messageInfoMulti.is_continued));
1352 read(reinterpret_cast<char*>(&messageInfoMulti.key_len), sizeof(messageInfoMulti.key_len));
1353
1354 // Read 'key' identifier ('keylength' byte) and its associated 'value'
1355 messageInfoMulti.key.resize(messageInfoMulti.key_len);
1356 read(messageInfoMulti.key.data(), messageInfoMulti.key_len);
1357 messageInfoMulti.value.resize(static_cast<size_t>(messageInfoMulti.header.msg_size - 2 - messageInfoMulti.key_len)); // contains 'is_continued' flag in contrast to information message
1358 read(messageInfoMulti.value.data(), messageInfoMulti.header.msg_size - 2 - messageInfoMulti.key_len);
1359 LOG_DATA("{}: Information message multi - key_len: {}", nameId(), messageInfoMulti.key_len);
1360 LOG_DATA("{}: Information message multi - key: {}", nameId(), messageInfoMulti.key);
1361 LOG_DATA("{}: Information message multi - value: {}", nameId(), messageInfoMulti.value);
1362
1363 // TODO: Use 'is_continued' to generate a list of values with the same key
1364}
1365
1366void NAV::UlogFile::readParameterMessage(uint16_t msgSize, char msgType)
1367{
1368 // Read msg size (2B) and type (1B)
1370 messageParam.header.msg_size = msgSize;
1371 messageParam.header.msg_type = msgType;
1372 read(reinterpret_cast<char*>(&messageParam.key_len), sizeof(messageParam.key_len));
1373
1374 // Read 'key' identifier ('keylength' byte) and its associated 'value'
1375 messageParam.key.resize(messageParam.key_len);
1376 read(messageParam.key.data(), messageParam.key_len);
1377
1378 if (!(messageParam.key.find("int32_t")) && !(messageParam.key.find("float")))
1379 {
1380 LOG_WARN("{}: Parameter message contains invalid data type. It is neither 'int32_t', nor 'float', instead: {}", nameId(), messageParam.key);
1381 }
1382
1383 if (messageParam.header.msg_size - 1 - messageParam.key_len < 0)
1384 {
1385 LOG_WARN("{}: Parameter msg has key_len: {}", nameId(), messageParam.key_len);
1386 }
1387 else
1388 {
1389 messageParam.value.resize(static_cast<size_t>(messageParam.header.msg_size - 1 - messageParam.key_len)); // 'msg_size' contains key and value, but not header
1390 read(messageParam.value.data(), messageParam.header.msg_size - 1 - messageParam.key_len);
1391 LOG_DATA("{}: Parameter message - key: {}", nameId(), messageParam.key);
1392 LOG_DATA("{}: Parameter message - value: {}", nameId(), messageParam.value);
1393 }
1394}
1395
1396void NAV::UlogFile::readParameterMessageDefault(uint16_t msgSize, char msgType)
1397{
1399 messageParamDefault.header.msg_size = msgSize;
1400 messageParamDefault.header.msg_type = msgType;
1401 read(reinterpret_cast<char*>(&messageParamDefault.default_types), sizeof(messageParamDefault.default_types));
1402 read(reinterpret_cast<char*>(&messageParamDefault.key_len), sizeof(messageParamDefault.key_len));
1403
1404 messageParamDefault.key.resize(messageParamDefault.key_len);
1405 read(messageParamDefault.key.data(), messageParamDefault.key_len);
1406 messageParamDefault.value.resize(static_cast<size_t>(messageParamDefault.header.msg_size - 2 - messageParamDefault.key_len));
1407 read(messageParamDefault.value.data(), messageParamDefault.header.msg_size - 2 - messageParamDefault.key_len);
1408 LOG_DEBUG("{}: Parameter default message - key: {}", nameId(), messageParamDefault.key);
1409 LOG_DEBUG("{}: Parameter default message - value: {}", nameId(), messageParamDefault.value);
1410
1411 // TODO: Restriction on '1<<0' and '1<<1'
1412}
1413
1415{
1416 std::array<bool, 2> accelHasData{};
1417 std::array<bool, 2> gyroHasData{};
1418
1419 for ([[maybe_unused]] const auto& [timestamp, measurement] : _epochData)
1420 {
1421 if (std::holds_alternative<SensorAccel>(measurement.data))
1422 {
1423 accelHasData.at(measurement.multi_id) = true;
1424 }
1425 else if (std::holds_alternative<SensorGyro>(measurement.data))
1426 {
1427 gyroHasData.at(measurement.multi_id) = true;
1428 }
1429 }
1430
1431 if (lastGnssTime.timeSinceStartup)
1432 {
1433 for (size_t i = 0; i < 2; ++i)
1434 {
1435 if (accelHasData.at(i) && gyroHasData.at(i))
1436 {
1437 return static_cast<int8_t>(i);
1438 }
1439 }
1440 }
1441
1442 return -1;
1443}
1444
1445std::array<std::multimap<uint64_t, NAV::UlogFile::MeasurementData>::iterator, 2> NAV::UlogFile::findPosVelAttData()
1446{
1447 auto gpsIter = _epochData.end();
1448 auto attIter = _epochData.end();
1449
1450 for (auto iter = _epochData.begin(); iter != _epochData.end(); ++iter)
1451 {
1452 if (std::holds_alternative<VehicleGpsPosition>(iter->second.data))
1453 {
1454 gpsIter = iter;
1455 }
1456 else if (std::holds_alternative<VehicleAttitude>(iter->second.data))
1457 {
1458 attIter = iter;
1459 }
1460 }
1461
1462 return { gpsIter, attIter };
1463}
File Chooser.
Save/Load the Nodes.
nlohmann::json json
json namespace
Parent Class for all IMU Observations.
Utility class for logging to console and file.
#define LOG_DEBUG
Debug information. Should not be called on functions which receive observations (spamming)
Definition Logger.hpp:67
#define LOG_DATA
All output which occurs repeatedly every time observations are received.
Definition Logger.hpp:29
#define LOG_ERROR
Error occurred, which stops part of the program to work, but not everything.
Definition Logger.hpp:73
#define LOG_WARN
Error occurred, but a fallback option exists and program continues to work normally.
Definition Logger.hpp:71
#define LOG_TRACE
Detailled info to trace the execution of the program. Should not be called on functions which receive...
Definition Logger.hpp:65
Position, Velocity and Attitude Storage Class.
List of all ulog message types.
File Reader for ULog files.
bool initialize()
Initialize the file reader.
void restore(const json &j)
Restores the node from a json object.
bool good() const
Fast error checking.
std::string _path
Path to the file.
FileType
File Type Enumeration.
@ BINARY
Binary data.
@ NONE
Not specified.
FileType _fileType
File Type.
auto eof() const
Check whether the end of file is reached.
auto & read(char *__s, std::streamsize __n)
Extraction without delimiters.
std::filesystem::path getFilepath()
Returns the path of the file.
@ PATH_CHANGED
The path changed and exists.
std::streampos tellg()
Getting the current read position.
GuiResult guiConfig(const char *vFilters, const std::vector< std::string > &extensions, size_t id, const std::string &nameId)
ImGui config.
void resetReader()
Moves the read cursor to the start.
json save() const
Saves the node into a json object.
auto & seekg(std::streamoff pos, std::ios_base::seekdir dir)
Changing the current read position.
void deinitialize()
Deinitialize the file reader.
static std::string type()
Returns the type of the data class.
Definition ImuObs.hpp:33
json save() const override
Saves the node into a json object.
Definition Imu.cpp:93
void restore(const json &j) override
Restores the node from a json object.
Definition Imu.cpp:104
void guiConfig() override
ImGui config window which is shown on double click.
Definition Imu.cpp:55
Imu(const Imu &)=delete
Copy constructor.
ImuPos _imuPos
Position and rotation information for conversion from platform to body frame.
Definition Imu.hpp:57
The class is responsible for all time-related tasks.
Definition InsTime.hpp:710
bool doDeinitialize(bool wait=false)
Asks the node worker to deinitialize the node.
Definition Node.cpp:465
ImVec2 _guiConfigDefaultWindowSize
Definition Node.hpp:522
OutputPin * CreateOutputPin(const char *name, Pin::Type pinType, const std::vector< std::string > &dataIdentifier, OutputPin::PinData data=static_cast< void * >(nullptr), int idx=-1)
Create an Output Pin object.
Definition Node.cpp:278
std::string nameId() const
Node name and id.
Definition Node.cpp:323
std::string name
Name of the Node.
Definition Node.hpp:507
bool doReinitialize(bool wait=false)
Asks the node worker to reinitialize the node.
Definition Node.cpp:420
void invokeCallbacks(size_t portIndex, const std::shared_ptr< const NodeData > &data)
Calls all registered callbacks on the specified output port.
Definition Node.cpp:179
bool _hasConfig
Flag if the config window should be shown.
Definition Node.hpp:525
static std::string type()
Returns the type of the data class.
Definition PosVelAtt.hpp:29
void readInformationMessage(uint16_t msgSize, char msgType)
Read msg type 'I'.
bool initialize() override
Initialize the node.
Definition UlogFile.cpp:107
void readInformationMessageMulti(uint16_t msgSize, char msgType)
Read msg type 'M'.
static std::string typeStatic()
String representation of the Class Type.
Definition UlogFile.cpp:47
json save() const override
Saves the node into a json object.
Definition UlogFile.cpp:81
void deinitialize() override
Deinitialize the node.
Definition UlogFile.cpp:114
static constexpr size_t OUTPUT_PORT_INDEX_POSVELATT
Flow (PosVelAtt)
Definition UlogFile.hpp:79
std::unordered_map< std::string, std::vector< DataField > > _messageFormats
Key: message_name, e.g. "sensor_accel".
Definition UlogFile.hpp:126
static constexpr size_t OUTPUT_PORT_INDEX_IMUOBS_1
Flow (ImuObs #1)
Definition UlogFile.hpp:77
std::multimap< uint64_t, MeasurementData > _epochData
Data message container. Key: [timestamp], Value: [0, "sensor_accel", SensorAccel{}...
Definition UlogFile.hpp:309
void restore(const json &j) override
Restores the node from a json object.
Definition UlogFile.cpp:93
void guiConfig() override
ImGui config window which is shown on double click.
Definition UlogFile.cpp:62
void readParameterMessage(uint16_t msgSize, char msgType)
Read msg type 'P'.
struct NAV::UlogFile::@006001242362324316321330200126144024365050007132 lastGnssTime
Stores GNSS timestamp of one epoch before the current one (relative or absolute)
std::array< std::multimap< uint64_t, NAV::UlogFile::MeasurementData >::iterator, 2 > findPosVelAttData()
Checks '_epochData' whether there is enough data available to output one PosVelAtt.
std::unordered_map< uint16_t, SubscriptionData > _subscribedMessages
Key: msg_id.
Definition UlogFile.hpp:298
bool resetNode() override
Resets the node. Moves the read cursor to the start.
Definition UlogFile.cpp:121
~UlogFile() override
Destructor.
Definition UlogFile.cpp:42
FileType determineFileType() override
Determines the type of the file.
Definition UlogFile.cpp:136
std::shared_ptr< const NodeData > pollData()
Polls data from the file.
Definition UlogFile.cpp:312
int8_t enoughImuDataAvailable()
Checks '_epochData' whether there is enough data available to output one ImuObs.
std::string type() const override
String representation of the Class Type.
Definition UlogFile.cpp:52
static constexpr size_t OUTPUT_PORT_INDEX_IMUOBS_2
Flow (ImuObs #2)
Definition UlogFile.hpp:78
UlogFile()
Default constructor.
Definition UlogFile.cpp:28
static std::string category()
String representation of the Class Category.
Definition UlogFile.cpp:57
void readHeader() override
Read the Header of the file.
Definition UlogFile.cpp:160
void readParameterMessageDefault(uint16_t msgSize, char msgType)
Read msg type 'Q'.
void ApplyChanges()
Signals that there have been changes to the flow.
constexpr auto deg2rad(const T &deg)
Convert Degree to Radians.
Definition Units.hpp:21
@ Flow
NodeData Trigger.
Definition Pin.hpp:52
Key-value pair of the message format.
Definition UlogFile.hpp:120
Combined (sensor-)message name with unique ID and data.
Definition UlogFile.hpp:302
Px4 acceleration sensor message.
Definition UlogFile.hpp:130
static constexpr uint8_t padding
padding
Definition UlogFile.hpp:141
uint64_t timestamp_sample
[µs]
Definition UlogFile.hpp:132
std::array< uint8_t, 3 > clip_counter
clip counter
Definition UlogFile.hpp:139
float temperature
Px4 temperature of accel sensor in [°C].
Definition UlogFile.hpp:137
uint64_t timestamp
Px4 accelerometer time since startup in [µs].
Definition UlogFile.hpp:131
float z
Px4 acceleration along z in p-frame [m/s^2].
Definition UlogFile.hpp:136
uint32_t device_id
unique device identifier
Definition UlogFile.hpp:133
uint32_t error_count
error count
Definition UlogFile.hpp:138
float y
Px4 acceleration along y in p-frame [m/s^2].
Definition UlogFile.hpp:135
float x
Px4 acceleration along x in p-frame [m/s^2].
Definition UlogFile.hpp:134
Px4 gyro sensor message.
Definition UlogFile.hpp:146
float temperature
Px4 temperature of gyro sensor in [°C].
Definition UlogFile.hpp:153
float z
Px4 rotation rate about z in p-frame [//TODO].
Definition UlogFile.hpp:152
uint64_t timestamp_sample
[µs]
Definition UlogFile.hpp:148
uint32_t device_id
unique device identifier
Definition UlogFile.hpp:149
uint64_t timestamp
Px4 gyroscope time since startup in [µs].
Definition UlogFile.hpp:147
float x
Px4 rotation rate about x in p-frame [//TODO].
Definition UlogFile.hpp:150
uint32_t error_count
error count
Definition UlogFile.hpp:154
float y
Px4 rotation rate about y in p-frame [//TODO].
Definition UlogFile.hpp:151
Px4 magnetometer sensor message.
Definition UlogFile.hpp:159
float y
Px4 magnetic flux density about y in p-frame [//TODO].
Definition UlogFile.hpp:164
uint32_t error_count
error count
Definition UlogFile.hpp:167
uint32_t device_id
unique device identifier
Definition UlogFile.hpp:162
static constexpr uint8_t padding
padding
Definition UlogFile.hpp:170
uint64_t timestamp
Px4 magnetometer time since startup in [µs].
Definition UlogFile.hpp:160
float x
Px4 magnetic flux density about x in p-frame [//TODO].
Definition UlogFile.hpp:163
uint64_t timestamp_sample
[µs]
Definition UlogFile.hpp:161
float z
Px4 magnetic flux density about z in p-frame [//TODO].
Definition UlogFile.hpp:165
float temperature
Px4 temperature of gyro sensor in [°C].
Definition UlogFile.hpp:166
Combined (sensor-)message name with unique ID.
Definition UlogFile.hpp:71
Px4 air data sensor message.
Definition UlogFile.hpp:218
uint64_t timestamp
Px4 air data sensor time since startup in [µs].
Definition UlogFile.hpp:219
float baro_temp_celcius
Px4 barometric temperature in [°C].
Definition UlogFile.hpp:223
uint32_t baro_device_id
unique device identifier
Definition UlogFile.hpp:221
float baro_alt_meter
Px4 barometric altitude in [m].
Definition UlogFile.hpp:222
static constexpr uint8_t padding
padding
Definition UlogFile.hpp:227
float baro_pressure_pa
Px4 barometric pressure in [Pa].
Definition UlogFile.hpp:224
Px4 GPS attitude message.
Definition UlogFile.hpp:207
static constexpr uint8_t padding
padding
Definition UlogFile.hpp:213
std::array< float, 4 > q
Px4 GPS attitude quaternion.
Definition UlogFile.hpp:209
std::array< float, 4 > delta_q_reset
delta q reset
Definition UlogFile.hpp:210
uint8_t quat_reset_counter
Quaternion reset counter.
Definition UlogFile.hpp:211
uint64_t timestamp
Px4 GPS sensor time since startup in [µs].
Definition UlogFile.hpp:208
Px4 control data message.
Definition UlogFile.hpp:232
bool flag_external_manual_override_ok
Flag: external manual override ok.
Definition UlogFile.hpp:235
bool flag_control_fixed_hdg_enabled
Flag: fixed heading enabled.
Definition UlogFile.hpp:250
bool flag_control_climb_rate_enabled
Flag: climb rate enabled.
Definition UlogFile.hpp:248
bool flag_control_auto_enabled
Flag: auto mode enabled.
Definition UlogFile.hpp:237
bool flag_control_force_enabled
Flag: force enabled.
Definition UlogFile.hpp:243
bool flag_control_attitude_enabled
Flag: attitude mode enabled.
Definition UlogFile.hpp:240
bool flag_control_manual_enabled
Flag: manual mode enabled.
Definition UlogFile.hpp:236
static constexpr uint8_t padding
padding
Definition UlogFile.hpp:252
uint64_t timestamp
Px4 controller time since startup in [µs].
Definition UlogFile.hpp:233
bool flag_control_rattitude_enabled
Flag: rattitude enabled.
Definition UlogFile.hpp:242
bool flag_control_offboard_enabled
Flag: offboard enabled.
Definition UlogFile.hpp:238
bool flag_armed
Flag: Arm switch.
Definition UlogFile.hpp:234
bool flag_control_position_enabled
Flag: position enabled.
Definition UlogFile.hpp:246
bool flag_control_velocity_enabled
Flag: velocity enabled.
Definition UlogFile.hpp:245
bool flag_control_acceleration_enabled
Flag: acceleration enabled.
Definition UlogFile.hpp:244
bool flag_control_altitude_enabled
Flag: altitude enabled.
Definition UlogFile.hpp:247
bool flag_control_yawrate_override_enabled
Flag: yawrate override enabled.
Definition UlogFile.hpp:241
bool flag_control_rates_enabled
Flag: rates enabled.
Definition UlogFile.hpp:239
bool flag_control_termination_enabled
Flag: termination enabled.
Definition UlogFile.hpp:249
Px4 GPS sensor message.
Definition UlogFile.hpp:175
float heading_offset
heading offset
Definition UlogFile.hpp:197
int32_t alt_ellipsoid
Altitude above ellipsoid in [mm] (unit retains precision despite integer)
Definition UlogFile.hpp:181
float vdop
Vertical dilusion of precision.
Definition UlogFile.hpp:187
float c_variance_rad
Variance of angle [rad²].
Definition UlogFile.hpp:183
float vel_e_m_s
Velocity east component in [m/s].
Definition UlogFile.hpp:192
float s_variance_m_s
Variance of speed [m²/s²].
Definition UlogFile.hpp:182
int32_t timestamp_time_relative
Relative time stamp.
Definition UlogFile.hpp:195
float vel_m_s
Velocity in [m/s].
Definition UlogFile.hpp:190
int32_t jamming_indicator
Jamming indicator.
Definition UlogFile.hpp:189
float vel_d_m_s
Velocity down component in [m/s].
Definition UlogFile.hpp:193
uint64_t timestamp
Px4 GPS sensor time since startup in [µs].
Definition UlogFile.hpp:176
int32_t lat
Latitude in [deg * 1e7] (unit retains precision despite integer)
Definition UlogFile.hpp:178
int32_t noise_per_ms
Noise per millisecond.
Definition UlogFile.hpp:188
bool vel_ned_valid
Flag for validation of velocity in NED.
Definition UlogFile.hpp:199
uint64_t time_utc_usec
Px4 GPS UTC time in [µs].
Definition UlogFile.hpp:177
float eph
Horizontal position error in [m].
Definition UlogFile.hpp:184
float hdop
Horizontal dilusion of precision.
Definition UlogFile.hpp:186
float vel_n_m_s
Velocity north component in [m/s].
Definition UlogFile.hpp:191
float cog_rad
Center of gravity.
Definition UlogFile.hpp:194
int32_t lon
Longitude in [deg * 1e7] (unit retains precision despite integer)
Definition UlogFile.hpp:179
int32_t alt
Altitude above ground in [mm] (unit retains precision despite integer)
Definition UlogFile.hpp:180
float epv
Vertical position error in [m].
Definition UlogFile.hpp:185
static constexpr uint8_t padding
padding
Definition UlogFile.hpp:202
Subscribed log message with name and ID. This must come before the first corresponding message_data_s...
uint8_t multi_id
the same message format can have multiple instances, for example if the system has two sensors of the...
std::string msg_name
message name to subscribe to. Must match one of the message_format_s definitions
uint16_t msg_id
unique id to match message_data_s data. The first use must set this to 0, then increase it....
uint16_t msg_id
unique id to match message_data_s data.
std::string data
contains the logged binary message as defined by message_format_s
message_header_s header
msg header
dropout (lost logging messages) of a given duration in ms. Dropouts can occur e.g....
format definition for a single (composite) type that can be logged or used in another definition as a...
std::string format
plain-text string with the following format: message_name:field0;field1;
The Definitions and Data sections consist of a stream of messages. Each starts with this header.
uint16_t msg_size
size of the message in bytes without the header (hdr_size= 3 bytes)
char msg_type
defines the content and is one of the following
std::string key
key, e.g. 'char[value_len] sys_name'
std::string value
value, e.g. 'PX4'
message_header_s header
msg header
Logged string message, i.e. printf output.
uint8_t log_level
same as in the Linux kernel
uint8_t log_level
same as in the Linux kernel
uint16_t tag
id representing source of logged message string. It could represent a process, thread or a class depe...
unsubscribe a message, to mark that it will not be logged anymore
uint16_t msg_id
unique id to match message_data_s data.
synchronization message so that a reader can recover from a corrupt message by searching for the next...
message_header_s header
msg header
std::array< uint8_t, 8 > syncMsg
synchronization message
The header is a fixed-size section and has the following format (16 bytes)
Flag bitset message. This message must be the first message, right after the header section,...
Information message multi. The same as the information message, except that there can be multiple mes...
std::string key
key, e.g. 'char[value_len] sys_name'
parameter default message. If a parameter dynamically changes during runtime, this message can also b...
uint8_t default_types
default types (TODO: Validate default value)