Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Ardupilot
GitHub Repository: Ardupilot/ardupilot
Path: blob/master/libraries/AP_ExternalAHRS/AP_ExternalAHRS_SBG.cpp
6351 views
1
/*
2
Copyright (C) 2025 Kraus Hamdani Aerospace Inc. All rights reserved.
3
4
This program is free software: you can redistribute it and/or modify
5
it under the terms of the GNU General Public License as published by
6
the Free Software Foundation, either version 3 of the License, or
7
(at your option) any later version.
8
9
This program is distributed in the hope that it will be useful,
10
but WITHOUT ANY WARRANTY; without even the implied warranty of
11
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
GNU General Public License for more details.
13
14
You should have received a copy of the GNU General Public License
15
along with this program. If not, see <http://www.gnu.org/licenses/>.
16
*/
17
/*
18
support for serial connected SBG INS
19
*/
20
21
#define AP_MATH_ALLOW_DOUBLE_FUNCTIONS 1
22
23
#include "AP_ExternalAHRS_config.h"
24
25
#if AP_EXTERNAL_AHRS_SBG_ENABLED
26
27
#include "AP_ExternalAHRS_SBG.h"
28
#include <AP_Math/AP_Math.h>
29
#include <AP_Math/crc.h>
30
#include <AP_Baro/AP_Baro.h>
31
#include <AP_Compass/AP_Compass.h>
32
#include <AP_InertialSensor/AP_InertialSensor.h>
33
#include <GCS_MAVLink/GCS.h>
34
#include <AP_SerialManager/AP_SerialManager.h>
35
#include <AP_RTC/AP_RTC.h>
36
#include <AP_GPS/AP_GPS.h>
37
#include <AP_Airspeed/AP_Airspeed.h>
38
#include <AP_Vehicle/AP_Vehicle_Type.h>
39
40
extern const AP_HAL::HAL &hal;
41
42
// constructor
43
AP_ExternalAHRS_SBG::AP_ExternalAHRS_SBG(AP_ExternalAHRS *_frontend,
44
AP_ExternalAHRS::state_t &_state) :
45
AP_ExternalAHRS_backend(_frontend, _state)
46
{
47
auto &sm = AP::serialmanager();
48
uart = sm.find_serial(AP_SerialManager::SerialProtocol_AHRS, 0);
49
if (uart == nullptr) {
50
GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "SBG ExternalAHRS no UART");
51
return;
52
}
53
baudrate = sm.find_baudrate(AP_SerialManager::SerialProtocol_AHRS, 0);
54
port_num = sm.find_portnum(AP_SerialManager::SerialProtocol_AHRS, 0);
55
56
57
// don't offer IMU by default, at 200Hz it is too slow for many aircraft
58
set_default_sensors(uint16_t(AP_ExternalAHRS::AvailableSensor::GPS) |
59
uint16_t(AP_ExternalAHRS::AvailableSensor::BARO) |
60
uint16_t(AP_ExternalAHRS::AvailableSensor::COMPASS));
61
62
if (!hal.scheduler->thread_create(FUNCTOR_BIND_MEMBER(&AP_ExternalAHRS_SBG::update_thread, void), "SBG", 4096, AP_HAL::Scheduler::PRIORITY_SPI, 0)) {
63
AP_HAL::panic("SBG Failed to start ExternalAHRS update thread");
64
}
65
GCS_SEND_TEXT(MAV_SEVERITY_INFO, "SBG ExternalAHRS initialised");
66
}
67
68
void AP_ExternalAHRS_SBG::update_thread()
69
{
70
hal.scheduler->delay(1000);
71
while (!hal.scheduler->is_system_initialized()) {
72
hal.scheduler->delay(100);
73
}
74
hal.scheduler->delay(1000);
75
76
if (uart == nullptr) {
77
return;
78
}
79
80
uart->begin(baudrate, 1024, 1024);
81
82
setup_complete = true;
83
84
while (true) {
85
86
if (check_uart()) {
87
// we've parsed something. There might be more so lets come back quickly
88
hal.scheduler->delay_microseconds(100);
89
continue;
90
}
91
92
// uart is idle, lets snooze a little more and then do some housekeeping
93
hal.scheduler->delay_microseconds(250);
94
95
const uint32_t now_ms = AP_HAL::millis();
96
if (cached.sbg.sbgEComDeviceInfo.firmwareRev == 0 && now_ms - version_check_ms >= 5000) {
97
// request Device Info every few seconds until we get a response
98
version_check_ms = now_ms;
99
100
// static uint32_t count = 1;
101
// GCS_SEND_TEXT(MAV_SEVERITY_INFO, "SBG Requesting DeviceInfo %u", count++);
102
103
const sbgMessage msg = sbgMessage(SBG_ECOM_CLASS_LOG_CMD_0, SBG_ECOM_CMD_INFO);
104
UNUSED_RESULT(send_sbgMessage(uart, msg)); // don't care about any error, just retry at 1Hz
105
106
#if AP_COMPASS_ENABLED
107
} else if (now_ms - send_MagData_ms >= 100) {
108
send_MagData_ms = now_ms;
109
110
if (!send_MagData(uart)) {
111
// TODO: if it fails maybe we should figure out why and retry?
112
// possible causes:
113
// 1) uart == nullptr
114
// 2) msg.len > sizeof(data)
115
// 3) did not write all the bytes out the uart: zero/fail is treated same as packet_len != bytes_sent
116
if (now_ms - send_mag_error_last_ms >= 5000) {
117
// throttle the error to no faster than 5Hz because if you get one error you'll likely get a lot
118
send_mag_error_last_ms = now_ms;
119
GCS_SEND_TEXT(MAV_SEVERITY_INFO, "SBG: Error sending Mag data");
120
}
121
}
122
#endif // AP_COMPASS_ENABLED
123
124
#if AP_BARO_ENABLED || AP_AIRSPEED_ENABLED
125
} else if (now_ms - send_AirData_ms >= 100) {
126
send_AirData_ms = now_ms;
127
128
if (!send_AirData(uart)) {
129
// TODO: if it fails maybe we should figure out why and retry?
130
// possible causes:
131
// 1) uart == nullptr
132
// 2) msg.len > sizeof(data)
133
// 3) did not write all the bytes out the uart: zero/fail is treated same as packet_len != bytes_sent
134
if (now_ms - send_air_error_last_ms >= 5000) {
135
// throttle the error to no faster than 5Hz because if you get one error you'll likely get a lot
136
send_air_error_last_ms = now_ms;
137
GCS_SEND_TEXT(MAV_SEVERITY_INFO, "SBG: Error sending Air data");
138
}
139
}
140
#endif // AP_BARO_ENABLED || AP_AIRSPEED_ENABLED
141
}
142
} // while
143
}
144
145
// Builds packets by looking at each individual byte, once a full packet has been read in it checks the checksum then handles the packet.
146
// returns true if any data was found in the UART buffer which was then processed
147
bool AP_ExternalAHRS_SBG::check_uart()
148
{
149
uint32_t nbytes = MIN(uart->available(), 512u);
150
if (nbytes == 0) {
151
return false;
152
}
153
while (nbytes--> 0) {
154
uint8_t b;
155
if (!uart->read(b)) {
156
break;
157
}
158
159
if (parse_byte(b, _inbound_state.msg, _inbound_state)) {
160
handle_msg(_inbound_state.msg);
161
}
162
}
163
return true;
164
}
165
166
167
bool AP_ExternalAHRS_SBG::send_sbgMessage(AP_HAL::UARTDriver *_uart, const sbgMessage &msg)
168
{
169
if (_uart == nullptr || msg.len > ARRAY_SIZE(msg.data)) {
170
// invalid _uart or msg.len is out of range
171
return false;
172
}
173
174
const uint16_t buffer_len = (SBG_PACKET_OVERHEAD + msg.len);
175
uint8_t buffer[buffer_len];
176
177
buffer[0] = SBG_PACKET_SYNC1;
178
buffer[1] = SBG_PACKET_SYNC2;
179
buffer[2] = msg.msgid;
180
buffer[3] = msg.msgclass;
181
buffer[4] = msg.len & 0xFF; // LSB first
182
buffer[5] = msg.len >> 8;
183
184
for (uint16_t i=0; i<msg.len; i++) {
185
buffer[6+i] = msg.data[i];
186
}
187
188
const uint16_t crc = crc16_ccitt_r((const uint8_t*)&msg, msg.len+4, 0, 0);
189
190
buffer[buffer_len-3] = crc & 0xFF; // LSB First
191
buffer[buffer_len-2] = crc >> 8;
192
buffer[buffer_len-1] = SBG_PACKET_ETX;
193
194
const uint32_t bytes_sent = _uart->write(buffer, buffer_len);
195
_uart->flush();
196
return (bytes_sent == buffer_len);
197
}
198
199
bool AP_ExternalAHRS_SBG::parse_byte(const uint8_t data, sbgMessage &msg, SBG_PACKET_INBOUND_STATE &inbound_state)
200
{
201
switch (inbound_state.parser) {
202
case SBG_PACKET_PARSE_STATE::SYNC1:
203
inbound_state.parser = (data == SBG_PACKET_SYNC1) ? SBG_PACKET_PARSE_STATE::SYNC2 : SBG_PACKET_PARSE_STATE::SYNC1;
204
break;
205
206
case SBG_PACKET_PARSE_STATE::SYNC2:
207
inbound_state.parser = (data == SBG_PACKET_SYNC2) ? SBG_PACKET_PARSE_STATE::MSG : SBG_PACKET_PARSE_STATE::SYNC1;
208
break;
209
210
case SBG_PACKET_PARSE_STATE::MSG:
211
msg.msgid = data;
212
inbound_state.parser = SBG_PACKET_PARSE_STATE::CLASS;
213
break;
214
215
case SBG_PACKET_PARSE_STATE::CLASS:
216
msg.msgclass = data;
217
inbound_state.parser = SBG_PACKET_PARSE_STATE::LEN1;
218
break;
219
220
case SBG_PACKET_PARSE_STATE::LEN1:
221
msg.len = data;
222
inbound_state.parser = SBG_PACKET_PARSE_STATE::LEN2;
223
break;
224
225
case SBG_PACKET_PARSE_STATE::LEN2:
226
msg.len |= uint16_t(data) << 8;
227
if (msg.len > sizeof(msg.data)) {
228
// we can't handle this packet, it's larger than the rx buffer which is larger than the largest packet we care about
229
inbound_state.data_count_skip = msg.len;
230
inbound_state.parser = SBG_PACKET_PARSE_STATE::DROP_THIS_PACKET;
231
} else {
232
inbound_state.data_count = 0;
233
inbound_state.parser = (msg.len > 0) ? SBG_PACKET_PARSE_STATE::DATA : SBG_PACKET_PARSE_STATE::CRC1;
234
}
235
break;
236
237
case SBG_PACKET_PARSE_STATE::DATA:
238
msg.data[inbound_state.data_count++] = data;
239
if (inbound_state.data_count >= sizeof(msg.data)) {
240
inbound_state.parser = SBG_PACKET_PARSE_STATE::SYNC1;
241
} else if (inbound_state.data_count >= msg.len) {
242
inbound_state.parser = SBG_PACKET_PARSE_STATE::CRC1;
243
}
244
break;
245
246
case SBG_PACKET_PARSE_STATE::CRC1:
247
inbound_state.crc = data;
248
inbound_state.parser = SBG_PACKET_PARSE_STATE::CRC2;
249
break;
250
251
case SBG_PACKET_PARSE_STATE::CRC2:
252
inbound_state.crc |= uint16_t(data) << 8;
253
inbound_state.parser = SBG_PACKET_PARSE_STATE::SYNC1; // skip ETX and go directly to SYNC1. Do not pass Go.
254
{
255
// CRC field is computed on [MSG(1), CLASS(1), LEN(2), DATA(msg.len)] fields
256
const uint16_t crc = crc16_ccitt_r((const uint8_t*)&msg, msg.len+4, 0, 0);
257
if (crc == inbound_state.crc) {
258
return true;
259
}
260
// GCS_SEND_TEXT(MAV_SEVERITY_INFO, "SBG: Failed CRC. Received 0x%04X, Expected 0x%04X", (unsigned)inbound_state.crc, (unsigned)crc);
261
}
262
break;
263
264
case SBG_PACKET_PARSE_STATE::ETX:
265
// we can just skip this state and let SYNC1 fail once when it gets the ETX byte every time... same amount of work
266
inbound_state.parser = SBG_PACKET_PARSE_STATE::SYNC1;
267
break;
268
269
default:
270
case SBG_PACKET_PARSE_STATE::DROP_THIS_PACKET:
271
// we're currently parsing a packet that is very large and is not one we care
272
// about. This is not the packet you're looking for... drop all those bytes
273
if (inbound_state.data_count_skip > 0) {
274
inbound_state.data_count_skip--;
275
}
276
if (inbound_state.data_count_skip == 0) {
277
inbound_state.parser = SBG_PACKET_PARSE_STATE::SYNC1;
278
}
279
break;
280
}
281
282
return false;
283
}
284
285
void AP_ExternalAHRS_SBG::handle_msg(const sbgMessage &msg)
286
{
287
const uint32_t now_ms = AP_HAL::millis();
288
const bool valid_class1_msg = ((SbgEComClass)msg.msgclass == SBG_ECOM_CLASS_LOG_ECOM_1 && (_SbgEComLog1MsgId)msg.msgid == SBG_ECOM_LOG_FAST_IMU_DATA);
289
290
if ((SbgEComClass)msg.msgclass == SBG_ECOM_CLASS_LOG_CMD_0) {
291
switch ((SbgEComCmd)msg.msgid) {
292
case SBG_ECOM_CMD_ACK: // 0
293
break;
294
295
case SBG_ECOM_CMD_INFO: // 4
296
safe_copy_msg_to_object((uint8_t*)&cached.sbg.sbgEComDeviceInfo, sizeof(cached.sbg.sbgEComDeviceInfo), msg.data, msg.len);
297
298
GCS_SEND_TEXT(MAV_SEVERITY_INFO, "SBG: %s", cached.sbg.sbgEComDeviceInfo.productCode);
299
// GCS_SEND_TEXT(MAV_SEVERITY_INFO, "SBG: %u, %u, %u, %u, %u, %u, %u: %u.%u.%u",
300
// (unsigned)cached.sbg.sbgEComDeviceInfo.serialNumber,
301
// (unsigned)cached.sbg.sbgEComDeviceInfo.calibationRev,
302
// (unsigned)cached.sbg.sbgEComDeviceInfo.calibrationYear,
303
// (unsigned)cached.sbg.sbgEComDeviceInfo.calibrationMonth,
304
// (unsigned)cached.sbg.sbgEComDeviceInfo.calibrationDay,
305
// (unsigned)cached.sbg.sbgEComDeviceInfo.hardwareRev,
306
// (unsigned)cached.sbg.sbgEComDeviceInfo.firmwareRev,
307
// (cached.sbg.sbgEComDeviceInfo.firmwareRev >> 22) & 0x3F,
308
// (cached.sbg.sbgEComDeviceInfo.firmwareRev >> 16) & 0x3F,
309
// cached.sbg.sbgEComDeviceInfo.firmwareRev & 0xFFFF
310
311
// GCS_SEND_TEXT(MAV_SEVERITY_INFO, "SBG: Serial Number: %u",(unsigned)cached.sbg.sbgEComDeviceInfo.serialNumber);
312
GCS_SEND_TEXT(MAV_SEVERITY_INFO, "SBG: Version HW v%u.%u FW v%u.%u.%u",
313
(unsigned)(cached.sbg.sbgEComDeviceInfo.hardwareRev >> 24) & 0x00FF,
314
(unsigned)(cached.sbg.sbgEComDeviceInfo.hardwareRev >> 16) & 0x00FF,
315
316
(unsigned) (cached.sbg.sbgEComDeviceInfo.firmwareRev >> 22) & 0x003F,
317
(unsigned)(cached.sbg.sbgEComDeviceInfo.firmwareRev >> 16) & 0x003F,
318
(unsigned)cached.sbg.sbgEComDeviceInfo.firmwareRev & 0xFFFF);
319
break;
320
321
case SBG_ECOM_CMD_COMPUTE_MAG_CALIB: // 15
322
break;
323
324
default:
325
// GCS_SEND_TEXT(MAV_SEVERITY_INFO, "SBG: Unknown ID=%u, CLASS=%u, LEN=%u", (unsigned)msg.msgid, (unsigned)msg.msgclass, (unsigned)msg.len);
326
break;
327
}
328
return;
329
}
330
331
332
333
if (((SbgEComClass)msg.msgclass != SBG_ECOM_CLASS_LOG_ECOM_0) && !valid_class1_msg) {
334
// GCS_SEND_TEXT(MAV_SEVERITY_INFO, "SBG: Unknown ID=%u, CLASS=%u, LEN=%u", (unsigned)msg.msgid, (unsigned)msg.msgclass, (unsigned)msg.len);
335
return;
336
}
337
338
bool updated_gps = false;
339
bool updated_baro = false;
340
bool updated_ins = false;
341
bool updated_mag = false;
342
bool updated_airspeed = false;
343
344
{
345
WITH_SEMAPHORE(state.sem);
346
347
switch (msg.msgid) { // (_SbgEComLog)
348
case SBG_ECOM_LOG_FAST_IMU_DATA: // 0
349
safe_copy_msg_to_object((uint8_t*)&cached.sbg.sbgLogFastImuData, sizeof(cached.sbg.sbgLogFastImuData), msg.data, msg.len);
350
351
state.accel = Vector3f(cached.sbg.sbgLogFastImuData.accelerometers[0], cached.sbg.sbgLogFastImuData.accelerometers[1], cached.sbg.sbgLogFastImuData.accelerometers[2]);
352
state.gyro = Vector3f(cached.sbg.sbgLogFastImuData.gyroscopes[0], cached.sbg.sbgLogFastImuData.gyroscopes[1], cached.sbg.sbgLogFastImuData.gyroscopes[2]);
353
updated_ins = true;
354
break;
355
356
case SBG_ECOM_LOG_UTC_TIME: // 2
357
safe_copy_msg_to_object((uint8_t*)&cached.sbg.sbgLogUtcData, sizeof(cached.sbg.sbgLogUtcData), msg.data, msg.len);
358
359
if (sbgEComLogUtcGetClockStatus(cached.sbg.sbgLogUtcData.status) != SBG_ECOM_CLOCK_VALID ||
360
sbgEComLogUtcGetClockUtcStatus(cached.sbg.sbgLogUtcData.status) != SBG_ECOM_UTC_VALID)
361
{
362
// Data is not valid, ignore it.
363
// This will happen even with a GPS fix = 3. A 3D lock is not enough, a valid
364
// clock has a higher threshold of quiality needed. It also needs time to sync
365
// its internal timing and PPS outputs before valid.
366
break;
367
}
368
369
// GCS_SEND_TEXT(MAV_SEVERITY_INFO, "------------------");
370
// GCS_SEND_TEXT(MAV_SEVERITY_INFO, "SBG: %u-%u-%u %02u:%02u:%02u",
371
// cached.sbg.sbgLogUtcData.year,
372
// (unsigned)cached.sbg.sbgLogUtcData.month,
373
// (unsigned)cached.sbg.sbgLogUtcData.day,
374
// (unsigned)cached.sbg.sbgLogUtcData.hour,
375
// (unsigned)cached.sbg.sbgLogUtcData.minute,
376
// (unsigned)cached.sbg.sbgLogUtcData.second);
377
378
#if AP_RTC_ENABLED
379
{
380
const uint32_t utc_epoch_sec = AP::rtc().date_fields_to_clock_s(
381
cached.sbg.sbgLogUtcData.year,
382
cached.sbg.sbgLogUtcData.month,
383
cached.sbg.sbgLogUtcData.day,
384
cached.sbg.sbgLogUtcData.hour,
385
cached.sbg.sbgLogUtcData.minute,
386
cached.sbg.sbgLogUtcData.second);
387
388
const uint64_t utc_epoch_usec = ((uint64_t)utc_epoch_sec) * 1E6 + (cached.sbg.sbgLogUtcData.nanoSecond * 1E-3);
389
AP::rtc().set_utc_usec(utc_epoch_usec, AP_RTC::SOURCE_GPS);
390
391
}
392
#endif // AP_RTC_ENABLED
393
394
cached.sensors.gps_data.ms_tow = cached.sbg.sbgLogUtcData.gpsTimeOfWeek;
395
cached.sensors.gps_data.gps_week = cached.sbg.sbgLogUtcData.gpsTimeOfWeek / AP_MSEC_PER_WEEK;
396
updated_gps = true;
397
break;
398
399
case SBG_ECOM_LOG_IMU_SHORT: // 44
400
safe_copy_msg_to_object((uint8_t*)&cached.sbg.sbgEComLogImuShort, sizeof(cached.sbg.sbgEComLogImuShort), msg.data, msg.len);
401
402
{
403
Vector3f temp;
404
/*!< X, Y, Z delta velocity. Unit is 1048576 LSB for 1 m.s^-2. */
405
temp = Vector3f(cached.sbg.sbgEComLogImuShort.deltaVelocity[0], cached.sbg.sbgEComLogImuShort.deltaVelocity[1], cached.sbg.sbgEComLogImuShort.deltaVelocity[2]);
406
state.accel = temp / SBG_ECOM_LOG_IMU_ACCEL_SCALE_STD;
407
408
/*!< X, Y, Z delta angle. Unit is either 67108864 LSB for 1 rad.s^-1 (standard) or 12304174 LSB for 1 rad.s^-1 (high range), managed automatically. */
409
temp = Vector3f(cached.sbg.sbgEComLogImuShort.deltaAngle[0], cached.sbg.sbgEComLogImuShort.deltaAngle[1], cached.sbg.sbgEComLogImuShort.deltaAngle[2]);
410
const float scaleFactor = (cached.sbg.sbgEComLogImuShort.status & SBG_ECOM_IMU_GYROS_USE_HIGH_SCALE) ? SBG_ECOM_LOG_IMU_GYRO_SCALE_HIGH : SBG_ECOM_LOG_IMU_GYRO_SCALE_STD;
411
state.gyro = temp / scaleFactor;
412
}
413
cached.sensors.ins_data.temperature = (cached.sbg.sbgEComLogImuShort.temperature / SBG_ECOM_LOG_IMU_TEMP_SCALE_STD);
414
updated_ins = true;
415
break;
416
417
case SBG_ECOM_LOG_IMU_DATA: // 3
418
safe_copy_msg_to_object((uint8_t*)&cached.sbg.sbgLogImuData, sizeof(cached.sbg.sbgLogImuData), msg.data, msg.len);
419
420
state.accel = Vector3f(cached.sbg.sbgLogImuData.accelerometers[0], cached.sbg.sbgLogImuData.accelerometers[1], cached.sbg.sbgLogImuData.accelerometers[2]);
421
state.gyro = Vector3f(cached.sbg.sbgLogImuData.gyroscopes[0], cached.sbg.sbgLogImuData.gyroscopes[1], cached.sbg.sbgLogImuData.gyroscopes[2]);
422
cached.sensors.ins_data.temperature = cached.sbg.sbgLogImuData.temperature;
423
updated_ins = true;
424
break;
425
426
case SBG_ECOM_LOG_MAG: // 4
427
safe_copy_msg_to_object((uint8_t*)&cached.sbg.sbgLogMag, sizeof(cached.sbg.sbgLogMag), msg.data, msg.len);
428
429
state.accel = Vector3f(cached.sbg.sbgLogMag.accelerometers[0], cached.sbg.sbgLogMag.accelerometers[1], cached.sbg.sbgLogMag.accelerometers[2]);
430
updated_ins = true;
431
432
cached.sensors.mag_data.field = Vector3f(cached.sbg.sbgLogMag.magnetometers[0], cached.sbg.sbgLogMag.magnetometers[1], cached.sbg.sbgLogMag.magnetometers[2]);
433
updated_mag = true;
434
break;
435
436
case SBG_ECOM_LOG_EKF_EULER: // 6
437
safe_copy_msg_to_object((uint8_t*)&cached.sbg.sbgLogEkfEulerData, sizeof(cached.sbg.sbgLogEkfEulerData), msg.data, msg.len);
438
439
// float euler[3]; /*!< Roll, Pitch and Yaw angles in rad. */
440
// float eulerStdDev[3]; /*!< Roll, Pitch and Yaw angles 1 sigma standard deviation in rad. */
441
state.quat.from_euler(cached.sbg.sbgLogEkfEulerData.euler[0], cached.sbg.sbgLogEkfEulerData.euler[1], cached.sbg.sbgLogEkfEulerData.euler[2]);
442
state.have_quaternion = true;
443
break;
444
445
case SBG_ECOM_LOG_EKF_QUAT: // 7
446
safe_copy_msg_to_object((uint8_t*)&cached.sbg.sbgLogEkfQuatData, sizeof(cached.sbg.sbgLogEkfQuatData), msg.data, msg.len);
447
448
// float quaternion[4]; /*!< Orientation quaternion stored in W, X, Y, Z form. */
449
// float eulerStdDev[3]; /*!< Roll, Pitch and Yaw angles 1 sigma standard deviation in rad. */
450
memcpy(&state.quat, cached.sbg.sbgLogEkfQuatData.quaternion, sizeof(state.quat));
451
state.have_quaternion = true;
452
break;
453
454
case SBG_ECOM_LOG_EKF_NAV: // 8
455
safe_copy_msg_to_object((uint8_t*)&cached.sbg.sbgLogEkfNavData, sizeof(cached.sbg.sbgLogEkfNavData), msg.data, msg.len);
456
457
state.velocity = Vector3f(cached.sbg.sbgLogEkfNavData.velocity[0], cached.sbg.sbgLogEkfNavData.velocity[1], cached.sbg.sbgLogEkfNavData.velocity[2]);
458
state.have_velocity = true;
459
460
state.location = Location(cached.sbg.sbgLogEkfNavData.position[0]*1e7, cached.sbg.sbgLogEkfNavData.position[1]*1e7, cached.sbg.sbgLogEkfNavData.position[2]*1e2, Location::AltFrame::ABSOLUTE);
461
state.last_location_update_us = AP_HAL::micros();
462
if (!state.have_location) {
463
state.have_location = true;
464
} else if (!state.have_origin && cached.sensors.gps_data.fix_type >= AP_GPS_FixType::FIX_3D && SbgEkfStatus_is_fullNav(cached.sbg.sbgLogEkfNavData.status)) {
465
// this is in an else so that origin doesn't get set on the very very first sample, do it on the second one just to give us a tiny bit more chance of a better origin
466
state.origin = state.location;
467
state.have_origin = true;
468
}
469
break;
470
471
case SBG_ECOM_LOG_GPS1_VEL: // 13
472
case SBG_ECOM_LOG_GPS2_VEL: // 16
473
safe_copy_msg_to_object((uint8_t*)&cached.sbg.sbgLogGpsVel, sizeof(cached.sbg.sbgLogGpsVel), msg.data, msg.len);
474
475
cached.sensors.gps_data.ms_tow = cached.sbg.sbgLogGpsVel.timeOfWeek;
476
cached.sensors.gps_data.ned_vel_north = cached.sbg.sbgLogGpsVel.velocity[0];
477
cached.sensors.gps_data.ned_vel_east = cached.sbg.sbgLogGpsVel.velocity[1];
478
cached.sensors.gps_data.ned_vel_down = cached.sbg.sbgLogGpsVel.velocity[2];
479
cached.sensors.gps_data.horizontal_vel_accuracy = Vector2f(cached.sensors.gps_data.ned_vel_north, cached.sensors.gps_data.ned_vel_east).length();
480
// unused - cached.sbg.sbgLogGpsVel.course
481
// unused - cached.sbg.sbgLogGpsVel.courseAcc
482
updated_gps = true;
483
break;
484
485
case SBG_ECOM_LOG_GPS1_POS: // 14
486
case SBG_ECOM_LOG_GPS2_POS: // 17
487
safe_copy_msg_to_object((uint8_t*)&cached.sbg.sbgLogGpsPos, sizeof(cached.sbg.sbgLogGpsPos), msg.data, msg.len);
488
489
cached.sensors.gps_data.ms_tow = cached.sbg.sbgLogGpsPos.timeOfWeek;
490
cached.sensors.gps_data.gps_week = cached.sbg.sbgLogGpsPos.timeOfWeek / AP_MSEC_PER_WEEK;
491
cached.sensors.gps_data.latitude = cached.sbg.sbgLogGpsPos.latitude * 1E7;
492
cached.sensors.gps_data.longitude = cached.sbg.sbgLogGpsPos.longitude * 1E7;
493
cached.sensors.gps_data.msl_altitude = cached.sbg.sbgLogGpsPos.altitude * 100;
494
// unused - cached.sbg.sbgLogGpsPos.undulation
495
cached.sensors.gps_data.horizontal_pos_accuracy = Vector2f(cached.sbg.sbgLogGpsPos.latitudeAccuracy, cached.sbg.sbgLogGpsPos.longitudeAccuracy).length();
496
cached.sensors.gps_data.hdop = cached.sensors.gps_data.horizontal_pos_accuracy;
497
cached.sensors.gps_data.vertical_pos_accuracy = cached.sbg.sbgLogGpsPos.altitudeAccuracy;
498
cached.sensors.gps_data.vdop = cached.sensors.gps_data.vertical_pos_accuracy;
499
cached.sensors.gps_data.satellites_in_view = cached.sbg.sbgLogGpsPos.numSvUsed;
500
// unused - cached.sbg.sbgLogGpsPos.baseStationId
501
// unused - cached.sbg.sbgLogGpsPos.differentialAge
502
cached.sensors.gps_data.fix_type = SbgGpsPosStatus_to_GpsFixType(cached.sbg.sbgLogGpsPos.status);
503
updated_gps = true;
504
break;
505
506
case SBG_ECOM_LOG_AIR_DATA: // 36
507
safe_copy_msg_to_object((uint8_t*)&cached.sbg.sbgLogAirData, sizeof(cached.sbg.sbgLogAirData), msg.data, msg.len);
508
509
if (cached.sbg.sbgLogAirData.status & SBG_ECOM_AIR_DATA_PRESSURE_ABS_VALID) {
510
cached.sensors.baro_data.pressure_pa = cached.sbg.sbgLogAirData.pressureAbs;
511
updated_baro = true;
512
}
513
if (cached.sbg.sbgLogAirData.status & SBG_ECOM_AIR_DATA_ALTITUDE_VALID) {
514
cached.sensors.baro_height = cached.sbg.sbgLogAirData.altitude;
515
updated_baro = true;
516
}
517
if (cached.sbg.sbgLogAirData.status & SBG_ECOM_AIR_DATA_PRESSURE_DIFF_VALID) {
518
cached.sensors.airspeed_data.differential_pressure = cached.sbg.sbgLogAirData.pressureDiff;
519
updated_airspeed = true;
520
}
521
// if (cached.sbg.sbgLogAirData.status & SBG_ECOM_AIR_DATA_AIRPSEED_VALID) {
522
// // we don't accept airspeed directly, we compute it ourselves in AP_Airspeed via diff pressure
523
// }
524
525
if ((cached.sbg.sbgLogAirData.status & SBG_ECOM_AIR_DATA_TEMPERATURE_VALID) && (updated_baro || updated_airspeed)) {
526
cached.sensors.airspeed_data.temperature = cached.sbg.sbgLogAirData.airTemperature;
527
cached.sensors.baro_data.temperature = cached.sbg.sbgLogAirData.airTemperature;
528
}
529
break;
530
531
default:
532
// GCS_SEND_TEXT(MAV_SEVERITY_INFO, "SBG: unhandled ID=%u, CLASS=%u, LEN=%u", (unsigned)msg.msgid, (unsigned)msg.msgclass, (unsigned)msg.len);
533
return;
534
} // switch msgid
535
} // semaphore
536
537
#if AP_GPS_ENABLED
538
if (updated_gps) {
539
cached.sensors.gps_ms = now_ms;
540
uint8_t instance;
541
if (AP::gps().get_first_external_instance(instance)) {
542
AP::gps().handle_external(cached.sensors.gps_data, instance);
543
}
544
}
545
#else
546
(void)updated_gps;
547
#endif
548
549
#if AP_COMPASS_EXTERNALAHRS_ENABLED
550
if (updated_mag) {
551
cached.sensors.mag_ms = now_ms;
552
AP::compass().handle_external(cached.sensors.mag_data);
553
}
554
#else
555
(void)updated_mag;
556
#endif
557
558
#if AP_BARO_EXTERNALAHRS_ENABLED
559
if (updated_baro) {
560
cached.sensors.baro_ms = now_ms;
561
cached.sensors.baro_data.instance = 0;
562
AP::baro().handle_external(cached.sensors.baro_data);
563
}
564
#else
565
(void)updated_baro;
566
#endif
567
568
#if AP_AIRSPEED_EXTERNAL_ENABLED && (APM_BUILD_COPTER_OR_HELI || APM_BUILD_TYPE(APM_BUILD_ArduPlane))
569
if (updated_airspeed && AP::airspeed() != nullptr) {
570
cached.sensors.airspeed_ms = now_ms;
571
AP::airspeed()->handle_external(cached.sensors.airspeed_data);
572
}
573
#else
574
(void)updated_airspeed;
575
#endif
576
577
578
if (updated_ins) {
579
cached.sensors.ins_data.accel = state.accel;
580
cached.sensors.ins_data.gyro = state.gyro;
581
cached.sensors.ins_ms = now_ms;
582
AP::ins().handle_external(cached.sensors.ins_data);
583
}
584
585
last_received_ms = now_ms;
586
}
587
588
void AP_ExternalAHRS_SBG::safe_copy_msg_to_object(uint8_t* dest, const uint16_t dest_len, const uint8_t* src, const uint16_t src_len)
589
{
590
// To allow for future changes of the SBG protocol the protocol allows extending messages
591
// which can be detected by a length mismatch, usually longer. To allow for this you assume unused data is zero
592
// but instead of zeroing the packet every time before populating it it's best to only zero when necessary.
593
if (dest_len != src_len) {
594
memset(dest, 0, dest_len);
595
}
596
memcpy(dest, src, MIN(dest_len,src_len));
597
}
598
599
bool AP_ExternalAHRS_SBG::SbgEkfStatus_is_fullNav(const uint32_t ekfStatus)
600
{
601
SbgEComSolutionMode solutionMode = (SbgEComSolutionMode)(ekfStatus & SBG_ECOM_LOG_EKF_SOLUTION_MODE_MASK);
602
603
return (solutionMode == SBG_ECOM_SOL_MODE_NAV_POSITION);
604
}
605
606
AP_GPS_FixType AP_ExternalAHRS_SBG::SbgGpsPosStatus_to_GpsFixType(const uint32_t gpsPosStatus)
607
{
608
const uint32_t fix = (gpsPosStatus >> SBG_ECOM_GPS_POS_TYPE_SHIFT) & SBG_ECOM_GPS_POS_TYPE_MASK;
609
switch ((SbgEComGpsPosType)fix) {
610
case SBG_ECOM_POS_NO_SOLUTION: /*!< No valid solution available. */
611
case SBG_ECOM_POS_UNKNOWN_TYPE: /*!< An unknown solution type has been computed. */
612
return AP_GPS_FixType::NONE;
613
614
case SBG_ECOM_POS_SINGLE: /*!< Single point solution position. */
615
return AP_GPS_FixType::FIX_3D;
616
617
case SBG_ECOM_POS_PSRDIFF: /*!< Standard Pseudorange Differential Solution (DGPS). */
618
case SBG_ECOM_POS_SBAS: /*!< SBAS satellite used for differential corrections. */
619
case SBG_ECOM_POS_OMNISTAR: /*!< Omnistar VBS Position (L1 sub-meter). */
620
return AP_GPS_FixType::DGPS;
621
622
case SBG_ECOM_POS_RTK_FLOAT: /*!< Floating RTK ambiguity solution (20 cms RTK). */
623
case SBG_ECOM_POS_PPP_FLOAT: /*!< Precise Point Positioning with float ambiguities. */
624
return AP_GPS_FixType::RTK_FLOAT;
625
626
case SBG_ECOM_POS_RTK_INT: /*!< Integer RTK ambiguity solution (2 cms RTK). */
627
case SBG_ECOM_POS_PPP_INT: /*!< Precise Point Positioning with fixed ambiguities. */
628
case SBG_ECOM_POS_FIXED: /*!< Fixed location solution position. */
629
return AP_GPS_FixType::RTK_FIXED;
630
}
631
return AP_GPS_FixType::NONE;
632
}
633
634
void AP_ExternalAHRS_SBG::get_filter_status(nav_filter_status &status) const
635
{
636
WITH_SEMAPHORE(state.sem);
637
memset(&status, 0, sizeof(status));
638
639
if (cached.sensors.ins_ms != 0 && cached.sensors.gps_ms != 0) {
640
status.flags.initalized = true;
641
}
642
if (!healthy()) {
643
return;
644
}
645
646
if (state.have_location) {
647
status.flags.vert_pos = true;
648
status.flags.horiz_pos_rel = true;
649
status.flags.horiz_pos_abs = true;
650
status.flags.pred_horiz_pos_rel = true;
651
status.flags.pred_horiz_pos_abs = true;
652
status.flags.using_gps = true;
653
}
654
655
if (state.have_quaternion) {
656
status.flags.attitude = true;
657
}
658
659
if (state.have_velocity) {
660
status.flags.vert_vel = true;
661
status.flags.horiz_vel = true;
662
}
663
}
664
665
bool AP_ExternalAHRS_SBG::pre_arm_check(char *failure_msg, uint8_t failure_msg_len) const
666
{
667
if (!setup_complete) {
668
hal.util->snprintf(failure_msg, failure_msg_len, "SBG setup failed");
669
return false;
670
}
671
if (!healthy()) {
672
hal.util->snprintf(failure_msg, failure_msg_len, "SBG unhealthy");
673
return false;
674
}
675
return true;
676
}
677
678
#if AP_COMPASS_ENABLED
679
bool AP_ExternalAHRS_SBG::send_MagData(AP_HAL::UARTDriver *_uart)
680
{
681
SbgLogMag mag_data_log {};
682
mag_data_log.timeStamp = AP_HAL::micros();
683
684
const auto &compass = AP::compass();
685
if (compass.available() || compass.healthy()) {
686
// TODO: consider also checking compass.last_update_usec() to only send when we have new data
687
688
const Vector3f mag_field = compass.get_field();
689
mag_data_log.magnetometers[0] = mag_field[0];
690
mag_data_log.magnetometers[1] = mag_field[1];
691
mag_data_log.magnetometers[2] = mag_field[2];
692
693
mag_data_log.status |= (SBG_ECOM_MAG_MAG_X_BIT | SBG_ECOM_MAG_MAG_Y_BIT | SBG_ECOM_MAG_MAG_Z_BIT | SBG_ECOM_MAG_MAGS_IN_RANGE | SBG_ECOM_MAG_CALIBRATION_OK);
694
}
695
696
const sbgMessage msg = sbgMessage(SBG_ECOM_CLASS_LOG_ECOM_0, SBG_ECOM_LOG_MAG, (uint8_t*)&mag_data_log, sizeof(mag_data_log));
697
return send_sbgMessage(_uart, msg);
698
}
699
#endif // AP_COMPASS_ENABLED
700
701
bool AP_ExternalAHRS_SBG::send_AirData(AP_HAL::UARTDriver *_uart)
702
{
703
SbgLogAirData air_data_log {};
704
air_data_log.timeStamp = 0;
705
air_data_log.status |= SBG_ECOM_AIR_DATA_TIME_IS_DELAY;
706
707
#if AP_BARO_ENABLED
708
const auto &baro = AP::baro();
709
if (baro.healthy()) {
710
air_data_log.pressureAbs = baro.get_pressure();
711
air_data_log.altitude = baro.get_altitude();
712
air_data_log.airTemperature = baro.get_temperature();
713
714
air_data_log.status |= (SBG_ECOM_AIR_DATA_PRESSURE_ABS_VALID | SBG_ECOM_AIR_DATA_ALTITUDE_VALID | SBG_ECOM_AIR_DATA_TEMPERATURE_VALID);
715
}
716
#endif // AP_BARO_ENABLED
717
718
#if AP_AIRSPEED_ENABLED
719
auto *airspeed = AP::airspeed();
720
if (airspeed != nullptr && airspeed->healthy()) {
721
float airTemperature;
722
if (airspeed->get_temperature(airTemperature)) {
723
air_data_log.airTemperature = airTemperature;
724
air_data_log.status |= SBG_ECOM_AIR_DATA_TEMPERATURE_VALID;
725
}
726
727
air_data_log.pressureDiff = airspeed->get_differential_pressure();
728
air_data_log.trueAirspeed = airspeed->get_airspeed();
729
air_data_log.status |= (SBG_ECOM_AIR_DATA_PRESSURE_DIFF_VALID | SBG_ECOM_AIR_DATA_AIRPSEED_VALID);
730
}
731
#endif // AP_AIRSPEED_ENABLED
732
733
const sbgMessage msg = sbgMessage(SBG_ECOM_CLASS_LOG_ECOM_0, SBG_ECOM_LOG_AIR_DATA, (uint8_t*)&air_data_log, sizeof(air_data_log));
734
return send_sbgMessage(_uart, msg);
735
}
736
737
#endif // AP_EXTERNAL_AHRS_SBG_ENABLED
738
739
740