Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Ardupilot
GitHub Repository: Ardupilot/ardupilot
Path: blob/master/libraries/AP_ExternalAHRS/AP_ExternalAHRS_SensAItion_Parser.cpp
13808 views
1
/*
2
This program is free software: you can redistribute it and/or modify
3
it under the terms of the GNU General Public License as published by
4
the Free Software Foundation, either version 3 of the License, or
5
(at your option) any later version.
6
7
This program is distributed in the hope that it will be useful,
8
but WITHOUT ANY WARRANTY; without even the implied warranty of
9
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
GNU General Public License for more details.
11
12
You should have received a copy of the GNU General Public License
13
along with this program. If not, see <http://www.gnu.org/licenses/>.
14
*/
15
16
#include <cassert>
17
#include <string.h> // Required for memchr, memmove
18
#include "AP_ExternalAHRS_SensAItion_Parser.h"
19
#include <AP_GPS/GPS_Backend.h>
20
#include <AP_HAL/AP_HAL.h>
21
#include <AP_HAL/utility/sparse-endian.h>
22
#include <GCS_MAVLink/GCS.h>
23
24
// Constructor
25
AP_ExternalAHRS_SensAItion_Parser::AP_ExternalAHRS_SensAItion_Parser(ConfigMode mode) :
26
config_mode(mode)
27
{
28
reset_parser();
29
}
30
31
size_t AP_ExternalAHRS_SensAItion_Parser::parse_stream(const uint8_t* data, size_t data_size, Measurement& meas)
32
{
33
for (size_t i = 0; i < data_size; i++) {
34
if (parse_single_byte(data[i])) {
35
decode_packet(meas);
36
reset_parser(); // Ready for more packets in same buffer
37
return i + 1; // No of bytes parsed
38
}
39
}
40
41
meas = Measurement(); // Uninitialized
42
return data_size;
43
}
44
45
void AP_ExternalAHRS_SensAItion_Parser::reset_parser()
46
{
47
parse_state = ParseState::WAITING_HEADER;
48
packet_buffer_len = 0;
49
target_payload_len = 0;
50
current_packet_id = PacketID::UNKNOWN;
51
}
52
53
// Error Handler
54
void AP_ExternalAHRS_SensAItion_Parser::handle_invalid_packet()
55
{
56
// Fallback if called with empty buffer, which should never happen
57
if (packet_buffer_len == 0) {
58
return;
59
}
60
61
// Look for a new header byte starting from index 1 to resync
62
uint8_t *p = (uint8_t *)memchr(&packet_buffer[1], HEADER_BYTE, packet_buffer_len - 1);
63
64
if (p == nullptr) {
65
// No header found, reset completely
66
reset_parser();
67
return;
68
}
69
70
const size_t bytes_to_discard = p - packet_buffer;
71
const size_t bytes_to_keep = packet_buffer_len - bytes_to_discard;
72
73
memmove(&packet_buffer[0], p, bytes_to_keep);
74
packet_buffer_len = bytes_to_keep;
75
76
// Determine State based on Mode and remaining data
77
if (config_mode == ConfigMode::INTERLEAVED_INS) {
78
if (packet_buffer_len >= 2) {
79
// We have Header + Potential ID. Process it immediately.
80
uint8_t id = packet_buffer[1];
81
switch (static_cast<PacketID>(id)) {
82
case PacketID::IMU:
83
target_payload_len = PAYLOAD_SIZE_IMU;
84
parse_state = ParseState::COLLECTING_PAYLOAD;
85
current_packet_id = PacketID::IMU;
86
break;
87
case PacketID::AHRS:
88
target_payload_len = PAYLOAD_SIZE_QUAT;
89
parse_state = ParseState::COLLECTING_PAYLOAD;
90
current_packet_id = PacketID::AHRS;
91
break;
92
case PacketID::INS:
93
target_payload_len = PAYLOAD_SIZE_INS;
94
parse_state = ParseState::COLLECTING_PAYLOAD;
95
current_packet_id = PacketID::INS;
96
break;
97
default:
98
// The "New" ID is also bad. Drop header and retry.
99
reset_parser();
100
return;
101
}
102
} else {
103
parse_state = ParseState::WAITING_ID;
104
}
105
} else {
106
// Legacy Mode
107
target_payload_len = PAYLOAD_SIZE_IMU;
108
parse_state = ParseState::COLLECTING_PAYLOAD;
109
}
110
}
111
112
// Checksum Validator & Deep Debugger
113
bool AP_ExternalAHRS_SensAItion_Parser::buffer_contains_valid_packet() const
114
{
115
if (packet_buffer_len < 2 || packet_buffer[0] != HEADER_BYTE) {
116
return false;
117
}
118
119
const uint8_t calculated = crc_xor_of_bytes(&packet_buffer[1], packet_buffer_len - 2);
120
const uint8_t received = packet_buffer[packet_buffer_len - 1];
121
122
if (calculated != received) {
123
return false;
124
}
125
126
return true;
127
}
128
129
// The Core State Machine
130
bool AP_ExternalAHRS_SensAItion_Parser::parse_single_byte(uint8_t byte)
131
{
132
// Safety: Prevent buffer overflow
133
if (packet_buffer_len >= MAX_PACKET_SIZE) {
134
handle_invalid_packet();
135
if (packet_buffer_len >= MAX_PACKET_SIZE) {
136
reset_parser();
137
}
138
}
139
140
switch (parse_state) {
141
case ParseState::WAITING_HEADER:
142
if (byte == HEADER_BYTE) {
143
packet_buffer_len = 0;
144
packet_buffer[packet_buffer_len++] = byte;
145
146
if (config_mode == ConfigMode::INTERLEAVED_INS) {
147
parse_state = ParseState::WAITING_ID;
148
} else {
149
target_payload_len = PAYLOAD_SIZE_IMU;
150
parse_state = ParseState::COLLECTING_PAYLOAD;
151
}
152
}
153
break;
154
155
case ParseState::WAITING_ID:
156
packet_buffer[packet_buffer_len++] = byte;
157
158
switch (static_cast<PacketID>(byte)) {
159
case PacketID::IMU:
160
target_payload_len = PAYLOAD_SIZE_IMU;
161
current_packet_id = PacketID::IMU;
162
parse_state = ParseState::COLLECTING_PAYLOAD;
163
break;
164
165
case PacketID::AHRS:
166
target_payload_len = PAYLOAD_SIZE_QUAT;
167
current_packet_id = PacketID::AHRS;
168
parse_state = ParseState::COLLECTING_PAYLOAD;
169
break;
170
171
case PacketID::INS:
172
target_payload_len = PAYLOAD_SIZE_INS;
173
current_packet_id = PacketID::INS;
174
parse_state = ParseState::COLLECTING_PAYLOAD;
175
break;
176
177
default:
178
parse_errors++;
179
handle_invalid_packet();
180
break;
181
}
182
break;
183
184
case ParseState::COLLECTING_PAYLOAD:
185
packet_buffer[packet_buffer_len++] = byte;
186
187
// Calculate Expected Total Length
188
// Legacy: Header(1) + Payload(N) + CRC(1)
189
// Interleaved: Header(1) + ID(1) + Payload(N) + CRC(1)
190
const size_t overhead = (config_mode == ConfigMode::INTERLEAVED_INS) ? 3 : 2;
191
const size_t expected_total_len = target_payload_len + overhead;
192
193
if (packet_buffer_len >= expected_total_len) {
194
if (buffer_contains_valid_packet()) {
195
valid_packets++;
196
return true;
197
}
198
199
parse_errors++;
200
handle_invalid_packet();
201
return false;
202
}
203
break;
204
}
205
206
return false;
207
}
208
209
bool AP_ExternalAHRS_SensAItion_Parser::validate_checksum() const
210
{
211
return buffer_contains_valid_packet();
212
}
213
214
// Router
215
void AP_ExternalAHRS_SensAItion_Parser::decode_packet(Measurement& measurement)
216
{
217
const uint8_t* payload = nullptr;
218
219
if (config_mode == ConfigMode::INTERLEAVED_INS) {
220
payload = &packet_buffer[2]; // Skip Header + ID
221
switch (current_packet_id) {
222
case PacketID::IMU:
223
decode_imu(payload, measurement);
224
break;
225
case PacketID::AHRS:
226
decode_ahrs(payload, measurement);
227
break;
228
case PacketID::INS:
229
decode_ins(payload, measurement);
230
break;
231
case PacketID::UNKNOWN:
232
assert(false && "decode_packet() should only be called with a valid packet ID!");
233
break;
234
}
235
} else {
236
payload = &packet_buffer[1]; // Skip Header
237
decode_imu(payload, measurement);
238
}
239
}
240
241
// IMU Decoder (Packet 0)
242
void AP_ExternalAHRS_SensAItion_Parser::decode_imu(const uint8_t* payload, Measurement& measurement)
243
{
244
// ... [Code omitted: No changes to decoders, use your existing implementation] ...
245
// Accel (Bytes 0-11): 3 x Int32 (ug)
246
const int32_t accel_x_ug = be32toh_ptr(&payload[0]);
247
const int32_t accel_y_ug = be32toh_ptr(&payload[4]);
248
const int32_t accel_z_ug = be32toh_ptr(&payload[8]);
249
250
// Gyro (Bytes 12-23): 3 x Int32 (udeg/s)
251
const int32_t gyro_x_udegs = be32toh_ptr(&payload[12]);
252
const int32_t gyro_y_udegs = be32toh_ptr(&payload[16]);
253
const int32_t gyro_z_udegs = be32toh_ptr(&payload[20]);
254
255
// Temp (Bytes 24-25): 1 x Int16 (Scaled)
256
const int16_t temp_raw = be16toh_ptr(&payload[24]);
257
258
// Mag (Bytes 26-31): 3 x Int16 (mGauss)
259
const int16_t mag_x_mgauss = be16toh_ptr(&payload[26]);
260
const int16_t mag_y_mgauss = be16toh_ptr(&payload[28]);
261
const int16_t mag_z_mgauss = be16toh_ptr(&payload[30]);
262
263
// Baro (Bytes 32-35): 1 x Int32 (0.1 Pa)
264
const int32_t baro_raw = be32toh_ptr(&payload[32]);
265
266
// Accel: ug -> m/s^2 (Note: 1,000,000 ug = 9.81 m/s^2 roughly)
267
const float ug_to_mss = 1.0e-6f * GRAVITY_MSS;
268
measurement.acceleration_mss = Vector3f(accel_x_ug, accel_y_ug, accel_z_ug) * ug_to_mss;
269
270
// Gyro: udeg/s -> rad/s (Note: 1,000,000 udeg/s = 1 deg/s = 0.017 rad/s)
271
const float udeg_to_rad = 1.0e-6f * DEG_TO_RAD;
272
measurement.angular_velocity_rads = Vector3f(gyro_x_udegs, gyro_y_udegs, gyro_z_udegs) * udeg_to_rad;
273
274
// Temp: (Raw * 0.008) + 20
275
measurement.temperature_degc = ((float)temp_raw * 0.008f) + 20.0f;
276
277
// Mag: mGauss (Pass-through, ArduPilot expects mGauss)
278
measurement.magnetic_field_mgauss = Vector3f(mag_x_mgauss, mag_y_mgauss, mag_z_mgauss);
279
280
// Baro: 0.1 Pa -> Pascal
281
measurement.air_pressure_p = (float)baro_raw * 0.1f;
282
283
// Metadata
284
measurement.type = MeasurementType::IMU;
285
measurement.timestamp_us = AP_HAL::micros64();
286
}
287
288
void AP_ExternalAHRS_SensAItion_Parser::decode_ahrs(const uint8_t* payload, Measurement& measurement)
289
{
290
// Payload layout: W (0-3), X (4-7), Y (8-11), Z (12-15)
291
const int32_t quat_w_raw = be32toh_ptr(&payload[0]);
292
const int32_t quat_x_raw = be32toh_ptr(&payload[4]);
293
const int32_t quat_y_raw = be32toh_ptr(&payload[8]);
294
const int32_t quat_z_raw = be32toh_ptr(&payload[12]);
295
296
const float scale_factor = 1.0e-6f;
297
298
measurement.orientation = Quaternion(
299
(float)quat_w_raw * scale_factor,
300
(float)quat_x_raw * scale_factor,
301
(float)quat_y_raw * scale_factor,
302
(float)quat_z_raw * scale_factor
303
);
304
305
measurement.type = MeasurementType::AHRS;
306
measurement.timestamp_us = AP_HAL::micros64();
307
}
308
309
void AP_ExternalAHRS_SensAItion_Parser::decode_ins(const uint8_t* payload, Measurement& measurement)
310
{
311
// --- 1. PARSE RAW BYTES (Big-Endian) ---
312
313
// 0-3: Num Sats (G2, G1)
314
measurement.num_sats_gnss2 = payload[1];
315
measurement.num_sats_gnss1 = payload[3];
316
317
// 4-7: Error Flags
318
measurement.error_flags = be32toh_ptr(&payload[4]);
319
320
// 8: Sensor Valid
321
measurement.sensor_valid = payload[8];
322
323
// 9-16: Lat/Lon (1e-7 deg)
324
const int32_t lat_raw = be32toh_ptr(&payload[9]);
325
const int32_t lon_raw = be32toh_ptr(&payload[13]);
326
327
// 17-28: Velocity N, E, D (mm/s)
328
const int32_t vel_n_mm = be32toh_ptr(&payload[17]);
329
const int32_t vel_e_mm = be32toh_ptr(&payload[21]);
330
const int32_t vel_d_mm = be32toh_ptr(&payload[25]);
331
332
// 29-32: Altitude relative to WGS 84 ellipsoid (mm)
333
const int32_t alt_raw_mm = be32toh_ptr(&payload[29]);
334
335
// 33: Alignment Status
336
measurement.alignment_status = payload[33];
337
338
// 34-37: Time of week (ms)
339
measurement.time_itow_ms = be32toh_ptr(&payload[34]);
340
341
// 38-39: GNSS Fix
342
measurement.gnss2_fix = payload[38];
343
measurement.gnss1_fix = payload[39];
344
345
// 40-44: UTC Date/Time
346
const uint16_t year = (uint16_t)((payload[40]<<8) | payload[41]);
347
const uint16_t month = (uint16_t)((payload[42]<<8) | payload[43]);
348
const uint8_t day = payload[44];
349
350
// 45-68: Accuracy Metrics (mm or mm/s)
351
const int32_t acc_lat_mm = be32toh_ptr(&payload[45]);
352
const int32_t acc_lon_mm = be32toh_ptr(&payload[49]);
353
const int32_t acc_vn_mm = be32toh_ptr(&payload[53]);
354
const int32_t acc_ve_mm = be32toh_ptr(&payload[57]);
355
const int32_t acc_vd_mm = be32toh_ptr(&payload[61]);
356
const int32_t acc_vd_pos_mm = be32toh_ptr(&payload[65]);
357
358
// --- 2. POPULATE & CONVERT ---
359
const float mm_to_cm = 0.1f;
360
measurement.location = Location(lat_raw, lon_raw, alt_raw_mm * mm_to_cm, Location::AltFrame::ABSOLUTE);
361
362
const float mms_to_ms = 0.001f;
363
const float mm_to_m = 0.001f;
364
365
measurement.velocity_ned.x = (float)vel_n_mm * mms_to_ms;
366
measurement.velocity_ned.y = (float)vel_e_mm * mms_to_ms;
367
measurement.velocity_ned.z = (float)vel_d_mm * mms_to_ms;
368
369
// Accuracy
370
measurement.pos_accuracy.x = (float)acc_lat_mm * mm_to_m; // Horizontal Latitude
371
measurement.pos_accuracy.y = (float)acc_lon_mm * mm_to_m; // Horizontal Longitude
372
measurement.pos_accuracy.z = (float)acc_vd_pos_mm * mm_to_m; // Vertical
373
374
measurement.vel_accuracy.x = (float)acc_vn_mm * mms_to_ms;
375
measurement.vel_accuracy.y = (float)acc_ve_mm * mms_to_ms;
376
measurement.vel_accuracy.z = (float)acc_vd_mm * mms_to_ms;
377
378
// GPS Week Calculation (Gate check)
379
if (measurement.gnss1_fix >= 2) {
380
measurement.gps_week = calculate_gps_week(year, month, day);
381
} else {
382
measurement.gps_week = 0;
383
}
384
385
measurement.type = MeasurementType::INS;
386
measurement.timestamp_us = AP_HAL::micros64();
387
388
}
389
390
uint16_t AP_ExternalAHRS_SensAItion_Parser::calculate_gps_week(uint16_t year, uint8_t month, uint8_t day)
391
{
392
// Convert to BCD form (DDMMYY) - the GPS_Backend function assumes year is 20YY
393
const uint32_t bcd_date = year % 100U + month * 100U + day * 10000U;
394
395
const uint32_t bcd_time_ms = 0U;
396
uint16_t gps_week;
397
uint32_t gps_time_ms;
398
AP_GPS_Backend::BCD_to_gps_time(bcd_date, bcd_time_ms, gps_week, gps_time_ms);
399
400
return gps_week;
401
}
402
403