Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Ardupilot
GitHub Repository: Ardupilot/ardupilot
Path: blob/master/libraries/AP_ExternalAHRS/AP_ExternalAHRS_GSOF.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
This program is distributed in the hope that it will be useful,
7
but WITHOUT ANY WARRANTY; without even the implied warranty of
8
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9
GNU General Public License for more details.
10
You should have received a copy of the GNU General Public License
11
along with this program. If not, see <http://www.gnu.org/licenses/>.
12
*/
13
14
// Usage in SITL with hardware for debugging:
15
// $ sim_vehicle.py -v Plane --console --map -DG
16
// param set AHRS_EKF_TYPE 11
17
// param set EAHRS_TYPE 6
18
// For EAHRS as a GPS:
19
// param set GPS1_TYPE 21
20
// Configure GSOF 1, 49,50,70 on UDP port 44444, "UDP Mode" and "UDP Broadcast Transmit"
21
// Consider setting EK3_SRC1_YAW to 2 on the bench...
22
23
// Usage with NET parameters and ethernet in SITL with hardware:
24
// param set NET_ENABLE 1
25
// param set NET_P1_TYPE 2
26
// # Set up AHRS input
27
// param set NET_P1_PROTOCOL 36
28
// param set SIM_GPS1_TYPE 0
29
// param set NET_P1_PORT 44444
30
// param set EAHRS_TYPE 6
31
//
32
// Usage with serial in SITL with hardware:
33
// $ sim_vehicle.py -v Plane -A "--serial3=uart:/dev/ttyUSB0" --console --map -DG
34
// param set SERIAL3_PROTOCOL 36
35
// param set SERIAL3_BAUD 115200
36
// param set EAHRS_TYPE 6
37
// # ensure NET_* if off if you were using ethernet.
38
// # To enable the EAHRS to provide GPS:
39
// param set GPS2_TYPE 21
40
41
// On most hardware, you must enable EAHRS:
42
// ./waf configure --board Pixhawk6X --enable-AHRS_EXT
43
44
// GPS ride-along with EARHS as the 2nd GPS
45
// param set GPS_AUTO_SWITCH 0
46
// param set GPS2_TYPE 21
47
// param set GPS_PRIMARY 0
48
49
#define AP_MATH_ALLOW_DOUBLE_FUNCTIONS 1
50
51
#include "AP_ExternalAHRS_config.h"
52
53
#if AP_EXTERNAL_AHRS_GSOF_ENABLED
54
55
#include "AP_ExternalAHRS_GSOF.h"
56
#include <AP_Baro/AP_Baro.h>
57
#include <AP_Compass/AP_Compass.h>
58
#include <AP_GPS/AP_GPS.h>
59
#include <AP_HAL/utility/sparse-endian.h>
60
#include <AP_InertialSensor/AP_InertialSensor.h>
61
#include <GCS_MAVLink/GCS.h>
62
#include <AP_Logger/AP_Logger.h>
63
#include <AP_BoardConfig/AP_BoardConfig.h>
64
#include <AP_HAL/utility/Socket.h>
65
66
static const char* LOG_FMT = "%s ExternalAHRS: %s";
67
68
extern const AP_HAL::HAL &hal;
69
70
AP_ExternalAHRS_GSOF::AP_ExternalAHRS_GSOF(AP_ExternalAHRS *_frontend,
71
AP_ExternalAHRS::state_t &_state): AP_ExternalAHRS_backend(_frontend, _state)
72
{
73
auto &sm = AP::serialmanager();
74
uart = sm.find_serial(AP_SerialManager::SerialProtocol_AHRS, 0);
75
if (uart == nullptr) {
76
GCS_SEND_TEXT(MAV_SEVERITY_ERROR, LOG_FMT, get_name(), "no UART");
77
return;
78
}
79
baudrate = sm.find_baudrate(AP_SerialManager::SerialProtocol_AHRS, 0);
80
port_num = sm.find_portnum(AP_SerialManager::SerialProtocol_AHRS, 0);
81
82
if (!hal.scheduler->thread_create(FUNCTOR_BIND_MEMBER(&AP_ExternalAHRS_GSOF::update_thread, void), "AHRS", 2048, AP_HAL::Scheduler::PRIORITY_SPI, 0)) {
83
AP_BoardConfig::allocation_error("GSOF ExternalAHRS failed to allocate ExternalAHRS update thread");
84
}
85
86
// Offer GPS even through it's a tightly coupled EKF.
87
set_default_sensors(uint16_t(AP_ExternalAHRS::AvailableSensor::GPS));
88
}
89
90
// get serial port number for the uart
91
int8_t AP_ExternalAHRS_GSOF::get_port(void) const
92
{
93
if (uart == nullptr) {
94
return -1;
95
}
96
return port_num;
97
};
98
99
void AP_ExternalAHRS_GSOF::update_thread(void)
100
{
101
// TODO configure receiver to output expected data.
102
103
#if AP_EXTERNALAHRS_GSOF_DEBUG_ENABLED
104
auto last_debug = AP_HAL::millis();
105
uint32_t pps = 0;
106
#endif // AP_EXTERNALAHRS_GSOF_DEBUG_ENABLED
107
108
uart->begin(baudrate);
109
110
while (true) {
111
const auto available = uart->available();
112
if (available == 0) {
113
hal.scheduler->delay_microseconds(100);
114
continue;
115
}
116
uint8_t c;
117
if (!uart->read(c)) {
118
hal.scheduler->delay_microseconds(100);
119
continue;
120
}
121
122
AP_GSOF::MsgTypes parsed;
123
const auto parse_res = parse(c, parsed);
124
if (parse_res != PARSED_GSOF_DATA) {
125
continue;
126
}
127
128
auto const now = AP_HAL::millis();
129
#if AP_EXTERNALAHRS_GSOF_DEBUG_ENABLED
130
pps++;
131
132
if (now - last_debug > 1000) {
133
GCS_SEND_TEXT(MAV_SEVERITY_INFO,
134
"GSOF PPS: %lu, ins %u",
135
static_cast<unsigned long>(pps),
136
static_cast<uint16_t>(ins_full_nav.gnss_status));
137
last_debug = now;
138
pps = 0;
139
}
140
#endif // AP_EXTERNALAHRS_GSOF_DEBUG_ENABLED
141
if (parsed.get(AP_GSOF::POS_TIME)) {
142
last_pos_time_ms = now;
143
144
gps_data.satellites_in_view = pos_time.num_sats;
145
}
146
147
if (parsed.get(AP_GSOF::INS_FULL_NAV)) {
148
last_ins_full_nav_ms = now;
149
150
// The Trimble PX-1 is a tightly coupled GNSS-INS.
151
// Although generally we don't want to couple EKF's
152
// by populating EKF data from a GPS into ArduPilot's GPS
153
// which then goes to AP's EKF, this is the only approach.
154
// Extensive flight testing indicates good performance,
155
// and proper handling of failure of the PX-1 INS results
156
// in proper ignoring of the GPS data.
157
gps_data.gps_week = ins_full_nav.gps_week;
158
gps_data.ms_tow = ins_full_nav.gps_time_ms;
159
gps_data.ned_vel_north = ins_full_nav.vel_n;
160
gps_data.ned_vel_east = ins_full_nav.vel_e;
161
gps_data.ned_vel_down = ins_full_nav.vel_d;
162
switch(ins_full_nav.gnss_status) {
163
case AP_GSOF::GnssStatus::FIX_NOT_AVAILABLE:
164
gps_data.fix_type = AP_GPS_FixType::NONE;
165
break;
166
case AP_GSOF::GnssStatus::GNSS_SPS_MODE:
167
gps_data.fix_type = AP_GPS_FixType::FIX_3D;
168
break;
169
case AP_GSOF::GnssStatus::DGPS_SPS_MODE:
170
gps_data.fix_type = AP_GPS_FixType::DGPS;
171
break;
172
case AP_GSOF::GnssStatus::GNSS_PPS_MODE:
173
gps_data.fix_type = AP_GPS_FixType::PPP;
174
break;
175
case AP_GSOF::GnssStatus::FIXED_RTK_MODE:
176
gps_data.fix_type = AP_GPS_FixType::RTK_FIXED;
177
break;
178
case AP_GSOF::GnssStatus::FLOAT_RTK_MODE:
179
gps_data.fix_type = AP_GPS_FixType::RTK_FLOAT;
180
break;
181
case AP_GSOF::GnssStatus::DR_MODE:
182
gps_data.fix_type = AP_GPS_FixType::NONE;
183
break;
184
}
185
if (ins_full_nav.gnss_status != AP_GSOF::GnssStatus::FIX_NOT_AVAILABLE) {
186
// If fix is unavailable, the lat/lon may be zero, so don't post the data.
187
// To reproduce this condition, disconnect the GPS antenna and reboot the receiver.
188
post_filter();
189
}
190
}
191
if (parsed.get(AP_GSOF::INS_RMS)) {
192
last_ins_rms_ms = now;
193
194
gps_data.horizontal_pos_accuracy = Vector2d(ins_rms.pos_rms_n, ins_rms.pos_rms_e).length();
195
gps_data.vertical_pos_accuracy = ins_rms.pos_rms_d;
196
gps_data.horizontal_vel_accuracy = Vector2d(ins_rms.vel_rms_n, ins_rms.vel_rms_e).length();
197
}
198
if (parsed.get(AP_GSOF::LLH_MSL)) {
199
last_llh_msl_ms = now;
200
201
gps_data.longitude = static_cast<int32_t>(llh_msl.longitude * 1E7);
202
gps_data.latitude = static_cast<int32_t>(llh_msl.latitude * 1E7);
203
gps_data.msl_altitude = static_cast<int32_t>(llh_msl.altitude_msl * 1E2);
204
}
205
206
if (parsed.get(AP_GSOF::LLH_MSL) && parsed.get(AP_GSOF::INS_FULL_NAV)) {
207
undulation = ins_full_nav.altitude - llh_msl.altitude_msl;
208
}
209
210
AP_GSOF::MsgTypes expected_gps;
211
expected_gps.set(AP_GSOF::POS_TIME);
212
expected_gps.set(AP_GSOF::INS_FULL_NAV);
213
expected_gps.set(AP_GSOF::INS_RMS);
214
expected_gps.set(AP_GSOF::LLH_MSL);
215
uint8_t instance;
216
if (AP::gps().get_first_external_instance(instance) && parsed == expected_gps) {
217
AP::gps().handle_external(gps_data, instance);
218
}
219
check_initialise_state();
220
}
221
}
222
223
void AP_ExternalAHRS_GSOF::check_initialise_state(void)
224
{
225
const bool new_init_state = initialised();
226
// Only send the message after fully booted up, otherwise it gets dropped.
227
if (!last_init_state && new_init_state && AP_HAL::millis() > 5000) {
228
GCS_SEND_TEXT(MAV_SEVERITY_INFO, LOG_FMT, get_name(), "initialised.");
229
last_init_state = new_init_state;
230
}
231
}
232
233
void AP_ExternalAHRS_GSOF::post_filter() const
234
{
235
WITH_SEMAPHORE(state.sem);
236
state.velocity = Vector3f{ins_full_nav.vel_n, ins_full_nav.vel_e, ins_full_nav.vel_d};
237
state.have_velocity = true;
238
239
state.location = Location(
240
ins_full_nav.latitude * 1E7,
241
ins_full_nav.longitude * 1E7,
242
(ins_full_nav.altitude - undulation) * 1E2,
243
Location::AltFrame::ABSOLUTE);
244
state.have_location = true;
245
246
state.quat.from_euler(
247
radians(ins_full_nav.roll_deg),
248
radians(ins_full_nav.pitch_deg),
249
radians(ins_full_nav.heading_deg));
250
state.have_quaternion = true;
251
252
#if CONFIG_HAL_BOARD == HAL_BOARD_SITL
253
if (!state.location.initialised()) {
254
AP_HAL::panic("Uninitialized location.");
255
}
256
#endif
257
258
if (!state.have_origin && filter_healthy()) {
259
state.origin = state.location;
260
state.have_origin = true;
261
}
262
263
state.last_location_update_us = AP_HAL::micros();
264
}
265
266
// Get model/type name
267
const char* AP_ExternalAHRS_GSOF::get_name() const
268
{
269
return "GSOF";
270
}
271
272
bool AP_ExternalAHRS_GSOF::healthy(void) const
273
{
274
return times_healthy() && filter_healthy();
275
}
276
277
bool AP_ExternalAHRS_GSOF::initialised(void) const
278
{
279
return last_pos_time_ms != 0 && last_ins_full_nav_ms != 0 && last_ins_rms_ms != 0 && last_llh_msl_ms != 0;
280
}
281
282
bool AP_ExternalAHRS_GSOF::pre_arm_check(char *failure_msg, uint8_t failure_msg_len) const
283
{
284
if (!initialised()) {
285
hal.util->snprintf(failure_msg, failure_msg_len, LOG_FMT, get_name(), "not initialised");
286
return false;
287
}
288
if (!times_healthy()) {
289
hal.util->snprintf(failure_msg, failure_msg_len, LOG_FMT, get_name(), "data is stale");
290
return false;
291
}
292
if (!filter_healthy()) {
293
hal.util->snprintf(failure_msg, failure_msg_len, LOG_FMT, get_name(), "filter is unhealthy");
294
return false;
295
}
296
if (!healthy()) {
297
hal.util->snprintf(failure_msg, failure_msg_len, LOG_FMT, get_name(), "unhealthy");
298
return false;
299
}
300
301
return true;
302
}
303
304
void AP_ExternalAHRS_GSOF::get_filter_status(nav_filter_status &status) const
305
{
306
memset(&status, 0, sizeof(status));
307
status.flags.initalized = initialised();
308
}
309
310
bool AP_ExternalAHRS_GSOF::get_variances(float &velVar, float &posVar, float &hgtVar, Vector3f &magVar, float &tasVar) const
311
{
312
velVar = Vector3<float>(
313
ins_rms.vel_rms_n,
314
ins_rms.vel_rms_e,
315
ins_rms.vel_rms_d).length() * vel_gate_scale;
316
posVar = Vector2<float>(
317
ins_rms.pos_rms_n,
318
ins_rms.pos_rms_e).length() * pos_gate_scale;
319
hgtVar = ins_rms.pos_rms_d * hgt_gate_scale;
320
tasVar = 0;
321
return true;
322
}
323
324
bool AP_ExternalAHRS_GSOF::times_healthy() const
325
{
326
auto const now = AP_HAL::millis();
327
328
auto const TIMES_FOS = 2.0;
329
330
// All messages must be at minimum 5Hz to pass the AP_GPS 4hz rate as logged in dataflash GPA.Delta logs.
331
332
// 5Hz = 200mS.
333
auto const GSOF_1_EXPECTED_DELAY_MS = 200;
334
auto const pos_time_healthy = now - last_pos_time_ms <= TIMES_FOS * GSOF_1_EXPECTED_DELAY_MS;
335
336
// 50Hz = 20mS.
337
auto const GSOF_49_EXPECTED_DELAY_MS = 20;
338
auto const ins_full_nav_healthy = now - last_ins_full_nav_ms <= TIMES_FOS * GSOF_49_EXPECTED_DELAY_MS;
339
340
// 5Hz = 200mS.
341
auto const GSOF_50_EXPECTED_DELAY_MS = 200;
342
auto const ins_rms_healthy = now - last_ins_rms_ms < TIMES_FOS * GSOF_50_EXPECTED_DELAY_MS;
343
344
// 5Hz = 200mS.
345
auto const GSOF_70_EXPECTED_DELAY_MS = 200;
346
auto const llh_msl_healthy = now - last_llh_msl_ms < TIMES_FOS * GSOF_70_EXPECTED_DELAY_MS;
347
348
#if CONFIG_HAL_BOARD == HAL_BOARD_SITL
349
if (!pos_time_healthy) {
350
GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "%s: GSOF pos time delayed by %f ms", get_name(), float(now - last_pos_time_ms));
351
}
352
if (!ins_full_nav_healthy) {
353
GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "%s: INS Full nav delayed by %f ms", get_name(), float(now - last_ins_full_nav_ms));
354
}
355
if (!ins_rms_healthy) {
356
GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "%s: INS rms delayed by %f ms", get_name(), float(now - last_ins_rms_ms));
357
}
358
if (!llh_msl_healthy) {
359
GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "%s: LLH MSL delayed by %f ms", get_name(), float(now - last_llh_msl_ms));
360
}
361
#endif // CONFIG_HAL_BOARD == HAL_BOARD_SITL
362
363
return pos_time_healthy && ins_full_nav_healthy && ins_rms_healthy && llh_msl_healthy;
364
}
365
366
bool AP_ExternalAHRS_GSOF::filter_healthy() const
367
{
368
// TODO get the right threshold from Trimble for arming vs in flight.
369
// Fow now, assume an aligned IMU is sufficient for flight.
370
auto const imu_alignment_healthy = (
371
ins_rms.imu_alignment_status == ImuAlignmentStatus::DEGRADED ||
372
ins_rms.imu_alignment_status == ImuAlignmentStatus::ALIGNED ||
373
ins_rms.imu_alignment_status == ImuAlignmentStatus::FULL_NAV
374
);
375
376
auto const gnss_healthy = ins_rms.gnss_status != GnssStatus::FIX_NOT_AVAILABLE;
377
378
// TODO check RMS errors.
379
return imu_alignment_healthy && gnss_healthy;
380
}
381
382
#endif // AP_EXTERNAL_AHRS_GSOF_ENABLED
383
384