QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
DataFlashUtility.cc
Go to the documentation of this file.
1#include "DataFlashUtility.h"
3
4#include <cstring>
5
6QGC_LOGGING_CATEGORY(DataFlashUtilityLog, "Utilities.DataFlashUtility")
7
9{
10
11// ============================================================================
12// Format Character Functions
13// ============================================================================
14
15int formatCharSize(char c)
16{
17 switch (c) {
18 case 'b': case 'B': case 'M':
19 return 1;
20 case 'h': case 'H': case 'c': case 'C': case 'g': // g = half-precision float
21 return 2;
22 case 'i': case 'I': case 'e': case 'E': case 'L': case 'f':
23 return 4;
24 case 'd': case 'q': case 'Q':
25 return 8;
26 case 'n':
27 return 4;
28 case 'N':
29 return 16;
30 case 'Z': case 'a': // Z = 64-char string, a = 64-byte array (32 int16)
31 return 64;
32 default:
33 return 0;
34 }
35}
36
37int calculatePayloadSize(const QString &format)
38{
39 int size = 0;
40 for (const QChar &ch : format) {
41 size += formatCharSize(ch.toLatin1());
42 }
43 return size;
44}
45
46// ============================================================================
47// Half-Precision Float Conversion
48// ============================================================================
49
50float halfToFloat(uint16_t bits)
51{
52 const uint32_t sign = (bits & 0x8000) << 16;
53 const uint32_t exponent = (bits >> 10) & 0x1F;
54 const uint32_t mantissa = bits & 0x3FF;
55 uint32_t result;
56
57 if (exponent == 0) {
58 result = sign; // Zero or denormalized (treat as zero)
59 } else if (exponent == 31) {
60 result = sign | 0x7F800000 | (mantissa << 13); // Inf or NaN
61 } else {
62 result = sign | ((exponent + 112) << 23) | (mantissa << 13);
63 }
64
65 float fval;
66 memcpy(&fval, &result, sizeof(fval));
67 return fval;
68}
69
70// ============================================================================
71// Value Parsing
72// ============================================================================
73
74QVariant parseValue(const char *data, char formatChar)
75{
76 switch (formatChar) {
77 case 'b':
78 return static_cast<int8_t>(*data);
79 case 'B':
80 case 'M':
81 return static_cast<uint8_t>(*data);
82 case 'h': {
83 int16_t val;
84 memcpy(&val, data, sizeof(val));
85 return val;
86 }
87 case 'H': {
88 uint16_t val;
89 memcpy(&val, data, sizeof(val));
90 return val;
91 }
92 case 'c': {
93 int16_t val;
94 memcpy(&val, data, sizeof(val));
95 return val / 100.0;
96 }
97 case 'C': {
98 uint16_t val;
99 memcpy(&val, data, sizeof(val));
100 return val / 100.0;
101 }
102 case 'i': {
103 int32_t val;
104 memcpy(&val, data, sizeof(val));
105 return val;
106 }
107 case 'I': {
108 uint32_t val;
109 memcpy(&val, data, sizeof(val));
110 return val;
111 }
112 case 'e': {
113 int32_t val;
114 memcpy(&val, data, sizeof(val));
115 return val / 100.0;
116 }
117 case 'E': {
118 uint32_t val;
119 memcpy(&val, data, sizeof(val));
120 return val / 100.0;
121 }
122 case 'L': {
123 int32_t val;
124 memcpy(&val, data, sizeof(val));
125 return val / 1.0e7; // Latitude/longitude in degrees
126 }
127 case 'f': {
128 float val;
129 memcpy(&val, data, sizeof(val));
130 return static_cast<double>(val);
131 }
132 case 'd': {
133 double val;
134 memcpy(&val, data, sizeof(val));
135 return val;
136 }
137 case 'q': {
138 int64_t val;
139 memcpy(&val, data, sizeof(val));
140 return static_cast<qlonglong>(val);
141 }
142 case 'Q': {
143 uint64_t val;
144 memcpy(&val, data, sizeof(val));
145 return static_cast<qulonglong>(val);
146 }
147 case 'g': {
148 uint16_t bits;
149 memcpy(&bits, data, sizeof(bits));
150 return static_cast<double>(halfToFloat(bits));
151 }
152 case 'n':
153 return QString::fromLatin1(data, qstrnlen(data, 4));
154 case 'N':
155 return QString::fromLatin1(data, qstrnlen(data, 16));
156 case 'Z':
157 return QString::fromLatin1(data, qstrnlen(data, 64));
158 case 'a':
159 // 64-byte array (32 int16 values) - return as raw bytes
160 return QByteArray(data, 64);
161 default:
162 return QVariant();
163 }
164}
165
166QMap<QString, QVariant> parseMessage(const char *data, const MessageFormat &fmt)
167{
168 QMap<QString, QVariant> result;
169 int offset = 0;
170
171 for (int i = 0; i < fmt.format.length() && i < fmt.columns.size(); ++i) {
172 const char formatChar = fmt.format.at(i).toLatin1();
173 const QString &columnName = fmt.columns.at(i);
174
175 const int size = formatCharSize(formatChar);
176 if (size == 0) {
177 continue;
178 }
179
180 result[columnName] = parseValue(data + offset, formatChar);
181 offset += size;
182 }
183
184 return result;
185}
186
187// ============================================================================
188// Header and Message Detection
189// ============================================================================
190
191bool isValidHeader(const char *data, qint64 size)
192{
193 if (size < 3) {
194 return false;
195 }
196 return static_cast<uint8_t>(data[0]) == kHeaderByte1 &&
197 static_cast<uint8_t>(data[1]) == kHeaderByte2;
198}
199
200qint64 findNextHeader(const char *data, qint64 size, qint64 offset)
201{
202 while (offset + 2 < size) {
203 if (static_cast<uint8_t>(data[offset]) == kHeaderByte1 &&
204 static_cast<uint8_t>(data[offset + 1]) == kHeaderByte2) {
205 return offset;
206 }
207 ++offset;
208 }
209 return -1;
210}
211
212// ============================================================================
213// FMT Message Parsing
214// ============================================================================
215
217{
218 MessageFormat fmt;
219 fmt.type = static_cast<uint8_t>(data[0]);
220 fmt.length = static_cast<uint8_t>(data[1]);
221 fmt.name = QString::fromLatin1(data + 2, qstrnlen(data + 2, 4));
222 fmt.format = QString::fromLatin1(data + 6, qstrnlen(data + 6, 16));
223 const QString columnsStr = QString::fromLatin1(data + 22, qstrnlen(data + 22, 64));
224 fmt.columns = columnsStr.trimmed().split(',');
225 return fmt;
226}
227
228bool parseFmtMessages(const char *data, qint64 size, QMap<uint8_t, MessageFormat> &formats)
229{
230 formats.clear();
231
232 if (!isValidHeader(data, size)) {
233 return false;
234 }
235
236 qint64 pos = 0;
237
238 while (pos + 3 <= size) {
239 // Find next message header
240 if (static_cast<uint8_t>(data[pos]) != kHeaderByte1 ||
241 static_cast<uint8_t>(data[pos + 1]) != kHeaderByte2) {
242 ++pos;
243 continue;
244 }
245
246 const uint8_t msgType = static_cast<uint8_t>(data[pos + 2]);
247 pos += 3;
248
249 if (msgType == kFmtMessageType) {
250 if (pos + kFmtPayloadSize > size) {
251 break;
252 }
253
254 const MessageFormat fmt = parseFmtPayload(data + pos);
255 formats[fmt.type] = fmt;
256 pos += kFmtPayloadSize;
257 } else {
258 // Skip message if we know its length
259 if (formats.contains(msgType)) {
260 pos += formats[msgType].length - 3; // -3 for header already consumed
261 } else {
262 // Unknown format, try to find next header
263 ++pos;
264 }
265 }
266 }
267
268 return !formats.isEmpty();
269}
270
271// ============================================================================
272// Message Iteration
273// ============================================================================
274
275int iterateMessages(const char *data, qint64 size,
276 const QMap<uint8_t, MessageFormat> &formats,
277 const MessageCallback &callback)
278{
279 int count = 0;
280 qint64 pos = 0;
281
282 while (pos + 3 <= size) {
283 // Find next message header
284 if (static_cast<uint8_t>(data[pos]) != kHeaderByte1 ||
285 static_cast<uint8_t>(data[pos + 1]) != kHeaderByte2) {
286 ++pos;
287 continue;
288 }
289
290 const uint8_t msgType = static_cast<uint8_t>(data[pos + 2]);
291 pos += 3;
292
293 if (!formats.contains(msgType)) {
294 continue;
295 }
296
297 const MessageFormat &fmt = formats[msgType];
298 const int payloadSize = fmt.length - 3;
299
300 if (pos + payloadSize > size) {
301 break;
302 }
303
304 ++count;
305 if (!callback(msgType, data + pos, payloadSize, fmt)) {
306 break;
307 }
308
309 pos += payloadSize;
310 }
311
312 return count;
313}
314
315} // namespace DataFlashUtility
#define QGC_LOGGING_CATEGORY(name, categoryStr)
float halfToFloat(uint16_t bits)
int formatCharSize(char c)
qint64 findNextHeader(const char *data, qint64 size, qint64 offset)
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)
constexpr uint8_t kHeaderByte1
QMap< QString, QVariant > parseMessage(const char *data, const MessageFormat &fmt)
std::function< bool(uint8_t msgType, const char *payload, int payloadSize, const MessageFormat &fmt)> MessageCallback
MessageFormat parseFmtPayload(const char *data)
int calculatePayloadSize(const QString &format)
constexpr uint8_t kFmtMessageType
bool isValidHeader(const char *data, qint64 size)
QVariant parseValue(const char *data, char formatChar)
constexpr int kFmtPayloadSize
constexpr uint8_t kHeaderByte2