Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Ardupilot
GitHub Repository: Ardupilot/ardupilot
Path: blob/master/libraries/AP_ExternalAHRS/AP_ExternalAHRS_SensAItion.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
Support for SensAItion serial connected INS and IMU
17
Implements SensAItion protocol with ArduPilot-specific adaptations
18
*/
19
20
#include "AP_ExternalAHRS_SensAItion.h"
21
22
#if AP_EXTERNAL_AHRS_SENSAITION_ENABLED
23
24
#include <float.h>
25
#include <math.h>
26
27
#include <AP_SerialManager/AP_SerialManager.h>
28
#include <GCS_MAVLink/GCS.h>
29
#include <AP_HAL/AP_HAL.h>
30
#include <AP_GPS/AP_GPS.h>
31
#include <AP_Baro/AP_Baro.h>
32
#include <AP_Compass/AP_Compass.h>
33
#include <AP_InertialSensor/AP_InertialSensor.h>
34
35
namespace
36
{
37
const float MINIMUM_INTERESTING_BAROMETER_CHANGE_p = 1.0f;
38
const float MINIMUM_INTERESTING_TEMP_CHANGE_degc = 0.1f;
39
const uint32_t BARO_UPDATE_TIMEOUT_ms = 100U;
40
}
41
42
extern const AP_HAL::HAL &hal;
43
44
AP_ExternalAHRS_SensAItion::AP_ExternalAHRS_SensAItion(AP_ExternalAHRS *_frontend, AP_ExternalAHRS::state_t &_state) :
45
AP_ExternalAHRS_backend(_frontend, _state),
46
parser(AP_ExternalAHRS_SensAItion_Parser::ConfigMode::IMU)
47
{
48
ins_mode_enabled = option_is_set(AP_ExternalAHRS::OPTIONS::SENSAITION_INS);
49
50
auto mode = ins_mode_enabled ?
51
AP_ExternalAHRS_SensAItion_Parser::ConfigMode::INTERLEAVED_INS :
52
AP_ExternalAHRS_SensAItion_Parser::ConfigMode::IMU;
53
54
if (ins_mode_enabled) {
55
parser = AP_ExternalAHRS_SensAItion_Parser(mode);
56
}
57
58
auto &sm = AP::serialmanager();
59
uart = sm.find_serial(AP_SerialManager::SerialProtocol_AHRS, 0);
60
baudrate = sm.find_baudrate(AP_SerialManager::SerialProtocol_AHRS, 0);
61
port_num = sm.find_portnum(AP_SerialManager::SerialProtocol_AHRS, 0);
62
if (!uart || baudrate == 0 || port_num == -1) {
63
GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "KEBNI: Serial Port Not Found!");
64
return;
65
}
66
67
if (ins_mode_enabled) {
68
set_default_sensors(uint16_t(AP_ExternalAHRS::AvailableSensor::IMU) |
69
uint16_t(AP_ExternalAHRS::AvailableSensor::GPS) |
70
uint16_t(AP_ExternalAHRS::AvailableSensor::BARO) |
71
uint16_t(AP_ExternalAHRS::AvailableSensor::COMPASS));
72
} else {
73
set_default_sensors(uint16_t(AP_ExternalAHRS::AvailableSensor::IMU));
74
}
75
76
if (!hal.scheduler->thread_create(
77
FUNCTOR_BIND_MEMBER(&AP_ExternalAHRS_SensAItion::update_thread, void),
78
"AHRS_SensAItion", 2048, AP_HAL::Scheduler::PRIORITY_SPI, 0)) {
79
GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "KEBNI: Failed to create thread!");
80
}
81
}
82
83
int8_t AP_ExternalAHRS_SensAItion::get_port() const
84
{
85
return uart ? port_num : -1;
86
}
87
88
const char* AP_ExternalAHRS_SensAItion::get_name() const
89
{
90
return "Kebni SensAItion";
91
}
92
93
bool AP_ExternalAHRS_SensAItion::healthy() const
94
{
95
const uint32_t now_ms = AP_HAL::millis();
96
97
WITH_SEMAPHORE(driver_state.semaphore);
98
99
if ((now_ms - driver_state.last_imu_pkt_ms) > 160) {
100
// IMU is required at high rate for health
101
return false;
102
}
103
104
if (ins_mode_enabled) {
105
if ((now_ms - driver_state.last_ins_pkt_ms) > 400) {
106
// INS packets must also have high enough rate
107
return false;
108
}
109
110
if (!(driver_state.last_sensor_valid & 0x01)) {
111
// IMU not available
112
return false;
113
}
114
115
if (driver_state.last_gnss1_fix < 3) {
116
// No 3D satellite fix
117
return false;
118
}
119
}
120
121
return true;
122
}
123
124
bool AP_ExternalAHRS_SensAItion::initialised() const
125
{
126
return setup_complete;
127
}
128
129
bool AP_ExternalAHRS_SensAItion::pre_arm_check(char *failure_msg, uint8_t failure_msg_len) const
130
{
131
if (!healthy()) {
132
hal.util->snprintf(failure_msg, failure_msg_len, "SensAItion Unhealthy");
133
return false;
134
}
135
136
if (ins_mode_enabled) {
137
uint8_t last_alignment_status;
138
{
139
WITH_SEMAPHORE(driver_state.semaphore);
140
last_alignment_status = driver_state.last_alignment_status;
141
}
142
if (last_alignment_status != 1) {
143
hal.util->snprintf(failure_msg, failure_msg_len, "SensAItion Aligning");
144
return false;
145
}
146
}
147
148
return true;
149
}
150
151
void AP_ExternalAHRS_SensAItion::get_filter_status(nav_filter_status &status) const
152
{
153
memset(&status, 0, sizeof(status));
154
155
status.flags.initalized = initialised();
156
157
if (healthy()) {
158
WITH_SEMAPHORE(driver_state.semaphore);
159
if (ins_mode_enabled && driver_state.last_alignment_status == 1) {
160
status.flags.attitude = true;
161
status.flags.horiz_pos_abs = true;
162
status.flags.vert_pos = true;
163
status.flags.horiz_vel = true;
164
status.flags.vert_vel = true;
165
status.flags.using_gps = true;
166
status.flags.horiz_pos_rel = true;
167
status.flags.pred_horiz_pos_abs = true;
168
status.flags.pred_horiz_pos_rel = true;
169
}
170
}
171
}
172
173
bool AP_ExternalAHRS_SensAItion::get_variances(float &velVar, float &posVar, float &hgtVar, Vector3f &magVar, float &tasVar) const
174
{
175
WITH_SEMAPHORE(driver_state.semaphore);
176
if (ins_mode_enabled && driver_state.last_alignment_status == 1) {
177
posVar = driver_state.last_h_pos_quality * pos_gate_scale;
178
velVar = driver_state.last_vel_quality * vel_gate_scale;
179
hgtVar = driver_state.last_v_pos_quality * hgt_gate_scale;
180
tasVar = 0; //not used
181
return true;
182
}
183
184
return false;
185
}
186
187
uint8_t AP_ExternalAHRS_SensAItion::num_gps_sensors() const
188
{
189
return ins_mode_enabled ? 1 : 0;
190
}
191
192
// ---------------------------------------------------------------------------
193
// THREAD & PROFILER
194
// ---------------------------------------------------------------------------
195
void AP_ExternalAHRS_SensAItion::update_thread()
196
{
197
while (true) {
198
if (!check_uart()) {
199
hal.scheduler->delay_microseconds(500);
200
}
201
}
202
}
203
204
bool AP_ExternalAHRS_SensAItion::check_uart()
205
{
206
WITH_SEMAPHORE(sem_handle);
207
208
if (!uart) {
209
return false;
210
}
211
212
if (!setup_complete) {
213
uart->begin(baudrate);
214
setup_complete = true;
215
GCS_SEND_TEXT(MAV_SEVERITY_INFO, "KEBNI: INIT. Mode:%d Baud:%u",
216
(int)ins_mode_enabled, (unsigned)baudrate);
217
}
218
219
const auto nread = uart->read(buffer, sizeof(buffer));
220
if (nread <= 0) {
221
return false;
222
}
223
224
const uint32_t now_ms = AP_HAL::millis();
225
226
AP_ExternalAHRS_SensAItion_Parser::Measurement meas;
227
bool found_measurement = false;
228
ssize_t parsed_bytes = 0;
229
while (parsed_bytes < nread) {
230
const size_t max_bytes_to_parse = nread - parsed_bytes;
231
parsed_bytes += parser.parse_stream(&buffer[parsed_bytes], max_bytes_to_parse, meas);
232
233
switch (meas.type) {
234
case AP_ExternalAHRS_SensAItion_Parser::MeasurementType::UNINITIALIZED:
235
// The parser is done parsing the whole buffer, but didn't find any
236
// more complete message. (We might be in the middle of a message
237
// that will be finished later.)
238
break;
239
case AP_ExternalAHRS_SensAItion_Parser::MeasurementType::IMU:
240
handle_imu(meas, now_ms);
241
found_measurement = true;
242
break;
243
case AP_ExternalAHRS_SensAItion_Parser::MeasurementType::AHRS:
244
handle_ahrs(meas, now_ms);
245
found_measurement = true;
246
break;
247
case AP_ExternalAHRS_SensAItion_Parser::MeasurementType::INS:
248
handle_ins(meas, now_ms);
249
found_measurement = true;
250
break;
251
}
252
}
253
254
return found_measurement;
255
}
256
257
void AP_ExternalAHRS_SensAItion::handle_imu(const AP_ExternalAHRS_SensAItion_Parser::Measurement& meas, uint32_t now_ms)
258
{
259
{
260
WITH_SEMAPHORE(state.sem);
261
state.accel = meas.acceleration_mss;
262
state.gyro = meas.angular_velocity_rads;
263
}
264
265
// INS
266
ins.accel = meas.acceleration_mss;
267
ins.gyro = meas.angular_velocity_rads;
268
ins.temperature = meas.temperature_degc;
269
AP::ins().handle_external(ins);
270
271
#if AP_COMPASS_EXTERNALAHRS_ENABLED
272
// COMPASS
273
mag.field = meas.magnetic_field_mgauss;
274
AP::compass().handle_external(mag);
275
#endif
276
277
#if AP_BARO_EXTERNALAHRS_ENABLED
278
bool baro_updated = false;
279
#endif
280
281
{
282
WITH_SEMAPHORE(driver_state.semaphore);
283
driver_state.last_imu_pkt_ms = now_ms;
284
285
#if AP_BARO_EXTERNALAHRS_ENABLED
286
// BARO
287
// ArduPlane has an internal check that triggers an error if there are too many barometer
288
// readings with the same value. At high sampling rates, we triggered that check because
289
// the barometer is internally sampled at a lower rate. To avoid that, we only update the value
290
// if it changes OR a certain minimum time has passed.
291
const bool pressure_changed = fabsf(baro.pressure_pa - meas.air_pressure_p) > MINIMUM_INTERESTING_BAROMETER_CHANGE_p;
292
const bool temp_changed = fabsf(baro.temperature - meas.temperature_degc) > MINIMUM_INTERESTING_TEMP_CHANGE_degc;
293
const bool timeout = now_ms > driver_state.last_baro_update_ms + BARO_UPDATE_TIMEOUT_ms;
294
295
if (pressure_changed || temp_changed || timeout) {
296
driver_state.last_baro_update_ms = now_ms;
297
baro.instance = 0;
298
baro.pressure_pa = meas.air_pressure_p;
299
baro.temperature = meas.temperature_degc;
300
baro_updated = true;
301
}
302
#endif
303
304
}
305
306
#if AP_BARO_EXTERNALAHRS_ENABLED
307
// Do the update after releasing the semaphore, for speed
308
if (baro_updated) {
309
AP::baro().handle_external(baro);
310
}
311
#endif
312
}
313
314
void AP_ExternalAHRS_SensAItion::handle_ahrs(const AP_ExternalAHRS_SensAItion_Parser::Measurement& meas, uint32_t now_ms)
315
{
316
{
317
WITH_SEMAPHORE(driver_state.semaphore);
318
driver_state.last_quat_pkt_ms = now_ms;
319
}
320
321
{
322
WITH_SEMAPHORE(state.sem);
323
state.quat = meas.orientation;
324
state.have_quaternion = true;
325
}
326
}
327
328
void AP_ExternalAHRS_SensAItion::handle_ins(const AP_ExternalAHRS_SensAItion_Parser::Measurement& meas, uint32_t now_ms)
329
{
330
// STATE
331
{
332
WITH_SEMAPHORE(state.sem);
333
state.location = Location(
334
meas.location.lat,
335
meas.location.lng,
336
meas.location.alt,
337
Location::AltFrame::ABSOLUTE
338
);
339
state.velocity = meas.velocity_ned;
340
state.have_location = true;
341
state.have_velocity = true;
342
state.last_location_update_us = AP_HAL::micros();
343
344
if (!state.have_origin && meas.alignment_status) {
345
state.origin = Location(
346
meas.location.lat,
347
meas.location.lng,
348
meas.location.alt,
349
Location::AltFrame::ABSOLUTE
350
);
351
state.have_origin = true;
352
GCS_SEND_TEXT(MAV_SEVERITY_NOTICE, "KEBNI: Origin Set.");
353
}
354
}
355
356
{
357
WITH_SEMAPHORE(driver_state.semaphore);
358
359
// Local data
360
driver_state.last_ins_pkt_ms = now_ms;
361
driver_state.last_alignment_status = meas.alignment_status;
362
driver_state.last_sensor_valid = meas.sensor_valid;
363
driver_state.last_gnss1_fix = meas.gnss1_fix;
364
driver_state.last_gnss2_fix = meas.gnss2_fix;
365
driver_state.last_error_flags = meas.error_flags;
366
driver_state.last_h_pos_quality = meas.pos_accuracy.xy().length();
367
driver_state.last_v_pos_quality = meas.pos_accuracy.z;
368
driver_state.last_vel_quality = meas.vel_accuracy.length();
369
370
// GPS
371
gps.horizontal_pos_accuracy = driver_state.last_h_pos_quality;
372
gps.vertical_pos_accuracy = driver_state.last_v_pos_quality;
373
}
374
375
// GPS - continued
376
gps.gps_week = meas.gps_week;
377
gps.ms_tow = meas.time_itow_ms;
378
gps.fix_type = AP_GPS_FixType(meas.gnss1_fix);
379
gps.satellites_in_view = meas.num_sats_gnss1;
380
gps.horizontal_vel_accuracy = meas.vel_accuracy.xy().length();
381
gps.latitude = meas.location.lat;
382
gps.longitude = meas.location.lng;
383
384
// Note: SensAItion reports altitude relative to WGS84, not MSL.
385
// But we expect the user to reset the altitude to 0 at start,
386
// so the absolute reference should not matter.
387
gps.msl_altitude = meas.location.alt;
388
gps.ned_vel_north = meas.velocity_ned.x;
389
gps.ned_vel_east = meas.velocity_ned.y;
390
gps.ned_vel_down = meas.velocity_ned.z;
391
392
// 3. Estimate DOPs (Unitless) using assumed UERE of 3.0m
393
// This answers "What is HDOP/VDOP?"
394
const float ASSUMED_UERE = 3.0f;
395
396
float est_hdop = gps.horizontal_pos_accuracy / ASSUMED_UERE;
397
float est_vdop = gps.vertical_pos_accuracy / ASSUMED_UERE;
398
399
// 4. Sanity Clamping (DOP cannot be 0, and rarely < 0.6)
400
if (est_hdop < 0.7f) {
401
est_hdop = 0.7f;
402
}
403
if (est_vdop < 0.7f) {
404
est_vdop = 0.7f;
405
}
406
gps.hdop = est_hdop;
407
gps.vdop = est_vdop;
408
409
// Handle
410
uint8_t instance;
411
if (AP::gps().get_first_external_instance(instance)) {
412
AP::gps().handle_external(gps, instance);
413
}
414
}
415
416
#endif // AP_EXTERNAL_AHRS_SENSAITION_ENABLED
417
418