QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
DataFlashParser.cc
Go to the documentation of this file.
1#include "DataFlashParser.h"
2#include "DataFlashUtility.h"
3#include "GeoTagData.h"
5
6#include <cmath>
7
8QGC_LOGGING_CATEGORY(DataFlashParserLog, "AnalyzeView.DataFlashParser")
9
11{
12
13namespace {
14
15GeoTagData extractGeoTagData(const QMap<QString, QVariant> &fields)
16{
17 GeoTagData feedback;
18
19 // Extract timestamp (microseconds since boot, convert to seconds)
20 if (fields.contains(QStringLiteral("TimeUS"))) {
21 feedback.timestamp = static_cast<qint64>(fields[QStringLiteral("TimeUS")].toULongLong() / 1000000);
22 }
23
24 // Extract image sequence number
25 if (fields.contains(QStringLiteral("Img"))) {
26 feedback.imageSequence = fields[QStringLiteral("Img")].toUInt();
27 }
28
29 // Extract GPS coordinates
30 double lat = 0, lon = 0, alt = 0;
31 if (fields.contains(QStringLiteral("Lat"))) {
32 lat = fields[QStringLiteral("Lat")].toDouble();
33 }
34 if (fields.contains(QStringLiteral("Lng"))) {
35 lon = fields[QStringLiteral("Lng")].toDouble();
36 }
37 if (fields.contains(QStringLiteral("Alt"))) {
38 alt = fields[QStringLiteral("Alt")].toDouble();
39 } else if (fields.contains(QStringLiteral("GPSAlt"))) {
40 alt = fields[QStringLiteral("GPSAlt")].toDouble();
41 }
42
43 feedback.coordinate = QGeoCoordinate(lat, lon, alt);
44
45 // Extract attitude (roll, pitch, yaw) and convert to quaternion
46 float roll = 0, pitch = 0, yaw = 0;
47 if (fields.contains(QStringLiteral("R"))) {
48 roll = static_cast<float>(fields[QStringLiteral("R")].toDouble());
49 }
50 if (fields.contains(QStringLiteral("P"))) {
51 pitch = static_cast<float>(fields[QStringLiteral("P")].toDouble());
52 }
53 if (fields.contains(QStringLiteral("Y"))) {
54 yaw = static_cast<float>(fields[QStringLiteral("Y")].toDouble());
55 }
56
57 // Convert Euler angles (degrees) to quaternion
58 const float cy = std::cos(yaw * 0.5f * static_cast<float>(M_PI) / 180.0f);
59 const float sy = std::sin(yaw * 0.5f * static_cast<float>(M_PI) / 180.0f);
60 const float cp = std::cos(pitch * 0.5f * static_cast<float>(M_PI) / 180.0f);
61 const float sp = std::sin(pitch * 0.5f * static_cast<float>(M_PI) / 180.0f);
62 const float cr = std::cos(roll * 0.5f * static_cast<float>(M_PI) / 180.0f);
63 const float sr = std::sin(roll * 0.5f * static_cast<float>(M_PI) / 180.0f);
64
65 feedback.attitude = QQuaternion(
66 cy * cp * cr + sy * sp * sr, // w
67 cy * cp * sr - sy * sp * cr, // x
68 sy * cp * sr + cy * sp * cr, // y
69 sy * cp * cr - cy * sp * sr // z
70 );
71
72 // ArduPilot CAM messages indicate successful captures
74
75 return feedback;
76}
77
78} // namespace
79
80bool getTagsFromLog(const char *data, qint64 size, QList<GeoTagData> &cameraFeedback, QString &errorMessage)
81{
82 cameraFeedback.clear();
83
84 if (!DataFlashUtility::isValidHeader(data, size)) {
85 errorMessage = QStringLiteral("Invalid DataFlash log format");
86 return false;
87 }
88
89 // First pass: Parse FMT messages to learn message formats
90 QMap<uint8_t, DataFlashUtility::MessageFormat> formats;
91 if (!DataFlashUtility::parseFmtMessages(data, size, formats)) {
92 errorMessage = QStringLiteral("No message formats found in log");
93 return false;
94 }
95
96 // Find CAM message type
97 uint8_t camMessageType = 0;
98 for (auto it = formats.constBegin(); it != formats.constEnd(); ++it) {
99 if (it.value().name == QStringLiteral("CAM")) {
100 camMessageType = it.key();
101 qCDebug(DataFlashParserLog) << "Found CAM format:" << it.value().format
102 << "columns:" << it.value().columns;
103 break;
104 }
105 }
106
107 if (camMessageType == 0) {
108 errorMessage = QStringLiteral("No CAM (camera) messages found in log");
109 return false;
110 }
111
112 // Second pass: Extract CAM messages using iterator
113 DataFlashUtility::iterateMessages(data, size, formats,
114 [&](uint8_t msgType, const char *payload, int, const DataFlashUtility::MessageFormat &fmt) {
115 if (msgType == camMessageType) {
116 const QMap<QString, QVariant> fields = DataFlashUtility::parseMessage(payload, fmt);
117 GeoTagData feedback = extractGeoTagData(fields);
118
119 if (feedback.coordinate.isValid()) {
120 cameraFeedback.append(feedback);
121 }
122 }
123 return true; // Continue iteration
124 });
125
126 if (cameraFeedback.isEmpty()) {
127 errorMessage = QStringLiteral("No valid camera capture events found in log");
128 return false;
129 }
130
131 qCDebug(DataFlashParserLog) << "Parsed" << cameraFeedback.size() << "camera capture events";
132
133 return true;
134}
135
136} // namespace DataFlashParser
#define QGC_LOGGING_CATEGORY(name, categoryStr)
bool getTagsFromLog(const char *data, qint64 size, QList< GeoTagData > &cameraFeedback, QString &errorMessage)
bool parseFmtMessages(const char *data, qint64 size, QMap< uint8_t, MessageFormat > &formats)
int iterateMessages(const char *data, qint64 size, const QMap< uint8_t, MessageFormat > &formats, const MessageCallback &callback)
QMap< QString, QVariant > parseMessage(const char *data, const MessageFormat &fmt)
bool isValidHeader(const char *data, qint64 size)
uint32_t imageSequence
Definition GeoTagData.h:16
QGeoCoordinate coordinate
Definition GeoTagData.h:17
QQuaternion attitude
Definition GeoTagData.h:19
qint64 timestamp
Seconds since epoch.
Definition GeoTagData.h:14
CaptureResult captureResult
Definition GeoTagData.h:20