[FLYWOOF411] add board documentation
[inav/snaewe.git] / src / main / flight / failsafe.c
blob6ac2783c187111f3b541fb8bf072ae65c71fe707
1 /*
2 * This file is part of Cleanflight.
4 * Cleanflight 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.
9 * Cleanflight 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.
14 * You should have received a copy of the GNU General Public License
15 * along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
18 #include <stdbool.h>
19 #include <stdint.h>
21 #include "platform.h"
23 #include "build/build_config.h"
25 #include "build/debug.h"
27 #include "common/axis.h"
29 #include "config/feature.h"
30 #include "config/parameter_group.h"
31 #include "config/parameter_group_ids.h"
33 #include "drivers/time.h"
35 #include "io/beeper.h"
37 #include "fc/fc_core.h"
38 #include "fc/config.h"
39 #include "fc/rc_controls.h"
40 #include "fc/rc_modes.h"
41 #include "fc/runtime_config.h"
42 #include "fc/controlrate_profile.h"
44 #include "flight/failsafe.h"
45 #include "flight/mixer.h"
46 #include "flight/pid.h"
48 #include "navigation/navigation.h"
50 #include "rx/rx.h"
52 #include "sensors/sensors.h"
55 * Usage:
57 * failsafeInit() and failsafeReset() must be called before the other methods are used.
59 * failsafeInit() and failsafeReset() can be called in any order.
60 * failsafeInit() should only be called once.
62 * enable() should be called after system initialisation.
65 static failsafeState_t failsafeState;
67 PG_REGISTER_WITH_RESET_TEMPLATE(failsafeConfig_t, failsafeConfig, PG_FAILSAFE_CONFIG, 1);
69 PG_RESET_TEMPLATE(failsafeConfig_t, failsafeConfig,
70 .failsafe_delay = 5, // 0.5 sec
71 .failsafe_recovery_delay = 5, // 0.5 seconds (plus 200ms explicit delay)
72 .failsafe_off_delay = 200, // 20sec
73 .failsafe_throttle = 1000, // default throttle off.
74 .failsafe_throttle_low_delay = 0, // default throttle low delay for "just disarm" on failsafe condition
75 .failsafe_procedure = FAILSAFE_PROCEDURE_AUTO_LANDING, // default full failsafe procedure
76 .failsafe_fw_roll_angle = -200, // 20 deg left
77 .failsafe_fw_pitch_angle = 100, // 10 deg dive (yes, positive means dive)
78 .failsafe_fw_yaw_rate = -45, // 45 deg/s left yaw (left is negative, 8s for full turn)
79 .failsafe_stick_motion_threshold = 50,
80 .failsafe_min_distance = 0, // No minimum distance for failsafe by default
81 .failsafe_min_distance_procedure = FAILSAFE_PROCEDURE_DROP_IT, // default minimum distance failsafe procedure
82 .failsafe_mission = true, // Enable failsafe in WP mode or not
85 typedef enum {
86 FAILSAFE_CHANNEL_HOLD, // Hold last known good value
87 FAILSAFE_CHANNEL_NEUTRAL, // RPY = zero, THR = zero
88 FAILSAFE_CHANNEL_AUTO, // Defined by failsafe configured values
89 } failsafeChannelBehavior_e;
91 typedef struct {
92 bool bypassNavigation;
93 bool forceAngleMode;
94 failsafeChannelBehavior_e channelBehavior[4];
95 } failsafeProcedureLogic_t;
97 static const failsafeProcedureLogic_t failsafeProcedureLogic[] = {
98 [FAILSAFE_PROCEDURE_AUTO_LANDING] = {
99 .bypassNavigation = true,
100 .forceAngleMode = true,
101 .channelBehavior = {
102 FAILSAFE_CHANNEL_AUTO, // ROLL
103 FAILSAFE_CHANNEL_AUTO, // PITCH
104 FAILSAFE_CHANNEL_AUTO, // YAW
105 FAILSAFE_CHANNEL_AUTO // THROTTLE
109 [FAILSAFE_PROCEDURE_DROP_IT] = {
110 .bypassNavigation = true,
111 .forceAngleMode = true,
112 .channelBehavior = {
113 FAILSAFE_CHANNEL_NEUTRAL, // ROLL
114 FAILSAFE_CHANNEL_NEUTRAL, // PITCH
115 FAILSAFE_CHANNEL_NEUTRAL, // YAW
116 FAILSAFE_CHANNEL_NEUTRAL // THROTTLE
120 [FAILSAFE_PROCEDURE_RTH] = {
121 .bypassNavigation = false,
122 .forceAngleMode = true,
123 .channelBehavior = {
124 FAILSAFE_CHANNEL_NEUTRAL, // ROLL
125 FAILSAFE_CHANNEL_NEUTRAL, // PITCH
126 FAILSAFE_CHANNEL_NEUTRAL, // YAW
127 FAILSAFE_CHANNEL_HOLD // THROTTLE
131 [FAILSAFE_PROCEDURE_NONE] = {
132 .bypassNavigation = false,
133 .forceAngleMode = false,
134 .channelBehavior = {
135 FAILSAFE_CHANNEL_HOLD, // ROLL
136 FAILSAFE_CHANNEL_HOLD, // PITCH
137 FAILSAFE_CHANNEL_HOLD, // YAW
138 FAILSAFE_CHANNEL_HOLD // THROTTLE
144 * Should called when the failsafe config needs to be changed - e.g. a different profile has been selected.
146 void failsafeReset(void)
148 failsafeState.rxDataFailurePeriod = PERIOD_RXDATA_FAILURE + failsafeConfig()->failsafe_delay * MILLIS_PER_TENTH_SECOND;
149 failsafeState.rxDataRecoveryPeriod = PERIOD_RXDATA_RECOVERY + failsafeConfig()->failsafe_recovery_delay * MILLIS_PER_TENTH_SECOND;
150 failsafeState.validRxDataReceivedAt = 0;
151 failsafeState.validRxDataFailedAt = 0;
152 failsafeState.throttleLowPeriod = 0;
153 failsafeState.landingShouldBeFinishedAt = 0;
154 failsafeState.receivingRxDataPeriod = 0;
155 failsafeState.receivingRxDataPeriodPreset = 0;
156 failsafeState.phase = FAILSAFE_IDLE;
157 failsafeState.rxLinkState = FAILSAFE_RXLINK_DOWN;
158 failsafeState.activeProcedure = failsafeConfig()->failsafe_procedure;
160 failsafeState.lastGoodRcCommand[ROLL] = 0;
161 failsafeState.lastGoodRcCommand[PITCH] = 0;
162 failsafeState.lastGoodRcCommand[YAW] = 0;
163 failsafeState.lastGoodRcCommand[THROTTLE] = 1000;
166 void failsafeInit(void)
168 failsafeState.events = 0;
169 failsafeState.monitoring = false;
170 failsafeState.suspended = false;
173 #ifdef USE_NAV
174 bool failsafeBypassNavigation(void)
176 return failsafeState.active &&
177 failsafeState.controlling &&
178 failsafeProcedureLogic[failsafeState.activeProcedure].bypassNavigation;
181 bool failsafeMayRequireNavigationMode(void)
183 return (failsafeConfig()->failsafe_procedure == FAILSAFE_PROCEDURE_RTH) ||
184 (failsafeConfig()->failsafe_min_distance_procedure == FAILSAFE_PROCEDURE_RTH);
186 #endif
188 failsafePhase_e failsafePhase(void)
190 return failsafeState.phase;
193 bool failsafeIsMonitoring(void)
195 return failsafeState.monitoring;
198 bool failsafeIsActive(void)
200 return failsafeState.active;
203 bool failsafeShouldApplyControlInput(void)
205 return failsafeState.controlling;
208 bool failsafeRequiresAngleMode(void)
210 return failsafeState.active &&
211 failsafeState.controlling &&
212 failsafeProcedureLogic[failsafeState.activeProcedure].forceAngleMode;
215 bool failsafeRequiresMotorStop(void)
217 return failsafeState.active &&
218 failsafeState.activeProcedure == FAILSAFE_PROCEDURE_AUTO_LANDING &&
219 failsafeConfig()->failsafe_throttle < getThrottleIdleValue();
222 void failsafeStartMonitoring(void)
224 failsafeState.monitoring = true;
227 static bool failsafeShouldHaveCausedLandingByNow(void)
229 return failsafeConfig()->failsafe_off_delay && (millis() > failsafeState.landingShouldBeFinishedAt);
232 static void failsafeSetActiveProcedure(failsafeProcedure_e procedure)
234 failsafeState.activeProcedure = procedure;
237 static void failsafeActivate(failsafePhase_e newPhase)
239 failsafeState.active = true;
240 failsafeState.controlling = true;
241 failsafeState.phase = newPhase;
242 ENABLE_FLIGHT_MODE(FAILSAFE_MODE);
243 failsafeState.landingShouldBeFinishedAt = millis() + failsafeConfig()->failsafe_off_delay * MILLIS_PER_TENTH_SECOND;
245 failsafeState.events++;
248 void failsafeUpdateRcCommandValues(void)
250 if (!failsafeState.active) {
251 for (int idx = 0; idx < 4; idx++) {
252 failsafeState.lastGoodRcCommand[idx] = rcCommand[idx];
257 void failsafeApplyControlInput(void)
259 // Prepare FAILSAFE_CHANNEL_AUTO values for rcCommand
260 int16_t autoRcCommand[4];
261 if (STATE(FIXED_WING_LEGACY)) {
262 autoRcCommand[ROLL] = pidAngleToRcCommand(failsafeConfig()->failsafe_fw_roll_angle, pidProfile()->max_angle_inclination[FD_ROLL]);
263 autoRcCommand[PITCH] = pidAngleToRcCommand(failsafeConfig()->failsafe_fw_pitch_angle, pidProfile()->max_angle_inclination[FD_PITCH]);
264 autoRcCommand[YAW] = -pidRateToRcCommand(failsafeConfig()->failsafe_fw_yaw_rate, currentControlRateProfile->stabilized.rates[FD_YAW]);
265 autoRcCommand[THROTTLE] = failsafeConfig()->failsafe_throttle;
267 else {
268 for (int i = 0; i < 3; i++) {
269 autoRcCommand[i] = 0;
271 autoRcCommand[THROTTLE] = failsafeConfig()->failsafe_throttle;
274 // Apply channel values
275 for (int idx = 0; idx < 4; idx++) {
276 switch (failsafeProcedureLogic[failsafeState.activeProcedure].channelBehavior[idx]) {
277 case FAILSAFE_CHANNEL_HOLD:
278 rcCommand[idx] = failsafeState.lastGoodRcCommand[idx];
279 break;
281 case FAILSAFE_CHANNEL_NEUTRAL:
282 switch (idx) {
283 case ROLL:
284 case PITCH:
285 case YAW:
286 rcCommand[idx] = 0;
287 break;
289 case THROTTLE:
290 rcCommand[idx] = feature(FEATURE_REVERSIBLE_MOTORS) ? PWM_RANGE_MIDDLE : getThrottleIdleValue();
291 break;
293 break;
295 case FAILSAFE_CHANNEL_AUTO:
296 rcCommand[idx] = autoRcCommand[idx];
297 break;
302 bool failsafeIsReceivingRxData(void)
304 return (failsafeState.rxLinkState == FAILSAFE_RXLINK_UP);
307 void failsafeOnRxSuspend(void)
309 failsafeState.suspended = true;
312 bool failsafeIsSuspended(void)
314 return failsafeState.suspended;
317 void failsafeOnRxResume(void)
319 failsafeState.suspended = false; // restart monitoring
320 failsafeState.validRxDataReceivedAt = millis(); // prevent RX link down trigger, restart rx link up
321 failsafeState.rxLinkState = FAILSAFE_RXLINK_UP; // do so while rx link is up
324 void failsafeOnValidDataReceived(void)
326 failsafeState.validRxDataReceivedAt = millis();
327 if ((failsafeState.validRxDataReceivedAt - failsafeState.validRxDataFailedAt) > failsafeState.rxDataRecoveryPeriod) {
328 failsafeState.rxLinkState = FAILSAFE_RXLINK_UP;
332 void failsafeOnValidDataFailed(void)
334 failsafeState.validRxDataFailedAt = millis();
335 if ((failsafeState.validRxDataFailedAt - failsafeState.validRxDataReceivedAt) > failsafeState.rxDataFailurePeriod) {
336 failsafeState.rxLinkState = FAILSAFE_RXLINK_DOWN;
340 static bool failsafeCheckStickMotion(void)
342 if (failsafeConfig()->failsafe_stick_motion_threshold > 0) {
343 uint32_t totalRcDelta = 0;
345 totalRcDelta += ABS(rxGetChannelValue(ROLL) - PWM_RANGE_MIDDLE);
346 totalRcDelta += ABS(rxGetChannelValue(PITCH) - PWM_RANGE_MIDDLE);
347 totalRcDelta += ABS(rxGetChannelValue(YAW) - PWM_RANGE_MIDDLE);
349 return totalRcDelta >= failsafeConfig()->failsafe_stick_motion_threshold;
351 else {
352 return true;
356 static failsafeProcedure_e failsafeChooseFailsafeProcedure(void)
358 if (FLIGHT_MODE(NAV_WP_MODE) && !failsafeConfig()->failsafe_mission) {
359 return FAILSAFE_PROCEDURE_NONE;
362 // Craft is closer than minimum failsafe procedure distance (if set to non-zero)
363 // GPS must also be working, and home position set
364 if ((failsafeConfig()->failsafe_min_distance > 0) &&
365 ((GPS_distanceToHome * 100) < failsafeConfig()->failsafe_min_distance) &&
366 sensors(SENSOR_GPS) && STATE(GPS_FIX) && STATE(GPS_FIX_HOME)) {
368 // Use the alternate, minimum distance failsafe procedure instead
369 return failsafeConfig()->failsafe_min_distance_procedure;
372 return failsafeConfig()->failsafe_procedure;
375 void failsafeUpdateState(void)
377 if (!failsafeIsMonitoring() || failsafeIsSuspended()) {
378 return;
381 const bool receivingRxDataAndNotFailsafeMode = failsafeIsReceivingRxData() && !IS_RC_MODE_ACTIVE(BOXFAILSAFE);
382 const bool armed = ARMING_FLAG(ARMED);
383 const bool sticksAreMoving = failsafeCheckStickMotion();
384 beeperMode_e beeperMode = BEEPER_SILENCE;
386 // Beep RX lost only if we are not seeing data and we have been armed earlier
387 if (!receivingRxDataAndNotFailsafeMode && ARMING_FLAG(WAS_EVER_ARMED)) {
388 beeperMode = BEEPER_RX_LOST;
391 bool reprocessState;
393 do {
394 reprocessState = false;
396 switch (failsafeState.phase) {
397 case FAILSAFE_IDLE:
398 if (armed) {
399 // Track throttle command below minimum time
400 if (THROTTLE_HIGH == calculateThrottleStatus(THROTTLE_STATUS_TYPE_RC)) {
401 failsafeState.throttleLowPeriod = millis() + failsafeConfig()->failsafe_throttle_low_delay * MILLIS_PER_TENTH_SECOND;
403 if (!receivingRxDataAndNotFailsafeMode) {
404 if ((failsafeConfig()->failsafe_throttle_low_delay && (millis() > failsafeState.throttleLowPeriod)) || STATE(NAV_MOTOR_STOP_OR_IDLE)) {
405 // JustDisarm: throttle was LOW for at least 'failsafe_throttle_low_delay' seconds or waiting for launch
406 // Don't disarm at all if `failsafe_throttle_low_delay` is set to zero
407 failsafeSetActiveProcedure(FAILSAFE_PROCEDURE_DROP_IT);
408 failsafeActivate(FAILSAFE_LANDED); // skip auto-landing procedure
409 failsafeState.receivingRxDataPeriodPreset = PERIOD_OF_3_SECONDS; // require 3 seconds of valid rxData
410 } else {
411 failsafeState.phase = FAILSAFE_RX_LOSS_DETECTED;
413 reprocessState = true;
415 } else {
416 // When NOT armed, show rxLinkState of failsafe switch in GUI (failsafe mode)
417 if (!receivingRxDataAndNotFailsafeMode) {
418 ENABLE_FLIGHT_MODE(FAILSAFE_MODE);
419 } else {
420 DISABLE_FLIGHT_MODE(FAILSAFE_MODE);
422 // Throttle low period expired (= low long enough for JustDisarm)
423 failsafeState.throttleLowPeriod = 0;
425 break;
427 case FAILSAFE_RX_LOSS_DETECTED:
428 if (receivingRxDataAndNotFailsafeMode) {
429 failsafeState.phase = FAILSAFE_RX_LOSS_RECOVERED;
430 } else {
431 // Set active failsafe procedure
432 failsafeSetActiveProcedure(failsafeChooseFailsafeProcedure());
434 switch (failsafeState.activeProcedure) {
435 case FAILSAFE_PROCEDURE_AUTO_LANDING:
436 // Stabilize, and set Throttle to specified level
437 failsafeActivate(FAILSAFE_LANDING);
438 break;
440 case FAILSAFE_PROCEDURE_DROP_IT:
441 // Drop the craft
442 failsafeActivate(FAILSAFE_LANDED); // skip auto-landing procedure
443 failsafeState.receivingRxDataPeriodPreset = PERIOD_OF_3_SECONDS; // require 3 seconds of valid rxData
444 break;
446 #if defined(USE_NAV)
447 case FAILSAFE_PROCEDURE_RTH:
448 // Proceed to handling & monitoring RTH navigation
449 failsafeActivate(FAILSAFE_RETURN_TO_HOME);
450 activateForcedRTH();
451 break;
452 #endif
453 case FAILSAFE_PROCEDURE_NONE:
454 default:
455 // Do nothing procedure
456 failsafeActivate(FAILSAFE_RX_LOSS_IDLE);
457 break;
460 reprocessState = true;
461 break;
463 /* A very simple do-nothing failsafe procedure. The only thing it will do is monitor the receiver state and switch out of FAILSAFE condition */
464 case FAILSAFE_RX_LOSS_IDLE:
465 if (receivingRxDataAndNotFailsafeMode && sticksAreMoving) {
466 failsafeState.phase = FAILSAFE_RX_LOSS_RECOVERED;
467 reprocessState = true;
469 break;
471 #if defined(USE_NAV)
472 case FAILSAFE_RETURN_TO_HOME:
473 if (receivingRxDataAndNotFailsafeMode && sticksAreMoving) {
474 abortForcedRTH();
475 failsafeState.phase = FAILSAFE_RX_LOSS_RECOVERED;
476 reprocessState = true;
478 else {
479 if (armed) {
480 beeperMode = BEEPER_RX_LOST_LANDING;
482 bool rthLanded = false;
483 switch (getStateOfForcedRTH()) {
484 case RTH_IN_PROGRESS:
485 break;
487 case RTH_HAS_LANDED:
488 rthLanded = true;
489 break;
491 case RTH_IDLE:
492 default:
493 // This shouldn't happen. If RTH was somehow aborted during failsafe - fallback to FAILSAFE_LANDING procedure
494 abortForcedRTH();
495 failsafeSetActiveProcedure(FAILSAFE_PROCEDURE_AUTO_LANDING);
496 failsafeActivate(FAILSAFE_LANDING);
497 reprocessState = true;
498 break;
500 if (rthLanded || !armed) {
501 failsafeState.receivingRxDataPeriodPreset = PERIOD_OF_30_SECONDS; // require 30 seconds of valid rxData
502 failsafeState.phase = FAILSAFE_LANDED;
503 reprocessState = true;
506 break;
507 #endif
509 case FAILSAFE_LANDING:
510 if (receivingRxDataAndNotFailsafeMode && sticksAreMoving) {
511 failsafeState.phase = FAILSAFE_RX_LOSS_RECOVERED;
512 reprocessState = true;
514 if (armed) {
515 beeperMode = BEEPER_RX_LOST_LANDING;
517 if (failsafeShouldHaveCausedLandingByNow() || !armed) {
518 failsafeState.receivingRxDataPeriodPreset = PERIOD_OF_30_SECONDS; // require 30 seconds of valid rxData
519 failsafeState.phase = FAILSAFE_LANDED;
520 reprocessState = true;
522 break;
524 case FAILSAFE_LANDED:
525 ENABLE_ARMING_FLAG(ARMING_DISABLED_FAILSAFE_SYSTEM); // To prevent accidently rearming by an intermittent rx link
526 disarm(DISARM_FAILSAFE);
527 failsafeState.receivingRxDataPeriod = millis() + failsafeState.receivingRxDataPeriodPreset; // set required period of valid rxData
528 failsafeState.phase = FAILSAFE_RX_LOSS_MONITORING;
529 failsafeState.controlling = false; // Failsafe no longer in control of the machine - release control to pilot
530 reprocessState = true;
531 break;
533 case FAILSAFE_RX_LOSS_MONITORING:
534 // Monitoring the rx link to allow rearming when it has become good for > `receivingRxDataPeriodPreset` time.
535 if (receivingRxDataAndNotFailsafeMode) {
536 if (millis() > failsafeState.receivingRxDataPeriod) {
537 // rx link is good now, when arming via ARM switch, it must be OFF first
538 if (!IS_RC_MODE_ACTIVE(BOXARM)) {
539 // XXX: Requirements for removing the ARMING_DISABLED_FAILSAFE_SYSTEM flag
540 // are tested by osd.c to show the user how to re-arm. If these
541 // requirements change, update osdArmingDisabledReasonMessage().
542 DISABLE_ARMING_FLAG(ARMING_DISABLED_FAILSAFE_SYSTEM);
543 failsafeState.phase = FAILSAFE_RX_LOSS_RECOVERED;
544 reprocessState = true;
547 } else {
548 failsafeState.receivingRxDataPeriod = millis() + failsafeState.receivingRxDataPeriodPreset;
550 break;
552 case FAILSAFE_RX_LOSS_RECOVERED:
553 // Entering IDLE with the requirement that throttle first must be at min_check for failsafe_throttle_low_delay period.
554 // This is to prevent that JustDisarm is activated on the next iteration.
555 // Because that would have the effect of shutting down failsafe handling on intermittent connections.
556 failsafeState.throttleLowPeriod = millis() + failsafeConfig()->failsafe_throttle_low_delay * MILLIS_PER_TENTH_SECOND;
557 failsafeState.phase = FAILSAFE_IDLE;
558 failsafeState.active = false;
559 failsafeState.controlling = false;
560 DISABLE_FLIGHT_MODE(FAILSAFE_MODE);
561 reprocessState = true;
562 break;
564 default:
565 break;
567 } while (reprocessState);
569 if (beeperMode != BEEPER_SILENCE) {
570 beeper(beeperMode);