forked from ArduPilot/ardupilot
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathevents.cpp
487 lines (421 loc) · 18.5 KB
/
events.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
#include "Copter.h"
/*
* This event will be called when the failsafe changes
* boolean failsafe reflects the current state
*/
bool Copter::failsafe_option(FailsafeOption opt) const
{
return (g2.fs_options & (uint32_t)opt);
}
void Copter::failsafe_radio_on_event()
{
AP::logger().Write_Error(LogErrorSubsystem::FAILSAFE_RADIO, LogErrorCode::FAILSAFE_OCCURRED);
// set desired action based on FS_THR_ENABLE parameter
FailsafeAction desired_action;
switch (g.failsafe_throttle) {
case FS_THR_DISABLED:
desired_action = FailsafeAction::NONE;
break;
case FS_THR_ENABLED_ALWAYS_RTL:
case FS_THR_ENABLED_CONTINUE_MISSION:
desired_action = FailsafeAction::RTL;
break;
case FS_THR_ENABLED_ALWAYS_SMARTRTL_OR_RTL:
desired_action = FailsafeAction::SMARTRTL;
break;
case FS_THR_ENABLED_ALWAYS_SMARTRTL_OR_LAND:
desired_action = FailsafeAction::SMARTRTL_LAND;
break;
case FS_THR_ENABLED_ALWAYS_LAND:
desired_action = FailsafeAction::LAND;
break;
case FS_THR_ENABLED_AUTO_RTL_OR_RTL:
desired_action = FailsafeAction::AUTO_DO_LAND_START;
break;
default:
desired_action = FailsafeAction::LAND;
}
// Conditions to deviate from FS_THR_ENABLE selection and send specific GCS warning
if (should_disarm_on_failsafe()) {
// should immediately disarm when we're on the ground
announce_failsafe("Radio", "Disarming");
arming.disarm(AP_Arming::Method::RADIOFAILSAFE);
desired_action = FailsafeAction::NONE;
} else if (flightmode->is_landing() && ((battery.has_failsafed() && battery.get_highest_failsafe_priority() <= FAILSAFE_LAND_PRIORITY))) {
// Allow landing to continue when battery failsafe requires it (not a user option)
announce_failsafe("Radio + Battery", "Continuing Landing");
desired_action = FailsafeAction::LAND;
} else if (flightmode->is_landing() && failsafe_option(FailsafeOption::CONTINUE_IF_LANDING)) {
// Allow landing to continue when FS_OPTIONS is set to continue landing
announce_failsafe("Radio", "Continuing Landing");
desired_action = FailsafeAction::LAND;
} else if (flightmode->mode_number() == Mode::Number::AUTO && failsafe_option(FailsafeOption::RC_CONTINUE_IF_AUTO)) {
// Allow mission to continue when FS_OPTIONS is set to continue mission
announce_failsafe("Radio", "Continuing Auto");
desired_action = FailsafeAction::NONE;
} else if ((flightmode->in_guided_mode()) && failsafe_option(FailsafeOption::RC_CONTINUE_IF_GUIDED)) {
// Allow guided mode to continue when FS_OPTIONS is set to continue in guided mode
announce_failsafe("Radio", "Continuing Guided Mode");
desired_action = FailsafeAction::NONE;
} else {
announce_failsafe("Radio");
}
// Call the failsafe action handler
do_failsafe_action(desired_action, ModeReason::RADIO_FAILSAFE);
}
// failsafe_off_event - respond to radio contact being regained
void Copter::failsafe_radio_off_event()
{
// no need to do anything except log the error as resolved
// user can now override roll, pitch, yaw and throttle and even use flight mode switch to restore previous flight mode
AP::logger().Write_Error(LogErrorSubsystem::FAILSAFE_RADIO, LogErrorCode::FAILSAFE_RESOLVED);
gcs().send_text(MAV_SEVERITY_WARNING, "Radio Failsafe Cleared");
}
void Copter::announce_failsafe(const char *type, const char *action_undertaken)
{
if (action_undertaken != nullptr) {
gcs().send_text(MAV_SEVERITY_WARNING, "%s Failsafe - %s", type, action_undertaken);
} else {
gcs().send_text(MAV_SEVERITY_WARNING, "%s Failsafe", type);
}
}
void Copter::handle_battery_failsafe(const char *type_str, const int8_t action)
{
AP::logger().Write_Error(LogErrorSubsystem::FAILSAFE_BATT, LogErrorCode::FAILSAFE_OCCURRED);
FailsafeAction desired_action = (FailsafeAction)action;
// Conditions to deviate from BATT_FS_XXX_ACT parameter setting
if (should_disarm_on_failsafe()) {
// should immediately disarm when we're on the ground
arming.disarm(AP_Arming::Method::BATTERYFAILSAFE);
desired_action = FailsafeAction::NONE;
announce_failsafe("Battery", "Disarming");
} else if (flightmode->is_landing() && failsafe_option(FailsafeOption::CONTINUE_IF_LANDING) && desired_action != FailsafeAction::NONE) {
// Allow landing to continue when FS_OPTIONS is set to continue when landing
desired_action = FailsafeAction::LAND;
announce_failsafe("Battery", "Continuing Landing");
} else {
announce_failsafe("Battery");
}
// Battery FS options already use the Failsafe_Options enum. So use them directly.
do_failsafe_action(desired_action, ModeReason::BATTERY_FAILSAFE);
}
// failsafe_gcs_check - check for ground station failsafe
void Copter::failsafe_gcs_check()
{
// Bypass GCS failsafe checks if disabled or GCS never connected
if (g.failsafe_gcs == FS_GCS_DISABLED) {
return;
}
const uint32_t gcs_last_seen_ms = gcs().sysid_myggcs_last_seen_time_ms();
if (gcs_last_seen_ms == 0) {
return;
}
// calc time since last gcs update
// note: this only looks at the heartbeat from the device id set by g.sysid_my_gcs
const uint32_t last_gcs_update_ms = millis() - gcs_last_seen_ms;
const uint32_t gcs_timeout_ms = uint32_t(constrain_float(g2.fs_gcs_timeout * 1000.0f, 0.0f, UINT32_MAX));
// Determine which event to trigger
if (last_gcs_update_ms < gcs_timeout_ms && failsafe.gcs) {
// Recovery from a GCS failsafe
set_failsafe_gcs(false);
failsafe_gcs_off_event();
} else if (last_gcs_update_ms < gcs_timeout_ms && !failsafe.gcs) {
// No problem, do nothing
} else if (last_gcs_update_ms > gcs_timeout_ms && failsafe.gcs) {
// Already in failsafe, do nothing
} else if (last_gcs_update_ms > gcs_timeout_ms && !failsafe.gcs) {
// New GCS failsafe event, trigger events
set_failsafe_gcs(true);
failsafe_gcs_on_event();
}
}
// failsafe_gcs_on_event - actions to take when GCS contact is lost
void Copter::failsafe_gcs_on_event(void)
{
AP::logger().Write_Error(LogErrorSubsystem::FAILSAFE_GCS, LogErrorCode::FAILSAFE_OCCURRED);
RC_Channels::clear_overrides();
// convert the desired failsafe response to the FailsafeAction enum
FailsafeAction desired_action;
switch (g.failsafe_gcs) {
case FS_GCS_DISABLED:
desired_action = FailsafeAction::NONE;
break;
case FS_GCS_ENABLED_ALWAYS_RTL:
case FS_GCS_ENABLED_CONTINUE_MISSION:
desired_action = FailsafeAction::RTL;
break;
case FS_GCS_ENABLED_ALWAYS_SMARTRTL_OR_RTL:
desired_action = FailsafeAction::SMARTRTL;
break;
case FS_GCS_ENABLED_ALWAYS_SMARTRTL_OR_LAND:
desired_action = FailsafeAction::SMARTRTL_LAND;
break;
case FS_GCS_ENABLED_ALWAYS_LAND:
desired_action = FailsafeAction::LAND;
break;
case FS_GCS_ENABLED_AUTO_RTL_OR_RTL:
desired_action = FailsafeAction::AUTO_DO_LAND_START;
break;
default: // if an invalid parameter value is set, the fallback is RTL
desired_action = FailsafeAction::RTL;
}
// Conditions to deviate from FS_GCS_ENABLE parameter setting
if (!motors->armed()) {
desired_action = FailsafeAction::NONE;
announce_failsafe("GCS");
} else if (should_disarm_on_failsafe()) {
// should immediately disarm when we're on the ground
arming.disarm(AP_Arming::Method::GCSFAILSAFE);
desired_action = FailsafeAction::NONE;
announce_failsafe("GCS", "Disarming");
} else if (flightmode->is_landing() && ((battery.has_failsafed() && battery.get_highest_failsafe_priority() <= FAILSAFE_LAND_PRIORITY))) {
// Allow landing to continue when battery failsafe requires it (not a user option)
announce_failsafe("GCS + Battery", "Continuing Landing");
desired_action = FailsafeAction::LAND;
} else if (flightmode->is_landing() && failsafe_option(FailsafeOption::CONTINUE_IF_LANDING)) {
// Allow landing to continue when FS_OPTIONS is set to continue landing
announce_failsafe("GCS", "Continuing Landing");
desired_action = FailsafeAction::LAND;
} else if (flightmode->mode_number() == Mode::Number::AUTO && failsafe_option(FailsafeOption::GCS_CONTINUE_IF_AUTO)) {
// Allow mission to continue when FS_OPTIONS is set to continue mission
announce_failsafe("GCS", "Continuing Auto Mode");
desired_action = FailsafeAction::NONE;
} else if (failsafe_option(FailsafeOption::GCS_CONTINUE_IF_PILOT_CONTROL) && !flightmode->is_autopilot()) {
// should continue when in a pilot controlled mode because FS_OPTIONS is set to continue in pilot controlled modes
announce_failsafe("GCS", "Continuing Pilot Control");
desired_action = FailsafeAction::NONE;
} else {
announce_failsafe("GCS");
}
// Call the failsafe action handler
do_failsafe_action(desired_action, ModeReason::GCS_FAILSAFE);
}
// failsafe_gcs_off_event - actions to take when GCS contact is restored
void Copter::failsafe_gcs_off_event(void)
{
gcs().send_text(MAV_SEVERITY_WARNING, "GCS Failsafe Cleared");
AP::logger().Write_Error(LogErrorSubsystem::FAILSAFE_GCS, LogErrorCode::FAILSAFE_RESOLVED);
}
// executes terrain failsafe if data is missing for longer than a few seconds
void Copter::failsafe_terrain_check()
{
// trigger within <n> milliseconds of failures while in various modes
bool timeout = (failsafe.terrain_last_failure_ms - failsafe.terrain_first_failure_ms) > FS_TERRAIN_TIMEOUT_MS;
bool trigger_event = timeout && flightmode->requires_terrain_failsafe();
// check for clearing of event
if (trigger_event != failsafe.terrain) {
if (trigger_event) {
failsafe_terrain_on_event();
} else {
AP::logger().Write_Error(LogErrorSubsystem::FAILSAFE_TERRAIN, LogErrorCode::ERROR_RESOLVED);
failsafe.terrain = false;
}
}
}
// set terrain data status (found or not found)
void Copter::failsafe_terrain_set_status(bool data_ok)
{
uint32_t now = millis();
// record time of first and latest failures (i.e. duration of failures)
if (!data_ok) {
failsafe.terrain_last_failure_ms = now;
if (failsafe.terrain_first_failure_ms == 0) {
failsafe.terrain_first_failure_ms = now;
}
} else {
// failures cleared after 0.1 seconds of persistent successes
if (now - failsafe.terrain_last_failure_ms > 100) {
failsafe.terrain_last_failure_ms = 0;
failsafe.terrain_first_failure_ms = 0;
}
}
}
// terrain failsafe action
void Copter::failsafe_terrain_on_event()
{
failsafe.terrain = true;
gcs().send_text(MAV_SEVERITY_CRITICAL,"Failsafe: Terrain data missing");
AP::logger().Write_Error(LogErrorSubsystem::FAILSAFE_TERRAIN, LogErrorCode::FAILSAFE_OCCURRED);
if (should_disarm_on_failsafe()) {
arming.disarm(AP_Arming::Method::TERRAINFAILSAFE);
#if MODE_RTL_ENABLED == ENABLED
} else if (flightmode->mode_number() == Mode::Number::RTL) {
mode_rtl.restart_without_terrain();
#endif
} else {
set_mode_RTL_or_land_with_pause(ModeReason::TERRAIN_FAILSAFE);
}
}
// check for gps glitch failsafe
void Copter::gpsglitch_check()
{
// get filter status
nav_filter_status filt_status = inertial_nav.get_filter_status();
bool gps_glitching = filt_status.flags.gps_glitching;
// log start or stop of gps glitch. AP_Notify update is handled from within AP_AHRS
if (ap.gps_glitching != gps_glitching) {
ap.gps_glitching = gps_glitching;
if (gps_glitching) {
AP::logger().Write_Error(LogErrorSubsystem::GPS, LogErrorCode::GPS_GLITCH);
gcs().send_text(MAV_SEVERITY_CRITICAL,"GPS Glitch or Compass error");
} else {
AP::logger().Write_Error(LogErrorSubsystem::GPS, LogErrorCode::ERROR_RESOLVED);
gcs().send_text(MAV_SEVERITY_CRITICAL,"Glitch cleared");
}
}
}
// dead reckoning alert and failsafe
void Copter::failsafe_deadreckon_check()
{
// update dead reckoning state
const char* dr_prefix_str = "Dead Reckoning";
// get EKF filter status
bool ekf_dead_reckoning = inertial_nav.get_filter_status().flags.dead_reckoning;
// alert user to start or stop of dead reckoning
const uint32_t now_ms = AP_HAL::millis();
if (dead_reckoning.active != ekf_dead_reckoning) {
dead_reckoning.active = ekf_dead_reckoning;
if (dead_reckoning.active) {
dead_reckoning.start_ms = now_ms;
gcs().send_text(MAV_SEVERITY_CRITICAL,"%s started", dr_prefix_str);
} else {
dead_reckoning.start_ms = 0;
dead_reckoning.timeout = false;
gcs().send_text(MAV_SEVERITY_CRITICAL,"%s stopped", dr_prefix_str);
}
}
// check for timeout
if (dead_reckoning.active && !dead_reckoning.timeout) {
const uint32_t dr_timeout_ms = uint32_t(constrain_float(g2.failsafe_dr_timeout * 1000.0f, 0.0f, UINT32_MAX));
if (now_ms - dead_reckoning.start_ms > dr_timeout_ms) {
dead_reckoning.timeout = true;
gcs().send_text(MAV_SEVERITY_CRITICAL,"%s timeout", dr_prefix_str);
}
}
// exit immediately if deadreckon failsafe is disabled
if (g2.failsafe_dr_enable <= 0) {
failsafe.deadreckon = false;
return;
}
// check for failsafe action
if (failsafe.deadreckon != ekf_dead_reckoning) {
failsafe.deadreckon = ekf_dead_reckoning;
// only take action in modes requiring position estimate
if (failsafe.deadreckon && copter.flightmode->requires_GPS()) {
// log error
AP::logger().Write_Error(LogErrorSubsystem::FAILSAFE_DEADRECKON, LogErrorCode::FAILSAFE_OCCURRED);
// immediately disarm while landed
if (should_disarm_on_failsafe()) {
arming.disarm(AP_Arming::Method::DEADRECKON_FAILSAFE);
return;
}
// take user specified action
do_failsafe_action((FailsafeAction)g2.failsafe_dr_enable.get(), ModeReason::DEADRECKON_FAILSAFE);
}
}
}
// set_mode_RTL_or_land_with_pause - sets mode to RTL if possible or LAND with 4 second delay before descent starts
// this is always called from a failsafe so we trigger notification to pilot
void Copter::set_mode_RTL_or_land_with_pause(ModeReason reason)
{
// attempt to switch to RTL, if this fails then switch to Land
if (!set_mode(Mode::Number::RTL, reason)) {
// set mode to land will trigger mode change notification to pilot
set_mode_land_with_pause(reason);
} else {
// alert pilot to mode change
AP_Notify::events.failsafe_mode_change = 1;
}
}
// set_mode_SmartRTL_or_land_with_pause - sets mode to SMART_RTL if possible or LAND with 4 second delay before descent starts
// this is always called from a failsafe so we trigger notification to pilot
void Copter::set_mode_SmartRTL_or_land_with_pause(ModeReason reason)
{
// attempt to switch to SMART_RTL, if this failed then switch to Land
if (!set_mode(Mode::Number::SMART_RTL, reason)) {
gcs().send_text(MAV_SEVERITY_WARNING, "SmartRTL Unavailable, Using Land Mode");
set_mode_land_with_pause(reason);
} else {
AP_Notify::events.failsafe_mode_change = 1;
}
}
// set_mode_SmartRTL_or_RTL - sets mode to SMART_RTL if possible or RTL if possible or LAND with 4 second delay before descent starts
// this is always called from a failsafe so we trigger notification to pilot
void Copter::set_mode_SmartRTL_or_RTL(ModeReason reason)
{
// attempt to switch to SmartRTL, if this failed then attempt to RTL
// if that fails, then land
if (!set_mode(Mode::Number::SMART_RTL, reason)) {
gcs().send_text(MAV_SEVERITY_WARNING, "SmartRTL Unavailable, Trying RTL Mode");
set_mode_RTL_or_land_with_pause(reason);
} else {
AP_Notify::events.failsafe_mode_change = 1;
}
}
// Sets mode to Auto and jumps to DO_LAND_START, as set with AUTO_RTL param
// This can come from failsafe or RC option
void Copter::set_mode_auto_do_land_start_or_RTL(ModeReason reason)
{
#if MODE_AUTO_ENABLED == ENABLED
if (set_mode(Mode::Number::AUTO_RTL, reason)) {
AP_Notify::events.failsafe_mode_change = 1;
return;
}
#endif
gcs().send_text(MAV_SEVERITY_WARNING, "Trying RTL Mode");
set_mode_RTL_or_land_with_pause(reason);
}
bool Copter::should_disarm_on_failsafe() {
if (ap.in_arming_delay) {
return true;
}
switch (flightmode->mode_number()) {
case Mode::Number::STABILIZE:
case Mode::Number::ACRO:
// if throttle is zero OR vehicle is landed disarm motors
return ap.throttle_zero || ap.land_complete;
case Mode::Number::AUTO:
case Mode::Number::AUTO_RTL:
// if mission has not started AND vehicle is landed, disarm motors
return !ap.auto_armed && ap.land_complete;
default:
// used for AltHold, Guided, Loiter, RTL, Circle, Drift, Sport, Flip, Autotune, PosHold
// if landed disarm
return ap.land_complete;
}
}
void Copter::do_failsafe_action(FailsafeAction action, ModeReason reason){
// Execute the specified desired_action
switch (action) {
case FailsafeAction::NONE:
return;
case FailsafeAction::LAND:
set_mode_land_with_pause(reason);
break;
case FailsafeAction::RTL:
set_mode_RTL_or_land_with_pause(reason);
break;
case FailsafeAction::SMARTRTL:
set_mode_SmartRTL_or_RTL(reason);
break;
case FailsafeAction::SMARTRTL_LAND:
set_mode_SmartRTL_or_land_with_pause(reason);
break;
case FailsafeAction::TERMINATE: {
#if ADVANCED_FAILSAFE == ENABLED
g2.afs.gcs_terminate(true, "Failsafe");
#else
arming.disarm(AP_Arming::Method::FAILSAFE_ACTION_TERMINATE);
#endif
break;
}
case FailsafeAction::AUTO_DO_LAND_START:
set_mode_auto_do_land_start_or_RTL(reason);
break;
}
#if GRIPPER_ENABLED == ENABLED
if (failsafe_option(FailsafeOption::RELEASE_GRIPPER)) {
copter.g2.gripper.release();
}
#endif
}