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/>.
25 #include "build/debug.h"
27 #include "common/axis.h"
28 #include "common/filter.h"
29 #include "common/maths.h"
30 #include "common/utils.h"
32 #include "config/parameter_group.h"
33 #include "config/parameter_group_ids.h"
35 #include "drivers/time.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 #ifdef USE_MULTI_MISSION
45 #include "fc/settings.h"
47 #include "flight/imu.h"
48 #include "flight/mixer.h"
49 #include "flight/pid.h"
51 #include "io/beeper.h"
54 #include "navigation/navigation.h"
55 #include "navigation/navigation_private.h"
59 #include "sensors/sensors.h"
60 #include "sensors/acceleration.h"
61 #include "sensors/boardalignment.h"
65 #define MR_RTH_CLIMB_OVERSHOOT_CM 100 // target this amount of cm *above* the target altitude to ensure it is actually reached (Vz > 0 at target alt)
66 #define MR_RTH_CLIMB_MARGIN_MIN_CM 100 // start cruising home this amount of cm *before* reaching the cruise altitude (while continuing the ascend)
67 #define MR_RTH_CLIMB_MARGIN_PERCENT 15 // on high RTH altitudes use even bigger margin - percent of the altitude set
69 #define FW_RTH_CLIMB_OVERSHOOT_CM 100
70 #define FW_RTH_CLIMB_MARGIN_MIN_CM 100
71 #define FW_RTH_CLIMB_MARGIN_PERCENT 15
73 /*-----------------------------------------------------------
74 * Compatibility for home position
75 *-----------------------------------------------------------*/
76 gpsLocation_t GPS_home
;
77 uint32_t GPS_distanceToHome
; // distance to home point in meters
78 int16_t GPS_directionToHome
; // direction to home point in degrees
80 fpVector3_t original_rth_home
; // the original rth home - save it, since it could be replaced by safehome or HOME_RESET
82 radar_pois_t radar_pois
[RADAR_MAX_POIS
];
83 #if defined(USE_SAFE_HOME)
84 int8_t safehome_index
= -1; // -1 if no safehome, 0 to MAX_SAFEHOMES -1 otherwise
85 uint32_t safehome_distance
= 0; // distance to the nearest safehome
86 fpVector3_t nearestSafeHome
; // The nearestSafeHome found during arming
87 bool safehome_applied
= false; // whether the safehome has been applied to home.
89 PG_REGISTER_ARRAY(navSafeHome_t
, MAX_SAFE_HOMES
, safeHomeConfig
, PG_SAFE_HOME_CONFIG
, 0);
93 // waypoint 254, 255 are special waypoints
94 STATIC_ASSERT(NAV_MAX_WAYPOINTS
< 254, NAV_MAX_WAYPOINTS_exceeded_allowable_range
);
96 #if defined(NAV_NON_VOLATILE_WAYPOINT_STORAGE)
97 PG_REGISTER_ARRAY(navWaypoint_t
, NAV_MAX_WAYPOINTS
, nonVolatileWaypointList
, PG_WAYPOINT_MISSION_STORAGE
, 1);
100 PG_REGISTER_WITH_RESET_TEMPLATE(navConfig_t
, navConfig
, PG_NAV_CONFIG
, 14);
102 PG_RESET_TEMPLATE(navConfig_t
, navConfig
,
106 .use_thr_mid_for_althold
= SETTING_NAV_USE_MIDTHR_FOR_ALTHOLD_DEFAULT
,
107 .extra_arming_safety
= SETTING_NAV_EXTRA_ARMING_SAFETY_DEFAULT
,
108 .user_control_mode
= SETTING_NAV_USER_CONTROL_MODE_DEFAULT
,
109 .rth_alt_control_mode
= SETTING_NAV_RTH_ALT_MODE_DEFAULT
,
110 .rth_climb_first
= SETTING_NAV_RTH_CLIMB_FIRST_DEFAULT
, // Climb first, turn after reaching safe altitude
111 .rth_climb_first_stage_mode
= SETTING_NAV_RTH_CLIMB_FIRST_STAGE_MODE_DEFAULT
, // To determine how rth_climb_first_stage_altitude is used
112 .rth_climb_ignore_emerg
= SETTING_NAV_RTH_CLIMB_IGNORE_EMERG_DEFAULT
, // Ignore GPS loss on initial climb
113 .rth_tail_first
= SETTING_NAV_RTH_TAIL_FIRST_DEFAULT
,
114 .disarm_on_landing
= SETTING_NAV_DISARM_ON_LANDING_DEFAULT
,
115 .rth_allow_landing
= SETTING_NAV_RTH_ALLOW_LANDING_DEFAULT
,
116 .rth_alt_control_override
= SETTING_NAV_RTH_ALT_CONTROL_OVERRIDE_DEFAULT
, // Override RTH Altitude and Climb First using Pitch and Roll stick
117 .nav_overrides_motor_stop
= SETTING_NAV_OVERRIDES_MOTOR_STOP_DEFAULT
,
118 .safehome_usage_mode
= SETTING_SAFEHOME_USAGE_MODE_DEFAULT
,
119 .mission_planner_reset
= SETTING_NAV_MISSION_PLANNER_RESET_DEFAULT
, // Allow mode switch toggle to reset Mission Planner WPs
120 .waypoint_mission_restart
= SETTING_NAV_WP_MISSION_RESTART_DEFAULT
, // WP mission restart action
121 .soaring_motor_stop
= SETTING_NAV_FW_SOARING_MOTOR_STOP_DEFAULT
, // stops motor when Saoring mode enabled
124 // General navigation parameters
125 .pos_failure_timeout
= SETTING_NAV_POSITION_TIMEOUT_DEFAULT
, // 5 sec
126 .waypoint_radius
= SETTING_NAV_WP_RADIUS_DEFAULT
, // 2m diameter
127 .waypoint_safe_distance
= SETTING_NAV_WP_SAFE_DISTANCE_DEFAULT
, // centimeters - first waypoint should be closer than this
128 #ifdef USE_MULTI_MISSION
129 .waypoint_multi_mission_index
= SETTING_NAV_WP_MULTI_MISSION_INDEX_DEFAULT
, // mission index selected from multi mission WP entry
131 .waypoint_load_on_boot
= SETTING_NAV_WP_LOAD_ON_BOOT_DEFAULT
, // load waypoints automatically during boot
132 .auto_speed
= SETTING_NAV_AUTO_SPEED_DEFAULT
, // speed in autonomous modes (3 m/s = 10.8 km/h)
133 .max_auto_speed
= SETTING_NAV_MAX_AUTO_SPEED_DEFAULT
, // max allowed speed autonomous modes
134 .max_auto_climb_rate
= SETTING_NAV_AUTO_CLIMB_RATE_DEFAULT
, // 5 m/s
135 .max_manual_speed
= SETTING_NAV_MANUAL_SPEED_DEFAULT
,
136 .max_manual_climb_rate
= SETTING_NAV_MANUAL_CLIMB_RATE_DEFAULT
,
137 .land_slowdown_minalt
= SETTING_NAV_LAND_SLOWDOWN_MINALT_DEFAULT
, // altitude in centimeters
138 .land_slowdown_maxalt
= SETTING_NAV_LAND_SLOWDOWN_MAXALT_DEFAULT
, // altitude in meters
139 .land_minalt_vspd
= SETTING_NAV_LAND_MINALT_VSPD_DEFAULT
, // centimeters/s
140 .land_maxalt_vspd
= SETTING_NAV_LAND_MAXALT_VSPD_DEFAULT
, // centimeters/s
141 .emerg_descent_rate
= SETTING_NAV_EMERG_LANDING_SPEED_DEFAULT
, // centimeters/s
142 .min_rth_distance
= SETTING_NAV_MIN_RTH_DISTANCE_DEFAULT
, // centimeters, if closer than this land immediately
143 .rth_altitude
= SETTING_NAV_RTH_ALTITUDE_DEFAULT
, // altitude in centimeters
144 .rth_home_altitude
= SETTING_NAV_RTH_HOME_ALTITUDE_DEFAULT
, // altitude in centimeters
145 .rth_climb_first_stage_altitude
= SETTING_NAV_RTH_CLIMB_FIRST_STAGE_ALTITUDE_DEFAULT
, // altitude in centimetres, 0= off
146 .rth_abort_threshold
= SETTING_NAV_RTH_ABORT_THRESHOLD_DEFAULT
, // centimeters - 500m should be safe for all aircraft
147 .max_terrain_follow_altitude
= SETTING_NAV_MAX_TERRAIN_FOLLOW_ALT_DEFAULT
, // max altitude in centimeters in terrain following mode
148 .safehome_max_distance
= SETTING_SAFEHOME_MAX_DISTANCE_DEFAULT
, // Max distance that a safehome is from the arming point
149 .max_altitude
= SETTING_NAV_MAX_ALTITUDE_DEFAULT
,
154 .max_bank_angle
= SETTING_NAV_MC_BANK_ANGLE_DEFAULT
, // degrees
155 .auto_disarm_delay
= SETTING_NAV_MC_AUTO_DISARM_DELAY_DEFAULT
, // milliseconds - time before disarming when auto disarm is enabled and landing is confirmed
157 #ifdef USE_MR_BRAKING_MODE
158 .braking_speed_threshold
= SETTING_NAV_MC_BRAKING_SPEED_THRESHOLD_DEFAULT
, // Braking can become active above 1m/s
159 .braking_disengage_speed
= SETTING_NAV_MC_BRAKING_DISENGAGE_SPEED_DEFAULT
, // Stop when speed goes below 0.75m/s
160 .braking_timeout
= SETTING_NAV_MC_BRAKING_TIMEOUT_DEFAULT
, // Timeout barking after 2s
161 .braking_boost_factor
= SETTING_NAV_MC_BRAKING_BOOST_FACTOR_DEFAULT
, // A 100% boost by default
162 .braking_boost_timeout
= SETTING_NAV_MC_BRAKING_BOOST_TIMEOUT_DEFAULT
, // Timout boost after 750ms
163 .braking_boost_speed_threshold
= SETTING_NAV_MC_BRAKING_BOOST_SPEED_THRESHOLD_DEFAULT
, // Boost can happen only above 1.5m/s
164 .braking_boost_disengage_speed
= SETTING_NAV_MC_BRAKING_BOOST_DISENGAGE_SPEED_DEFAULT
, // Disable boost at 1m/s
165 .braking_bank_angle
= SETTING_NAV_MC_BRAKING_BANK_ANGLE_DEFAULT
, // Max braking angle
168 .posDecelerationTime
= SETTING_NAV_MC_POS_DECELERATION_TIME_DEFAULT
, // posDecelerationTime * 100
169 .posResponseExpo
= SETTING_NAV_MC_POS_EXPO_DEFAULT
, // posResponseExpo * 100
170 .slowDownForTurning
= SETTING_NAV_MC_WP_SLOWDOWN_DEFAULT
,
175 .max_bank_angle
= SETTING_NAV_FW_BANK_ANGLE_DEFAULT
, // degrees
176 .max_climb_angle
= SETTING_NAV_FW_CLIMB_ANGLE_DEFAULT
, // degrees
177 .max_dive_angle
= SETTING_NAV_FW_DIVE_ANGLE_DEFAULT
, // degrees
178 .cruise_speed
= SETTING_NAV_FW_CRUISE_SPEED_DEFAULT
, // cm/s
179 .control_smoothness
= SETTING_NAV_FW_CONTROL_SMOOTHNESS_DEFAULT
,
180 .pitch_to_throttle_smooth
= SETTING_NAV_FW_PITCH2THR_SMOOTHING_DEFAULT
,
181 .pitch_to_throttle_thresh
= SETTING_NAV_FW_PITCH2THR_THRESHOLD_DEFAULT
,
182 .loiter_radius
= SETTING_NAV_FW_LOITER_RADIUS_DEFAULT
, // 75m
185 .land_dive_angle
= SETTING_NAV_FW_LAND_DIVE_ANGLE_DEFAULT
, // 2 degrees dive by default
188 .launch_velocity_thresh
= SETTING_NAV_FW_LAUNCH_VELOCITY_DEFAULT
, // 3 m/s
189 .launch_accel_thresh
= SETTING_NAV_FW_LAUNCH_ACCEL_DEFAULT
, // cm/s/s (1.9*G)
190 .launch_time_thresh
= SETTING_NAV_FW_LAUNCH_DETECT_TIME_DEFAULT
, // 40ms
191 .launch_motor_timer
= SETTING_NAV_FW_LAUNCH_MOTOR_DELAY_DEFAULT
, // ms
192 .launch_idle_motor_timer
= SETTING_NAV_FW_LAUNCH_IDLE_MOTOR_DELAY_DEFAULT
, // ms
193 .launch_motor_spinup_time
= SETTING_NAV_FW_LAUNCH_SPINUP_TIME_DEFAULT
, // ms, time to gredually increase throttle from idle to launch
194 .launch_end_time
= SETTING_NAV_FW_LAUNCH_END_TIME_DEFAULT
, // ms, time to gradually decrease/increase throttle and decrease pitch angle from launch to the current flight mode
195 .launch_min_time
= SETTING_NAV_FW_LAUNCH_MIN_TIME_DEFAULT
, // ms, min time in launch mode
196 .launch_timeout
= SETTING_NAV_FW_LAUNCH_TIMEOUT_DEFAULT
, // ms, timeout for launch procedure
197 .launch_max_altitude
= SETTING_NAV_FW_LAUNCH_MAX_ALTITUDE_DEFAULT
, // cm, altitude where to consider launch ended
198 .launch_climb_angle
= SETTING_NAV_FW_LAUNCH_CLIMB_ANGLE_DEFAULT
, // 18 degrees
199 .launch_max_angle
= SETTING_NAV_FW_LAUNCH_MAX_ANGLE_DEFAULT
, // 45 deg
200 .cruise_yaw_rate
= SETTING_NAV_FW_CRUISE_YAW_RATE_DEFAULT
, // 20dps
201 .allow_manual_thr_increase
= SETTING_NAV_FW_ALLOW_MANUAL_THR_INCREASE_DEFAULT
,
202 .useFwNavYawControl
= SETTING_NAV_USE_FW_YAW_CONTROL_DEFAULT
,
203 .yawControlDeadband
= SETTING_NAV_FW_YAW_DEADBAND_DEFAULT
,
204 .soaring_pitch_deadband
= SETTING_NAV_FW_SOARING_PITCH_DEADBAND_DEFAULT
,// pitch angle mode deadband when Saoring mode enabled
208 static navWapointHeading_t wpHeadingControl
;
209 navigationPosControl_t posControl
;
210 navSystemStatus_t NAV_Status
;
211 EXTENDED_FASTRAM multicopterPosXyCoefficients_t multicopterPosXyCoefficients
;
214 int16_t navCurrentState
;
215 int16_t navActualVelocity
[3];
216 int16_t navDesiredVelocity
[3];
217 int16_t navActualHeading
;
218 int16_t navDesiredHeading
;
219 int32_t navTargetPosition
[3];
220 int32_t navLatestActualPosition
[3];
221 int16_t navActualSurface
;
225 int16_t navAccNEU
[3];
226 //End of blackbox states
228 static fpVector3_t
* rthGetHomeTargetPosition(rthTargetMode_e mode
);
229 static void updateDesiredRTHAltitude(void);
230 static void resetAltitudeController(bool useTerrainFollowing
);
231 static void resetPositionController(void);
232 static void setupAltitudeController(void);
233 static void resetHeadingController(void);
234 void resetGCSFlags(void);
236 static void setupJumpCounters(void);
237 static void resetJumpCounter(void);
238 static void clearJumpCounters(void);
240 static void calculateAndSetActiveWaypoint(const navWaypoint_t
* waypoint
);
241 static void calculateAndSetActiveWaypointToLocalPosition(const fpVector3_t
* pos
);
242 void calculateInitialHoldPosition(fpVector3_t
* pos
);
243 void calculateFarAwayTarget(fpVector3_t
* farAwayPos
, int32_t yaw
, int32_t distance
);
244 void calculateNewCruiseTarget(fpVector3_t
* origin
, int32_t yaw
, int32_t distance
);
245 static bool isWaypointPositionReached(const fpVector3_t
* pos
, const bool isWaypointHome
);
246 static void mapWaypointToLocalPosition(fpVector3_t
* localPos
, const navWaypoint_t
* waypoint
, geoAltitudeConversionMode_e altConv
);
247 static navigationFSMEvent_t
nextForNonGeoStates(void);
248 static bool isWaypointMissionValid(void);
249 void missionPlannerSetWaypoint(void);
251 void initializeRTHSanityChecker(void);
252 bool validateRTHSanityChecker(void);
253 void updateHomePosition(void);
255 static bool rthAltControlStickOverrideCheck(unsigned axis
);
257 /*************************************************************************************************/
258 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_IDLE(navigationFSMState_t previousState
);
259 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_ALTHOLD_INITIALIZE(navigationFSMState_t previousState
);
260 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_ALTHOLD_IN_PROGRESS(navigationFSMState_t previousState
);
261 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_POSHOLD_3D_INITIALIZE(navigationFSMState_t previousState
);
262 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_POSHOLD_3D_IN_PROGRESS(navigationFSMState_t previousState
);
263 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_COURSE_HOLD_INITIALIZE(navigationFSMState_t previousState
);
264 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_COURSE_HOLD_IN_PROGRESS(navigationFSMState_t previousState
);
265 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_COURSE_HOLD_ADJUSTING(navigationFSMState_t previousState
);
266 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_CRUISE_INITIALIZE(navigationFSMState_t previousState
);
267 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_CRUISE_IN_PROGRESS(navigationFSMState_t previousState
);
268 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_CRUISE_ADJUSTING(navigationFSMState_t previousState
);
269 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_INITIALIZE(navigationFSMState_t previousState
);
270 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_CLIMB_TO_SAFE_ALT(navigationFSMState_t previousState
);
271 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_HEAD_HOME(navigationFSMState_t previousState
);
272 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_HOVER_PRIOR_TO_LANDING(navigationFSMState_t previousState
);
273 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_HOVER_ABOVE_HOME(navigationFSMState_t previousState
);
274 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_LANDING(navigationFSMState_t previousState
);
275 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_FINISHING(navigationFSMState_t previousState
);
276 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_FINISHED(navigationFSMState_t previousState
);
277 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_WAYPOINT_INITIALIZE(navigationFSMState_t previousState
);
278 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_WAYPOINT_PRE_ACTION(navigationFSMState_t previousState
);
279 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_WAYPOINT_IN_PROGRESS(navigationFSMState_t previousState
);
280 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_WAYPOINT_REACHED(navigationFSMState_t previousState
);
281 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_WAYPOINT_HOLD_TIME(navigationFSMState_t previousState
);
282 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_WAYPOINT_NEXT(navigationFSMState_t previousState
);
283 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_WAYPOINT_FINISHED(navigationFSMState_t previousState
);
284 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_WAYPOINT_RTH_LAND(navigationFSMState_t previousState
);
285 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_EMERGENCY_LANDING_INITIALIZE(navigationFSMState_t previousState
);
286 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_EMERGENCY_LANDING_IN_PROGRESS(navigationFSMState_t previousState
);
287 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_EMERGENCY_LANDING_FINISHED(navigationFSMState_t previousState
);
288 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_LAUNCH_INITIALIZE(navigationFSMState_t previousState
);
289 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_LAUNCH_WAIT(navigationFSMState_t previousState
);
290 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_LAUNCH_IN_PROGRESS(navigationFSMState_t previousState
);
292 static const navigationFSMStateDescriptor_t navFSM
[NAV_STATE_COUNT
] = {
293 /** Idle state ******************************************************/
295 .persistentId
= NAV_PERSISTENT_ID_IDLE
,
296 .onEntry
= navOnEnteringState_NAV_STATE_IDLE
,
299 .mapToFlightModes
= 0,
300 .mwState
= MW_NAV_STATE_NONE
,
301 .mwError
= MW_NAV_ERROR_NONE
,
303 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
304 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
305 [NAV_FSM_EVENT_SWITCH_TO_RTH
] = NAV_STATE_RTH_INITIALIZE
,
306 [NAV_FSM_EVENT_SWITCH_TO_WAYPOINT
] = NAV_STATE_WAYPOINT_INITIALIZE
,
307 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
308 [NAV_FSM_EVENT_SWITCH_TO_LAUNCH
] = NAV_STATE_LAUNCH_INITIALIZE
,
309 [NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD
] = NAV_STATE_COURSE_HOLD_INITIALIZE
,
310 [NAV_FSM_EVENT_SWITCH_TO_CRUISE
] = NAV_STATE_CRUISE_INITIALIZE
,
314 /** ALTHOLD mode ***************************************************/
315 [NAV_STATE_ALTHOLD_INITIALIZE
] = {
316 .persistentId
= NAV_PERSISTENT_ID_ALTHOLD_INITIALIZE
,
317 .onEntry
= navOnEnteringState_NAV_STATE_ALTHOLD_INITIALIZE
,
319 .stateFlags
= NAV_CTL_ALT
| NAV_REQUIRE_ANGLE_FW
| NAV_REQUIRE_THRTILT
,
320 .mapToFlightModes
= NAV_ALTHOLD_MODE
,
321 .mwState
= MW_NAV_STATE_NONE
,
322 .mwError
= MW_NAV_ERROR_NONE
,
324 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_ALTHOLD_IN_PROGRESS
,
325 [NAV_FSM_EVENT_ERROR
] = NAV_STATE_IDLE
,
326 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
330 [NAV_STATE_ALTHOLD_IN_PROGRESS
] = {
331 .persistentId
= NAV_PERSISTENT_ID_ALTHOLD_IN_PROGRESS
,
332 .onEntry
= navOnEnteringState_NAV_STATE_ALTHOLD_IN_PROGRESS
,
334 .stateFlags
= NAV_CTL_ALT
| NAV_REQUIRE_ANGLE_FW
| NAV_REQUIRE_THRTILT
| NAV_RC_ALT
,
335 .mapToFlightModes
= NAV_ALTHOLD_MODE
,
336 .mwState
= MW_NAV_STATE_NONE
,
337 .mwError
= MW_NAV_ERROR_NONE
,
339 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_ALTHOLD_IN_PROGRESS
, // re-process the state
340 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
341 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
342 [NAV_FSM_EVENT_SWITCH_TO_RTH
] = NAV_STATE_RTH_INITIALIZE
,
343 [NAV_FSM_EVENT_SWITCH_TO_WAYPOINT
] = NAV_STATE_WAYPOINT_INITIALIZE
,
344 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
345 [NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD
] = NAV_STATE_COURSE_HOLD_INITIALIZE
,
346 [NAV_FSM_EVENT_SWITCH_TO_CRUISE
] = NAV_STATE_CRUISE_INITIALIZE
,
350 /** POSHOLD_3D mode ************************************************/
351 [NAV_STATE_POSHOLD_3D_INITIALIZE
] = {
352 .persistentId
= NAV_PERSISTENT_ID_POSHOLD_3D_INITIALIZE
,
353 .onEntry
= navOnEnteringState_NAV_STATE_POSHOLD_3D_INITIALIZE
,
355 .stateFlags
= NAV_CTL_ALT
| NAV_CTL_POS
| NAV_REQUIRE_ANGLE
| NAV_REQUIRE_THRTILT
,
356 .mapToFlightModes
= NAV_ALTHOLD_MODE
| NAV_POSHOLD_MODE
,
357 .mwState
= MW_NAV_STATE_HOLD_INFINIT
,
358 .mwError
= MW_NAV_ERROR_NONE
,
360 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_POSHOLD_3D_IN_PROGRESS
,
361 [NAV_FSM_EVENT_ERROR
] = NAV_STATE_IDLE
,
362 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
366 [NAV_STATE_POSHOLD_3D_IN_PROGRESS
] = {
367 .persistentId
= NAV_PERSISTENT_ID_POSHOLD_3D_IN_PROGRESS
,
368 .onEntry
= navOnEnteringState_NAV_STATE_POSHOLD_3D_IN_PROGRESS
,
370 .stateFlags
= NAV_CTL_ALT
| NAV_CTL_POS
| NAV_CTL_YAW
| NAV_REQUIRE_ANGLE
| NAV_REQUIRE_THRTILT
| NAV_RC_ALT
| NAV_RC_POS
| NAV_RC_YAW
,
371 .mapToFlightModes
= NAV_ALTHOLD_MODE
| NAV_POSHOLD_MODE
,
372 .mwState
= MW_NAV_STATE_HOLD_INFINIT
,
373 .mwError
= MW_NAV_ERROR_NONE
,
375 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_POSHOLD_3D_IN_PROGRESS
, // re-process the state
376 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
377 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
378 [NAV_FSM_EVENT_SWITCH_TO_RTH
] = NAV_STATE_RTH_INITIALIZE
,
379 [NAV_FSM_EVENT_SWITCH_TO_WAYPOINT
] = NAV_STATE_WAYPOINT_INITIALIZE
,
380 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
381 [NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD
] = NAV_STATE_COURSE_HOLD_INITIALIZE
,
382 [NAV_FSM_EVENT_SWITCH_TO_CRUISE
] = NAV_STATE_CRUISE_INITIALIZE
,
385 /** CRUISE_HOLD mode ************************************************/
386 [NAV_STATE_COURSE_HOLD_INITIALIZE
] = {
387 .persistentId
= NAV_PERSISTENT_ID_COURSE_HOLD_INITIALIZE
,
388 .onEntry
= navOnEnteringState_NAV_STATE_COURSE_HOLD_INITIALIZE
,
390 .stateFlags
= NAV_REQUIRE_ANGLE
,
391 .mapToFlightModes
= NAV_COURSE_HOLD_MODE
,
392 .mwState
= MW_NAV_STATE_NONE
,
393 .mwError
= MW_NAV_ERROR_NONE
,
395 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_COURSE_HOLD_IN_PROGRESS
,
396 [NAV_FSM_EVENT_ERROR
] = NAV_STATE_IDLE
,
397 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
401 [NAV_STATE_COURSE_HOLD_IN_PROGRESS
] = {
402 .persistentId
= NAV_PERSISTENT_ID_COURSE_HOLD_IN_PROGRESS
,
403 .onEntry
= navOnEnteringState_NAV_STATE_COURSE_HOLD_IN_PROGRESS
,
405 .stateFlags
= NAV_CTL_POS
| NAV_CTL_YAW
| NAV_REQUIRE_ANGLE
| NAV_RC_POS
| NAV_RC_YAW
,
406 .mapToFlightModes
= NAV_COURSE_HOLD_MODE
,
407 .mwState
= MW_NAV_STATE_NONE
,
408 .mwError
= MW_NAV_ERROR_NONE
,
410 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_COURSE_HOLD_IN_PROGRESS
, // re-process the state
411 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
412 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
413 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
414 [NAV_FSM_EVENT_SWITCH_TO_CRUISE
] = NAV_STATE_CRUISE_INITIALIZE
,
415 [NAV_FSM_EVENT_SWITCH_TO_COURSE_ADJ
] = NAV_STATE_COURSE_HOLD_ADJUSTING
,
416 [NAV_FSM_EVENT_SWITCH_TO_RTH
] = NAV_STATE_RTH_INITIALIZE
,
417 [NAV_FSM_EVENT_SWITCH_TO_WAYPOINT
] = NAV_STATE_WAYPOINT_INITIALIZE
,
418 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
422 [NAV_STATE_COURSE_HOLD_ADJUSTING
] = {
423 .persistentId
= NAV_PERSISTENT_ID_COURSE_HOLD_ADJUSTING
,
424 .onEntry
= navOnEnteringState_NAV_STATE_COURSE_HOLD_ADJUSTING
,
426 .stateFlags
= NAV_REQUIRE_ANGLE
| NAV_RC_POS
,
427 .mapToFlightModes
= NAV_COURSE_HOLD_MODE
,
428 .mwState
= MW_NAV_STATE_NONE
,
429 .mwError
= MW_NAV_ERROR_NONE
,
431 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_COURSE_HOLD_IN_PROGRESS
,
432 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_COURSE_HOLD_ADJUSTING
,
433 [NAV_FSM_EVENT_ERROR
] = NAV_STATE_IDLE
,
434 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
435 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
436 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
437 [NAV_FSM_EVENT_SWITCH_TO_CRUISE
] = NAV_STATE_CRUISE_INITIALIZE
,
438 [NAV_FSM_EVENT_SWITCH_TO_RTH
] = NAV_STATE_RTH_INITIALIZE
,
439 [NAV_FSM_EVENT_SWITCH_TO_WAYPOINT
] = NAV_STATE_WAYPOINT_INITIALIZE
,
440 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
444 /** CRUISE_3D mode ************************************************/
445 [NAV_STATE_CRUISE_INITIALIZE
] = {
446 .persistentId
= NAV_PERSISTENT_ID_CRUISE_INITIALIZE
,
447 .onEntry
= navOnEnteringState_NAV_STATE_CRUISE_INITIALIZE
,
449 .stateFlags
= NAV_REQUIRE_ANGLE
,
450 .mapToFlightModes
= NAV_ALTHOLD_MODE
| NAV_COURSE_HOLD_MODE
,
451 .mwState
= MW_NAV_STATE_NONE
,
452 .mwError
= MW_NAV_ERROR_NONE
,
454 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_CRUISE_IN_PROGRESS
,
455 [NAV_FSM_EVENT_ERROR
] = NAV_STATE_IDLE
,
456 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
460 [NAV_STATE_CRUISE_IN_PROGRESS
] = {
461 .persistentId
= NAV_PERSISTENT_ID_CRUISE_IN_PROGRESS
,
462 .onEntry
= navOnEnteringState_NAV_STATE_CRUISE_IN_PROGRESS
,
464 .stateFlags
= NAV_CTL_POS
| NAV_CTL_YAW
| NAV_CTL_ALT
| NAV_REQUIRE_ANGLE
| NAV_RC_POS
| NAV_RC_YAW
| NAV_RC_ALT
,
465 .mapToFlightModes
= NAV_ALTHOLD_MODE
| NAV_COURSE_HOLD_MODE
,
466 .mwState
= MW_NAV_STATE_NONE
,
467 .mwError
= MW_NAV_ERROR_NONE
,
469 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_CRUISE_IN_PROGRESS
, // re-process the state
470 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
471 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
472 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
473 [NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD
] = NAV_STATE_COURSE_HOLD_INITIALIZE
,
474 [NAV_FSM_EVENT_SWITCH_TO_COURSE_ADJ
] = NAV_STATE_CRUISE_ADJUSTING
,
475 [NAV_FSM_EVENT_SWITCH_TO_RTH
] = NAV_STATE_RTH_INITIALIZE
,
476 [NAV_FSM_EVENT_SWITCH_TO_WAYPOINT
] = NAV_STATE_WAYPOINT_INITIALIZE
,
477 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
481 [NAV_STATE_CRUISE_ADJUSTING
] = {
482 .persistentId
= NAV_PERSISTENT_ID_CRUISE_ADJUSTING
,
483 .onEntry
= navOnEnteringState_NAV_STATE_CRUISE_ADJUSTING
,
485 .stateFlags
= NAV_CTL_ALT
| NAV_REQUIRE_ANGLE
| NAV_RC_POS
| NAV_RC_ALT
,
486 .mapToFlightModes
= NAV_ALTHOLD_MODE
| NAV_COURSE_HOLD_MODE
,
487 .mwState
= MW_NAV_STATE_NONE
,
488 .mwError
= MW_NAV_ERROR_NONE
,
490 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_CRUISE_IN_PROGRESS
,
491 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_CRUISE_ADJUSTING
,
492 [NAV_FSM_EVENT_ERROR
] = NAV_STATE_IDLE
,
493 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
494 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
495 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
496 [NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD
] = NAV_STATE_COURSE_HOLD_INITIALIZE
,
497 [NAV_FSM_EVENT_SWITCH_TO_RTH
] = NAV_STATE_RTH_INITIALIZE
,
498 [NAV_FSM_EVENT_SWITCH_TO_WAYPOINT
] = NAV_STATE_WAYPOINT_INITIALIZE
,
499 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
503 /** RTH_3D mode ************************************************/
504 [NAV_STATE_RTH_INITIALIZE
] = {
505 .persistentId
= NAV_PERSISTENT_ID_RTH_INITIALIZE
,
506 .onEntry
= navOnEnteringState_NAV_STATE_RTH_INITIALIZE
,
508 .stateFlags
= NAV_CTL_ALT
| NAV_CTL_POS
| NAV_CTL_YAW
| NAV_REQUIRE_ANGLE
| NAV_REQUIRE_MAGHOLD
| NAV_REQUIRE_THRTILT
| NAV_AUTO_RTH
,
509 .mapToFlightModes
= NAV_RTH_MODE
| NAV_ALTHOLD_MODE
,
510 .mwState
= MW_NAV_STATE_RTH_START
,
511 .mwError
= MW_NAV_ERROR_NONE
,
513 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_RTH_INITIALIZE
, // re-process the state
514 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_RTH_CLIMB_TO_SAFE_ALT
,
515 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
516 [NAV_FSM_EVENT_SWITCH_TO_RTH_LANDING
] = NAV_STATE_RTH_HOVER_PRIOR_TO_LANDING
,
517 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
521 [NAV_STATE_RTH_CLIMB_TO_SAFE_ALT
] = {
522 .persistentId
= NAV_PERSISTENT_ID_RTH_CLIMB_TO_SAFE_ALT
,
523 .onEntry
= navOnEnteringState_NAV_STATE_RTH_CLIMB_TO_SAFE_ALT
,
525 .stateFlags
= NAV_CTL_ALT
| NAV_CTL_POS
| NAV_CTL_YAW
| NAV_REQUIRE_ANGLE
| NAV_REQUIRE_MAGHOLD
| NAV_REQUIRE_THRTILT
| NAV_AUTO_RTH
| NAV_RC_POS
| NAV_RC_YAW
, // allow pos adjustment while climbind to safe alt
526 .mapToFlightModes
= NAV_RTH_MODE
| NAV_ALTHOLD_MODE
,
527 .mwState
= MW_NAV_STATE_RTH_CLIMB
,
528 .mwError
= MW_NAV_ERROR_WAIT_FOR_RTH_ALT
,
530 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_RTH_CLIMB_TO_SAFE_ALT
, // re-process the state
531 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_RTH_HEAD_HOME
,
532 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
533 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
534 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
535 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
536 [NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD
] = NAV_STATE_COURSE_HOLD_INITIALIZE
,
537 [NAV_FSM_EVENT_SWITCH_TO_CRUISE
] = NAV_STATE_CRUISE_INITIALIZE
,
541 [NAV_STATE_RTH_HEAD_HOME
] = {
542 .persistentId
= NAV_PERSISTENT_ID_RTH_HEAD_HOME
,
543 .onEntry
= navOnEnteringState_NAV_STATE_RTH_HEAD_HOME
,
545 .stateFlags
= NAV_CTL_ALT
| NAV_CTL_POS
| NAV_CTL_YAW
| NAV_REQUIRE_ANGLE
| NAV_REQUIRE_MAGHOLD
| NAV_REQUIRE_THRTILT
| NAV_AUTO_RTH
| NAV_RC_POS
| NAV_RC_YAW
,
546 .mapToFlightModes
= NAV_RTH_MODE
| NAV_ALTHOLD_MODE
,
547 .mwState
= MW_NAV_STATE_RTH_ENROUTE
,
548 .mwError
= MW_NAV_ERROR_NONE
,
550 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_RTH_HEAD_HOME
, // re-process the state
551 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_RTH_HOVER_PRIOR_TO_LANDING
,
552 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
553 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
554 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
555 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
556 [NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD
] = NAV_STATE_COURSE_HOLD_INITIALIZE
,
557 [NAV_FSM_EVENT_SWITCH_TO_CRUISE
] = NAV_STATE_CRUISE_INITIALIZE
,
561 [NAV_STATE_RTH_HOVER_PRIOR_TO_LANDING
] = {
562 .persistentId
= NAV_PERSISTENT_ID_RTH_HOVER_PRIOR_TO_LANDING
,
563 .onEntry
= navOnEnteringState_NAV_STATE_RTH_HOVER_PRIOR_TO_LANDING
,
565 .stateFlags
= NAV_CTL_ALT
| NAV_CTL_POS
| NAV_CTL_YAW
| NAV_REQUIRE_ANGLE
| NAV_REQUIRE_MAGHOLD
| NAV_REQUIRE_THRTILT
| NAV_AUTO_RTH
| NAV_RC_POS
| NAV_RC_YAW
,
566 .mapToFlightModes
= NAV_RTH_MODE
| NAV_ALTHOLD_MODE
,
567 .mwState
= MW_NAV_STATE_LAND_SETTLE
,
568 .mwError
= MW_NAV_ERROR_NONE
,
570 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_RTH_HOVER_PRIOR_TO_LANDING
,
571 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_RTH_LANDING
,
572 [NAV_FSM_EVENT_SWITCH_TO_RTH_HOVER_ABOVE_HOME
] = NAV_STATE_RTH_HOVER_ABOVE_HOME
,
573 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
574 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
575 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
576 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
577 [NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD
] = NAV_STATE_COURSE_HOLD_INITIALIZE
,
578 [NAV_FSM_EVENT_SWITCH_TO_CRUISE
] = NAV_STATE_CRUISE_INITIALIZE
,
582 [NAV_STATE_RTH_HOVER_ABOVE_HOME
] = {
583 .persistentId
= NAV_PERSISTENT_ID_RTH_HOVER_ABOVE_HOME
,
584 .onEntry
= navOnEnteringState_NAV_STATE_RTH_HOVER_ABOVE_HOME
,
586 .stateFlags
= NAV_CTL_ALT
| NAV_CTL_POS
| NAV_CTL_YAW
| NAV_REQUIRE_ANGLE
| NAV_REQUIRE_MAGHOLD
| NAV_REQUIRE_THRTILT
| NAV_AUTO_RTH
| NAV_RC_POS
| NAV_RC_YAW
| NAV_RC_ALT
,
587 .mapToFlightModes
= NAV_RTH_MODE
| NAV_ALTHOLD_MODE
,
588 .mwState
= MW_NAV_STATE_HOVER_ABOVE_HOME
,
589 .mwError
= MW_NAV_ERROR_NONE
,
591 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_RTH_HOVER_ABOVE_HOME
,
592 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
593 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
594 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
595 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
596 [NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD
] = NAV_STATE_COURSE_HOLD_INITIALIZE
,
597 [NAV_FSM_EVENT_SWITCH_TO_CRUISE
] = NAV_STATE_CRUISE_INITIALIZE
,
601 [NAV_STATE_RTH_LANDING
] = {
602 .persistentId
= NAV_PERSISTENT_ID_RTH_LANDING
,
603 .onEntry
= navOnEnteringState_NAV_STATE_RTH_LANDING
,
605 .stateFlags
= NAV_CTL_ALT
| NAV_CTL_POS
| NAV_CTL_YAW
| NAV_CTL_LAND
| NAV_REQUIRE_ANGLE
| NAV_REQUIRE_MAGHOLD
| NAV_REQUIRE_THRTILT
| NAV_AUTO_RTH
| NAV_RC_POS
| NAV_RC_YAW
,
606 .mapToFlightModes
= NAV_RTH_MODE
| NAV_ALTHOLD_MODE
,
607 .mwState
= MW_NAV_STATE_LAND_IN_PROGRESS
,
608 .mwError
= MW_NAV_ERROR_LANDING
,
610 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_RTH_LANDING
, // re-process state
611 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_RTH_FINISHING
,
612 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
613 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
614 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
615 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
619 [NAV_STATE_RTH_FINISHING
] = {
620 .persistentId
= NAV_PERSISTENT_ID_RTH_FINISHING
,
621 .onEntry
= navOnEnteringState_NAV_STATE_RTH_FINISHING
,
623 .stateFlags
= NAV_CTL_ALT
| NAV_CTL_POS
| NAV_CTL_YAW
| NAV_REQUIRE_ANGLE
| NAV_REQUIRE_MAGHOLD
| NAV_REQUIRE_THRTILT
| NAV_AUTO_RTH
,
624 .mapToFlightModes
= NAV_RTH_MODE
| NAV_ALTHOLD_MODE
,
625 .mwState
= MW_NAV_STATE_LAND_IN_PROGRESS
,
626 .mwError
= MW_NAV_ERROR_LANDING
,
628 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_RTH_FINISHED
,
629 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
633 [NAV_STATE_RTH_FINISHED
] = {
634 .persistentId
= NAV_PERSISTENT_ID_RTH_FINISHED
,
635 .onEntry
= navOnEnteringState_NAV_STATE_RTH_FINISHED
,
637 .stateFlags
= NAV_CTL_ALT
| NAV_REQUIRE_ANGLE
| NAV_REQUIRE_THRTILT
| NAV_AUTO_RTH
,
638 .mapToFlightModes
= NAV_RTH_MODE
| NAV_ALTHOLD_MODE
,
639 .mwState
= MW_NAV_STATE_LANDED
,
640 .mwError
= MW_NAV_ERROR_NONE
,
642 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_RTH_FINISHED
, // re-process state
643 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
644 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
645 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
646 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
650 /** WAYPOINT mode ************************************************/
651 [NAV_STATE_WAYPOINT_INITIALIZE
] = {
652 .persistentId
= NAV_PERSISTENT_ID_WAYPOINT_INITIALIZE
,
653 .onEntry
= navOnEnteringState_NAV_STATE_WAYPOINT_INITIALIZE
,
655 .stateFlags
= NAV_CTL_ALT
| NAV_CTL_POS
| NAV_CTL_YAW
| NAV_REQUIRE_ANGLE
| NAV_REQUIRE_MAGHOLD
| NAV_REQUIRE_THRTILT
| NAV_AUTO_WP
,
656 .mapToFlightModes
= NAV_WP_MODE
| NAV_ALTHOLD_MODE
,
657 .mwState
= MW_NAV_STATE_PROCESS_NEXT
,
658 .mwError
= MW_NAV_ERROR_NONE
,
660 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_WAYPOINT_PRE_ACTION
,
661 [NAV_FSM_EVENT_ERROR
] = NAV_STATE_IDLE
,
662 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
663 [NAV_FSM_EVENT_SWITCH_TO_WAYPOINT_FINISHED
] = NAV_STATE_WAYPOINT_FINISHED
,
667 [NAV_STATE_WAYPOINT_PRE_ACTION
] = {
668 .persistentId
= NAV_PERSISTENT_ID_WAYPOINT_PRE_ACTION
,
669 .onEntry
= navOnEnteringState_NAV_STATE_WAYPOINT_PRE_ACTION
,
671 .stateFlags
= NAV_CTL_ALT
| NAV_CTL_POS
| NAV_CTL_YAW
| NAV_REQUIRE_ANGLE
| NAV_REQUIRE_MAGHOLD
| NAV_REQUIRE_THRTILT
| NAV_AUTO_WP
,
672 .mapToFlightModes
= NAV_WP_MODE
| NAV_ALTHOLD_MODE
,
673 .mwState
= MW_NAV_STATE_PROCESS_NEXT
,
674 .mwError
= MW_NAV_ERROR_NONE
,
676 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_WAYPOINT_PRE_ACTION
, // re-process the state (for JUMP)
677 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_WAYPOINT_IN_PROGRESS
,
678 [NAV_FSM_EVENT_ERROR
] = NAV_STATE_IDLE
,
679 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
680 [NAV_FSM_EVENT_SWITCH_TO_RTH
] = NAV_STATE_RTH_INITIALIZE
,
681 [NAV_FSM_EVENT_SWITCH_TO_WAYPOINT_FINISHED
] = NAV_STATE_WAYPOINT_FINISHED
,
685 [NAV_STATE_WAYPOINT_IN_PROGRESS
] = {
686 .persistentId
= NAV_PERSISTENT_ID_WAYPOINT_IN_PROGRESS
,
687 .onEntry
= navOnEnteringState_NAV_STATE_WAYPOINT_IN_PROGRESS
,
689 .stateFlags
= NAV_CTL_ALT
| NAV_CTL_POS
| NAV_CTL_YAW
| NAV_REQUIRE_ANGLE
| NAV_REQUIRE_MAGHOLD
| NAV_REQUIRE_THRTILT
| NAV_AUTO_WP
,
690 .mapToFlightModes
= NAV_WP_MODE
| NAV_ALTHOLD_MODE
,
691 .mwState
= MW_NAV_STATE_WP_ENROUTE
,
692 .mwError
= MW_NAV_ERROR_NONE
,
694 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_WAYPOINT_IN_PROGRESS
, // re-process the state
695 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_WAYPOINT_REACHED
, // successfully reached waypoint
696 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
697 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
698 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
699 [NAV_FSM_EVENT_SWITCH_TO_RTH
] = NAV_STATE_RTH_INITIALIZE
,
700 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
701 [NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD
] = NAV_STATE_COURSE_HOLD_INITIALIZE
,
702 [NAV_FSM_EVENT_SWITCH_TO_CRUISE
] = NAV_STATE_CRUISE_INITIALIZE
,
706 [NAV_STATE_WAYPOINT_REACHED
] = {
707 .persistentId
= NAV_PERSISTENT_ID_WAYPOINT_REACHED
,
708 .onEntry
= navOnEnteringState_NAV_STATE_WAYPOINT_REACHED
,
710 .stateFlags
= NAV_CTL_ALT
| NAV_CTL_POS
| NAV_CTL_YAW
| NAV_REQUIRE_ANGLE
| NAV_REQUIRE_MAGHOLD
| NAV_REQUIRE_THRTILT
| NAV_AUTO_WP
,
711 .mapToFlightModes
= NAV_WP_MODE
| NAV_ALTHOLD_MODE
,
712 .mwState
= MW_NAV_STATE_PROCESS_NEXT
,
713 .mwError
= MW_NAV_ERROR_NONE
,
715 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_WAYPOINT_REACHED
, // re-process state
716 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_WAYPOINT_NEXT
,
717 [NAV_FSM_EVENT_SWITCH_TO_WAYPOINT_HOLD_TIME
] = NAV_STATE_WAYPOINT_HOLD_TIME
,
718 [NAV_FSM_EVENT_SWITCH_TO_WAYPOINT_FINISHED
] = NAV_STATE_WAYPOINT_FINISHED
,
719 [NAV_FSM_EVENT_SWITCH_TO_WAYPOINT_RTH_LAND
] = NAV_STATE_WAYPOINT_RTH_LAND
,
720 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
721 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
722 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
723 [NAV_FSM_EVENT_SWITCH_TO_RTH
] = NAV_STATE_RTH_INITIALIZE
,
724 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
725 [NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD
] = NAV_STATE_COURSE_HOLD_INITIALIZE
,
726 [NAV_FSM_EVENT_SWITCH_TO_CRUISE
] = NAV_STATE_CRUISE_INITIALIZE
,
730 [NAV_STATE_WAYPOINT_HOLD_TIME
] = {
731 .persistentId
= NAV_PERSISTENT_ID_WAYPOINT_HOLD_TIME
, // There is no state for timed hold?
732 .onEntry
= navOnEnteringState_NAV_STATE_WAYPOINT_HOLD_TIME
,
734 .stateFlags
= NAV_CTL_ALT
| NAV_CTL_POS
| NAV_CTL_YAW
| NAV_REQUIRE_ANGLE
| NAV_REQUIRE_MAGHOLD
| NAV_REQUIRE_THRTILT
| NAV_AUTO_WP
,
735 .mapToFlightModes
= NAV_WP_MODE
| NAV_ALTHOLD_MODE
,
736 .mwState
= MW_NAV_STATE_HOLD_TIMED
,
737 .mwError
= MW_NAV_ERROR_NONE
,
739 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_WAYPOINT_HOLD_TIME
, // re-process the state
740 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_WAYPOINT_NEXT
, // successfully reached waypoint
741 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
742 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
743 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
744 [NAV_FSM_EVENT_SWITCH_TO_RTH
] = NAV_STATE_RTH_INITIALIZE
,
745 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
746 [NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD
] = NAV_STATE_COURSE_HOLD_INITIALIZE
,
747 [NAV_FSM_EVENT_SWITCH_TO_CRUISE
] = NAV_STATE_CRUISE_INITIALIZE
,
751 [NAV_STATE_WAYPOINT_RTH_LAND
] = {
752 .persistentId
= NAV_PERSISTENT_ID_WAYPOINT_RTH_LAND
,
753 .onEntry
= navOnEnteringState_NAV_STATE_WAYPOINT_RTH_LAND
,
755 .stateFlags
= NAV_CTL_ALT
| NAV_CTL_POS
| NAV_CTL_YAW
| NAV_CTL_LAND
| NAV_REQUIRE_ANGLE
| NAV_REQUIRE_MAGHOLD
| NAV_REQUIRE_THRTILT
| NAV_AUTO_WP
,
756 .mapToFlightModes
= NAV_WP_MODE
| NAV_ALTHOLD_MODE
,
757 .mwState
= MW_NAV_STATE_LAND_IN_PROGRESS
,
758 .mwError
= MW_NAV_ERROR_LANDING
,
760 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_WAYPOINT_RTH_LAND
, // re-process state
761 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_WAYPOINT_FINISHED
,
762 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
763 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
764 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
765 [NAV_FSM_EVENT_SWITCH_TO_RTH
] = NAV_STATE_RTH_INITIALIZE
,
766 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
767 [NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD
] = NAV_STATE_COURSE_HOLD_INITIALIZE
,
768 [NAV_FSM_EVENT_SWITCH_TO_CRUISE
] = NAV_STATE_CRUISE_INITIALIZE
,
772 [NAV_STATE_WAYPOINT_NEXT
] = {
773 .persistentId
= NAV_PERSISTENT_ID_WAYPOINT_NEXT
,
774 .onEntry
= navOnEnteringState_NAV_STATE_WAYPOINT_NEXT
,
776 .stateFlags
= NAV_CTL_ALT
| NAV_CTL_POS
| NAV_CTL_YAW
| NAV_REQUIRE_ANGLE
| NAV_REQUIRE_MAGHOLD
| NAV_REQUIRE_THRTILT
| NAV_AUTO_WP
,
777 .mapToFlightModes
= NAV_WP_MODE
| NAV_ALTHOLD_MODE
,
778 .mwState
= MW_NAV_STATE_PROCESS_NEXT
,
779 .mwError
= MW_NAV_ERROR_NONE
,
781 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_WAYPOINT_PRE_ACTION
,
782 [NAV_FSM_EVENT_SWITCH_TO_WAYPOINT_FINISHED
] = NAV_STATE_WAYPOINT_FINISHED
,
786 [NAV_STATE_WAYPOINT_FINISHED
] = {
787 .persistentId
= NAV_PERSISTENT_ID_WAYPOINT_FINISHED
,
788 .onEntry
= navOnEnteringState_NAV_STATE_WAYPOINT_FINISHED
,
790 .stateFlags
= NAV_CTL_ALT
| NAV_CTL_POS
| NAV_CTL_YAW
| NAV_REQUIRE_ANGLE
| NAV_REQUIRE_MAGHOLD
| NAV_REQUIRE_THRTILT
| NAV_AUTO_WP
| NAV_AUTO_WP_DONE
,
791 .mapToFlightModes
= NAV_WP_MODE
| NAV_ALTHOLD_MODE
,
792 .mwState
= MW_NAV_STATE_WP_ENROUTE
,
793 .mwError
= MW_NAV_ERROR_FINISH
,
795 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_WAYPOINT_FINISHED
, // re-process state
796 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
797 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
798 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
799 [NAV_FSM_EVENT_SWITCH_TO_RTH
] = NAV_STATE_RTH_INITIALIZE
,
800 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
801 [NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD
] = NAV_STATE_COURSE_HOLD_INITIALIZE
,
802 [NAV_FSM_EVENT_SWITCH_TO_CRUISE
] = NAV_STATE_CRUISE_INITIALIZE
,
806 /** EMERGENCY LANDING ************************************************/
807 [NAV_STATE_EMERGENCY_LANDING_INITIALIZE
] = {
808 .persistentId
= NAV_PERSISTENT_ID_EMERGENCY_LANDING_INITIALIZE
,
809 .onEntry
= navOnEnteringState_NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
811 .stateFlags
= NAV_CTL_EMERG
| NAV_REQUIRE_ANGLE
,
812 .mapToFlightModes
= 0,
813 .mwState
= MW_NAV_STATE_EMERGENCY_LANDING
,
814 .mwError
= MW_NAV_ERROR_LANDING
,
816 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_EMERGENCY_LANDING_IN_PROGRESS
,
817 [NAV_FSM_EVENT_ERROR
] = NAV_STATE_IDLE
,
818 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
819 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
820 [NAV_FSM_EVENT_SWITCH_TO_RTH
] = NAV_STATE_RTH_INITIALIZE
,
821 [NAV_FSM_EVENT_SWITCH_TO_WAYPOINT
] = NAV_STATE_WAYPOINT_INITIALIZE
,
825 [NAV_STATE_EMERGENCY_LANDING_IN_PROGRESS
] = {
826 .persistentId
= NAV_PERSISTENT_ID_EMERGENCY_LANDING_IN_PROGRESS
,
827 .onEntry
= navOnEnteringState_NAV_STATE_EMERGENCY_LANDING_IN_PROGRESS
,
829 .stateFlags
= NAV_CTL_EMERG
| NAV_REQUIRE_ANGLE
,
830 .mapToFlightModes
= 0,
831 .mwState
= MW_NAV_STATE_EMERGENCY_LANDING
,
832 .mwError
= MW_NAV_ERROR_LANDING
,
834 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_EMERGENCY_LANDING_IN_PROGRESS
, // re-process the state
835 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_EMERGENCY_LANDING_FINISHED
,
836 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
837 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
838 [NAV_FSM_EVENT_SWITCH_TO_RTH
] = NAV_STATE_RTH_INITIALIZE
,
839 [NAV_FSM_EVENT_SWITCH_TO_WAYPOINT
] = NAV_STATE_WAYPOINT_INITIALIZE
,
843 [NAV_STATE_EMERGENCY_LANDING_FINISHED
] = {
844 .persistentId
= NAV_PERSISTENT_ID_EMERGENCY_LANDING_FINISHED
,
845 .onEntry
= navOnEnteringState_NAV_STATE_EMERGENCY_LANDING_FINISHED
,
847 .stateFlags
= NAV_CTL_EMERG
| NAV_REQUIRE_ANGLE
,
848 .mapToFlightModes
= 0,
849 .mwState
= MW_NAV_STATE_LANDED
,
850 .mwError
= MW_NAV_ERROR_LANDING
,
852 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_EMERGENCY_LANDING_FINISHED
,
853 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
857 [NAV_STATE_LAUNCH_INITIALIZE
] = {
858 .persistentId
= NAV_PERSISTENT_ID_LAUNCH_INITIALIZE
,
859 .onEntry
= navOnEnteringState_NAV_STATE_LAUNCH_INITIALIZE
,
861 .stateFlags
= NAV_REQUIRE_ANGLE
,
862 .mapToFlightModes
= NAV_LAUNCH_MODE
,
863 .mwState
= MW_NAV_STATE_NONE
,
864 .mwError
= MW_NAV_ERROR_NONE
,
866 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_LAUNCH_WAIT
,
867 [NAV_FSM_EVENT_ERROR
] = NAV_STATE_IDLE
,
868 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
872 [NAV_STATE_LAUNCH_WAIT
] = {
873 .persistentId
= NAV_PERSISTENT_ID_LAUNCH_WAIT
,
874 .onEntry
= navOnEnteringState_NAV_STATE_LAUNCH_WAIT
,
876 .stateFlags
= NAV_CTL_LAUNCH
| NAV_REQUIRE_ANGLE
,
877 .mapToFlightModes
= NAV_LAUNCH_MODE
,
878 .mwState
= MW_NAV_STATE_NONE
,
879 .mwError
= MW_NAV_ERROR_NONE
,
881 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_LAUNCH_WAIT
, // re-process the state
882 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_LAUNCH_IN_PROGRESS
,
883 [NAV_FSM_EVENT_ERROR
] = NAV_STATE_IDLE
,
884 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
888 [NAV_STATE_LAUNCH_IN_PROGRESS
] = {
889 .persistentId
= NAV_PERSISTENT_ID_LAUNCH_IN_PROGRESS
,
890 .onEntry
= navOnEnteringState_NAV_STATE_LAUNCH_IN_PROGRESS
,
892 .stateFlags
= NAV_CTL_LAUNCH
| NAV_REQUIRE_ANGLE
,
893 .mapToFlightModes
= NAV_LAUNCH_MODE
,
894 .mwState
= MW_NAV_STATE_NONE
,
895 .mwError
= MW_NAV_ERROR_NONE
,
897 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_LAUNCH_IN_PROGRESS
, // re-process the state
898 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_IDLE
,
899 [NAV_FSM_EVENT_ERROR
] = NAV_STATE_IDLE
,
900 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
905 static navigationFSMStateFlags_t
navGetStateFlags(navigationFSMState_t state
)
907 return navFSM
[state
].stateFlags
;
910 static flightModeFlags_e
navGetMappedFlightModes(navigationFSMState_t state
)
912 return navFSM
[state
].mapToFlightModes
;
915 navigationFSMStateFlags_t
navGetCurrentStateFlags(void)
917 return navGetStateFlags(posControl
.navState
);
920 static bool navTerrainFollowingRequested(void)
922 // Terrain following not supported on FIXED WING aircraft yet
923 return !STATE(FIXED_WING_LEGACY
) && IS_RC_MODE_ACTIVE(BOXSURFACE
);
926 /*************************************************************************************************/
927 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_IDLE(navigationFSMState_t previousState
)
929 UNUSED(previousState
);
931 resetAltitudeController(false);
932 resetHeadingController();
933 resetPositionController();
935 return NAV_FSM_EVENT_NONE
;
938 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_ALTHOLD_INITIALIZE(navigationFSMState_t previousState
)
940 const navigationFSMStateFlags_t prevFlags
= navGetStateFlags(previousState
);
941 const bool terrainFollowingToggled
= (posControl
.flags
.isTerrainFollowEnabled
!= navTerrainFollowingRequested());
945 // If surface tracking mode changed value - reset altitude controller
946 if ((prevFlags
& NAV_CTL_ALT
) == 0 || terrainFollowingToggled
) {
947 resetAltitudeController(navTerrainFollowingRequested());
950 if (((prevFlags
& NAV_CTL_ALT
) == 0) || ((prevFlags
& NAV_AUTO_RTH
) != 0) || ((prevFlags
& NAV_AUTO_WP
) != 0) || terrainFollowingToggled
) {
951 setupAltitudeController();
952 setDesiredPosition(&navGetCurrentActualPositionAndVelocity()->pos
, posControl
.actualState
.yaw
, NAV_POS_UPDATE_Z
); // This will reset surface offset
955 return NAV_FSM_EVENT_SUCCESS
;
958 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_ALTHOLD_IN_PROGRESS(navigationFSMState_t previousState
)
960 UNUSED(previousState
);
962 // If GCS was disabled - reset altitude setpoint
963 if (posControl
.flags
.isGCSAssistedNavigationReset
) {
964 setDesiredPosition(&navGetCurrentActualPositionAndVelocity()->pos
, posControl
.actualState
.yaw
, NAV_POS_UPDATE_Z
);
968 return NAV_FSM_EVENT_NONE
;
971 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_POSHOLD_3D_INITIALIZE(navigationFSMState_t previousState
)
973 const navigationFSMStateFlags_t prevFlags
= navGetStateFlags(previousState
);
974 const bool terrainFollowingToggled
= (posControl
.flags
.isTerrainFollowEnabled
!= navTerrainFollowingRequested());
978 if ((prevFlags
& NAV_CTL_POS
) == 0) {
979 resetPositionController();
982 if ((prevFlags
& NAV_CTL_ALT
) == 0 || terrainFollowingToggled
) {
983 resetAltitudeController(navTerrainFollowingRequested());
984 setupAltitudeController();
987 if (((prevFlags
& NAV_CTL_ALT
) == 0) || ((prevFlags
& NAV_AUTO_RTH
) != 0) || ((prevFlags
& NAV_AUTO_WP
) != 0) || terrainFollowingToggled
) {
988 setDesiredPosition(&navGetCurrentActualPositionAndVelocity()->pos
, posControl
.actualState
.yaw
, NAV_POS_UPDATE_Z
); // This will reset surface offset
991 if ((previousState
!= NAV_STATE_RTH_HOVER_PRIOR_TO_LANDING
) && (previousState
!= NAV_STATE_RTH_HOVER_ABOVE_HOME
) && (previousState
!= NAV_STATE_RTH_LANDING
)) {
992 fpVector3_t targetHoldPos
;
993 calculateInitialHoldPosition(&targetHoldPos
);
994 setDesiredPosition(&targetHoldPos
, posControl
.actualState
.yaw
, NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_HEADING
);
997 return NAV_FSM_EVENT_SUCCESS
;
1000 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_POSHOLD_3D_IN_PROGRESS(navigationFSMState_t previousState
)
1002 UNUSED(previousState
);
1004 // If GCS was disabled - reset 2D pos setpoint
1005 if (posControl
.flags
.isGCSAssistedNavigationReset
) {
1006 fpVector3_t targetHoldPos
;
1007 calculateInitialHoldPosition(&targetHoldPos
);
1008 setDesiredPosition(&navGetCurrentActualPositionAndVelocity()->pos
, posControl
.actualState
.yaw
, NAV_POS_UPDATE_Z
);
1009 setDesiredPosition(&targetHoldPos
, posControl
.actualState
.yaw
, NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_HEADING
);
1013 return NAV_FSM_EVENT_NONE
;
1017 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_COURSE_HOLD_INITIALIZE(navigationFSMState_t previousState
)
1019 const navigationFSMStateFlags_t prevFlags
= navGetStateFlags(previousState
);
1021 if (!STATE(FIXED_WING_LEGACY
)) { return NAV_FSM_EVENT_ERROR
; } // Only on FW for now
1023 DEBUG_SET(DEBUG_CRUISE
, 0, 1);
1024 if (checkForPositionSensorTimeout()) {
1025 return NAV_FSM_EVENT_SWITCH_TO_IDLE
;
1026 } // Switch to IDLE if we do not have an healty position. Try the next iteration.
1028 if (!(prevFlags
& NAV_CTL_POS
)) {
1029 resetPositionController();
1032 posControl
.cruise
.yaw
= posControl
.actualState
.yaw
; // Store the yaw to follow
1033 posControl
.cruise
.previousYaw
= posControl
.cruise
.yaw
;
1034 posControl
.cruise
.lastYawAdjustmentTime
= 0;
1036 return NAV_FSM_EVENT_SUCCESS
; // Go to CRUISE_XD_IN_PROGRESS state
1039 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_COURSE_HOLD_IN_PROGRESS(navigationFSMState_t previousState
)
1041 const timeMs_t currentTimeMs
= millis();
1043 if (checkForPositionSensorTimeout()) {
1044 return NAV_FSM_EVENT_SWITCH_TO_IDLE
;
1045 } // Switch to IDLE if we do not have an healty position. Do the CRUISE init the next iteration.
1047 DEBUG_SET(DEBUG_CRUISE
, 0, 2);
1048 DEBUG_SET(DEBUG_CRUISE
, 2, 0);
1050 if (posControl
.flags
.isAdjustingPosition
) {
1051 return NAV_FSM_EVENT_SWITCH_TO_COURSE_ADJ
;
1054 // User is yawing. We record the desidered yaw and we change the desidered target in the meanwhile
1055 if (posControl
.flags
.isAdjustingHeading
) {
1056 timeMs_t timeDifference
= currentTimeMs
- posControl
.cruise
.lastYawAdjustmentTime
;
1057 if (timeDifference
> 100) timeDifference
= 0; // if adjustment was called long time ago, reset the time difference.
1058 float rateTarget
= scaleRangef((float)rcCommand
[YAW
], -500.0f
, 500.0f
, -DEGREES_TO_CENTIDEGREES(navConfig()->fw
.cruise_yaw_rate
), DEGREES_TO_CENTIDEGREES(navConfig()->fw
.cruise_yaw_rate
));
1059 float centidegsPerIteration
= rateTarget
* timeDifference
* 0.001f
;
1060 posControl
.cruise
.yaw
= wrap_36000(posControl
.cruise
.yaw
- centidegsPerIteration
);
1061 DEBUG_SET(DEBUG_CRUISE
, 1, CENTIDEGREES_TO_DEGREES(posControl
.cruise
.yaw
));
1062 posControl
.cruise
.lastYawAdjustmentTime
= currentTimeMs
;
1065 if (currentTimeMs
- posControl
.cruise
.lastYawAdjustmentTime
> 4000)
1066 posControl
.cruise
.previousYaw
= posControl
.cruise
.yaw
;
1068 uint32_t distance
= gpsSol
.groundSpeed
* 60; // next WP to be reached in 60s [cm]
1070 if ((previousState
== NAV_STATE_COURSE_HOLD_INITIALIZE
) || (previousState
== NAV_STATE_COURSE_HOLD_ADJUSTING
)
1071 || (previousState
== NAV_STATE_CRUISE_INITIALIZE
) || (previousState
== NAV_STATE_CRUISE_ADJUSTING
)
1072 || posControl
.flags
.isAdjustingHeading
) {
1073 calculateFarAwayTarget(&posControl
.cruise
.targetPos
, posControl
.cruise
.yaw
, distance
);
1074 DEBUG_SET(DEBUG_CRUISE
, 2, 1);
1075 } else if (calculateDistanceToDestination(&posControl
.cruise
.targetPos
) <= (navConfig()->fw
.loiter_radius
* 1.10f
)) { //10% margin
1076 calculateNewCruiseTarget(&posControl
.cruise
.targetPos
, posControl
.cruise
.yaw
, distance
);
1077 DEBUG_SET(DEBUG_CRUISE
, 2, 2);
1080 setDesiredPosition(&posControl
.cruise
.targetPos
, posControl
.cruise
.yaw
, NAV_POS_UPDATE_XY
);
1082 return NAV_FSM_EVENT_NONE
;
1085 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_COURSE_HOLD_ADJUSTING(navigationFSMState_t previousState
)
1087 UNUSED(previousState
);
1088 DEBUG_SET(DEBUG_CRUISE
, 0, 3);
1090 // User is rolling, changing manually direction. Wait until it is done and then restore CRUISE
1091 if (posControl
.flags
.isAdjustingPosition
) {
1092 posControl
.cruise
.yaw
= posControl
.actualState
.yaw
; //store current heading
1093 posControl
.cruise
.lastYawAdjustmentTime
= millis();
1094 return NAV_FSM_EVENT_NONE
; // reprocess the state
1097 resetPositionController();
1099 return NAV_FSM_EVENT_SUCCESS
; // go back to the CRUISE_XD_IN_PROGRESS state
1102 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_CRUISE_INITIALIZE(navigationFSMState_t previousState
)
1104 if (!STATE(FIXED_WING_LEGACY
)) { return NAV_FSM_EVENT_ERROR
; } // only on FW for now
1106 navOnEnteringState_NAV_STATE_ALTHOLD_INITIALIZE(previousState
);
1108 return navOnEnteringState_NAV_STATE_COURSE_HOLD_INITIALIZE(previousState
);
1111 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_CRUISE_IN_PROGRESS(navigationFSMState_t previousState
)
1113 navOnEnteringState_NAV_STATE_ALTHOLD_IN_PROGRESS(previousState
);
1115 return navOnEnteringState_NAV_STATE_COURSE_HOLD_IN_PROGRESS(previousState
);
1118 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_CRUISE_ADJUSTING(navigationFSMState_t previousState
)
1120 navOnEnteringState_NAV_STATE_ALTHOLD_IN_PROGRESS(previousState
);
1122 return navOnEnteringState_NAV_STATE_COURSE_HOLD_ADJUSTING(previousState
);
1125 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_INITIALIZE(navigationFSMState_t previousState
)
1127 navigationFSMStateFlags_t prevFlags
= navGetStateFlags(previousState
);
1129 if ((posControl
.flags
.estHeadingStatus
== EST_NONE
) || (posControl
.flags
.estAltStatus
== EST_NONE
) || !STATE(GPS_FIX_HOME
)) {
1130 // Heading sensor, altitude sensor and HOME fix are mandatory for RTH. If not satisfied - switch to emergency landing
1131 // Relevant to failsafe forced RTH only. Switched RTH blocked in selectNavEventFromBoxModeInput if sensors unavailable.
1132 // If we are in dead-reckoning mode - also fail, since coordinates may be unreliable
1133 return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
;
1136 if (STATE(FIXED_WING_LEGACY
) && (posControl
.homeDistance
< navConfig()->general
.min_rth_distance
) && !posControl
.flags
.forcedRTHActivated
) {
1137 // Prevent RTH from activating on airplanes if too close to home unless it's a failsafe RTH
1138 return NAV_FSM_EVENT_SWITCH_TO_IDLE
;
1141 // If we have valid position sensor or configured to ignore it's loss at initial stage - continue
1142 if ((posControl
.flags
.estPosStatus
>= EST_USABLE
) || navConfig()->general
.flags
.rth_climb_ignore_emerg
) {
1143 // Reset altitude and position controllers if necessary
1144 if ((prevFlags
& NAV_CTL_POS
) == 0) {
1145 resetPositionController();
1148 // Reset altitude controller if it was not enabled or if we are in terrain follow mode
1149 if ((prevFlags
& NAV_CTL_ALT
) == 0 || posControl
.flags
.isTerrainFollowEnabled
) {
1150 // Make sure surface tracking is not enabled - RTH uses global altitude, not AGL
1151 resetAltitudeController(false);
1152 setupAltitudeController();
1155 // If close to home - reset home position and land
1156 if (posControl
.homeDistance
< navConfig()->general
.min_rth_distance
) {
1157 setHomePosition(&navGetCurrentActualPositionAndVelocity()->pos
, posControl
.actualState
.yaw
, NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_HEADING
, NAV_HOME_VALID_ALL
);
1158 setDesiredPosition(&navGetCurrentActualPositionAndVelocity()->pos
, posControl
.actualState
.yaw
, NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_Z
| NAV_POS_UPDATE_HEADING
);
1160 return NAV_FSM_EVENT_SWITCH_TO_RTH_LANDING
; // NAV_STATE_RTH_HOVER_PRIOR_TO_LANDING
1163 fpVector3_t targetHoldPos
;
1165 if (STATE(FIXED_WING_LEGACY
)) {
1166 // Airplane - climbout before heading home
1167 if (navConfig()->general
.flags
.rth_climb_first
== ON_FW_SPIRAL
) {
1168 // Spiral climb centered at xy of RTH activation
1169 calculateInitialHoldPosition(&targetHoldPos
);
1171 calculateFarAwayTarget(&targetHoldPos
, posControl
.actualState
.yaw
, 100000.0f
); // 1km away Linear climb
1174 // Multicopter, hover and climb
1175 calculateInitialHoldPosition(&targetHoldPos
);
1177 // Initialize RTH sanity check to prevent fly-aways on RTH
1178 // For airplanes this is delayed until climb-out is finished
1179 initializeRTHSanityChecker();
1182 setDesiredPosition(&targetHoldPos
, posControl
.actualState
.yaw
, NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_HEADING
);
1184 return NAV_FSM_EVENT_SUCCESS
; // NAV_STATE_RTH_CLIMB_TO_SAFE_ALT
1187 /* Position sensor failure timeout - land. Land immediately if failsafe RTH and timeout disabled (set to 0) */
1188 else if (checkForPositionSensorTimeout() || (!navConfig()->general
.pos_failure_timeout
&& posControl
.flags
.forcedRTHActivated
)) {
1189 return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
;
1191 /* No valid POS sensor but still within valid timeout - wait */
1192 return NAV_FSM_EVENT_NONE
;
1195 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_CLIMB_TO_SAFE_ALT(navigationFSMState_t previousState
)
1197 UNUSED(previousState
);
1199 if (!STATE(ALTITUDE_CONTROL
)) {
1200 //If altitude control is not a thing, switch to RTH in progress instead
1201 return NAV_FSM_EVENT_SUCCESS
; //Will cause NAV_STATE_RTH_HEAD_HOME
1204 rthAltControlStickOverrideCheck(PITCH
);
1206 /* Position sensor failure timeout and not configured to ignore GPS loss - land */
1207 if ((posControl
.flags
.estHeadingStatus
== EST_NONE
) ||
1208 (checkForPositionSensorTimeout() && !navConfig()->general
.flags
.rth_climb_ignore_emerg
)) {
1209 return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
;
1212 const uint8_t rthClimbMarginPercent
= STATE(FIXED_WING_LEGACY
) ? FW_RTH_CLIMB_MARGIN_PERCENT
: MR_RTH_CLIMB_MARGIN_PERCENT
;
1213 const float rthAltitudeMargin
= MAX(FW_RTH_CLIMB_MARGIN_MIN_CM
, (rthClimbMarginPercent
/100.0) * fabsf(posControl
.rthState
.rthInitialAltitude
- posControl
.rthState
.homePosition
.pos
.z
));
1215 // If we reached desired initial RTH altitude or we don't want to climb first
1216 if (((navGetCurrentActualPositionAndVelocity()->pos
.z
- posControl
.rthState
.rthInitialAltitude
) > -rthAltitudeMargin
) || (navConfig()->general
.flags
.rth_climb_first
== OFF
) || rthAltControlStickOverrideCheck(ROLL
) || rthClimbStageActiveAndComplete()) {
1218 // Delayed initialization for RTH sanity check on airplanes - allow to finish climb first as it can take some distance
1219 if (STATE(FIXED_WING_LEGACY
)) {
1220 initializeRTHSanityChecker();
1223 // Save initial home distance for future use
1224 posControl
.rthState
.rthInitialDistance
= posControl
.homeDistance
;
1225 fpVector3_t
* tmpHomePos
= rthGetHomeTargetPosition(RTH_HOME_ENROUTE_INITIAL
);
1227 if (navConfig()->general
.flags
.rth_tail_first
&& !STATE(FIXED_WING_LEGACY
)) {
1228 setDesiredPosition(tmpHomePos
, 0, NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_Z
| NAV_POS_UPDATE_BEARING_TAIL_FIRST
);
1231 setDesiredPosition(tmpHomePos
, 0, NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_Z
| NAV_POS_UPDATE_BEARING
);
1234 return NAV_FSM_EVENT_SUCCESS
; // NAV_STATE_RTH_HEAD_HOME
1238 fpVector3_t
* tmpHomePos
= rthGetHomeTargetPosition(RTH_HOME_ENROUTE_INITIAL
);
1240 /* For multi-rotors execute sanity check during initial ascent as well */
1241 if (!STATE(FIXED_WING_LEGACY
) && !validateRTHSanityChecker()) {
1242 return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
;
1245 // Climb to safe altitude and turn to correct direction
1246 if (STATE(FIXED_WING_LEGACY
)) {
1247 if (navConfig()->general
.flags
.rth_climb_first
== ON_FW_SPIRAL
) {
1248 float altitudeChangeDirection
= (tmpHomePos
->z
+= FW_RTH_CLIMB_OVERSHOOT_CM
) > navGetCurrentActualPositionAndVelocity()->pos
.z
? 1 : -1;
1249 updateClimbRateToAltitudeController(altitudeChangeDirection
* navConfig()->general
.max_auto_climb_rate
, ROC_TO_ALT_NORMAL
);
1251 tmpHomePos
->z
+= FW_RTH_CLIMB_OVERSHOOT_CM
;
1252 setDesiredPosition(tmpHomePos
, 0, NAV_POS_UPDATE_Z
);
1255 // Until the initial climb phase is complete target slightly *above* the cruise altitude to ensure we actually reach
1256 // it in a reasonable time. Immediately after we finish this phase - target the original altitude.
1257 tmpHomePos
->z
+= MR_RTH_CLIMB_OVERSHOOT_CM
;
1259 if (navConfig()->general
.flags
.rth_tail_first
) {
1260 setDesiredPosition(tmpHomePos
, 0, NAV_POS_UPDATE_Z
| NAV_POS_UPDATE_BEARING_TAIL_FIRST
);
1262 setDesiredPosition(tmpHomePos
, 0, NAV_POS_UPDATE_Z
| NAV_POS_UPDATE_BEARING
);
1266 return NAV_FSM_EVENT_NONE
;
1270 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_HEAD_HOME(navigationFSMState_t previousState
)
1272 UNUSED(previousState
);
1274 rthAltControlStickOverrideCheck(PITCH
);
1276 /* If position sensors unavailable - land immediately */
1277 if ((posControl
.flags
.estHeadingStatus
== EST_NONE
) || !validateRTHSanityChecker()) {
1278 return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
;
1281 // If we have position sensor - continue home
1282 if ((posControl
.flags
.estPosStatus
>= EST_USABLE
)) {
1283 fpVector3_t
* tmpHomePos
= rthGetHomeTargetPosition(RTH_HOME_ENROUTE_PROPORTIONAL
);
1285 if (isWaypointPositionReached(tmpHomePos
, true)) {
1286 // Successfully reached position target - update XYZ-position
1287 setDesiredPosition(tmpHomePos
, posControl
.rthState
.homePosition
.yaw
, NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_Z
| NAV_POS_UPDATE_HEADING
);
1288 return NAV_FSM_EVENT_SUCCESS
; // NAV_STATE_RTH_HOVER_PRIOR_TO_LANDING
1290 setDesiredPosition(tmpHomePos
, 0, NAV_POS_UPDATE_Z
| NAV_POS_UPDATE_XY
);
1291 return NAV_FSM_EVENT_NONE
;
1294 /* Position sensor failure timeout - land */
1295 else if (checkForPositionSensorTimeout()) {
1296 return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
;
1298 /* No valid POS sensor but still within valid timeout - wait */
1299 return NAV_FSM_EVENT_NONE
;
1302 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_HOVER_PRIOR_TO_LANDING(navigationFSMState_t previousState
)
1304 UNUSED(previousState
);
1306 //On ROVER and BOAT we immediately switch to the next event
1307 if (!STATE(ALTITUDE_CONTROL
)) {
1308 return NAV_FSM_EVENT_SUCCESS
;
1311 /* If position sensors unavailable - land immediately (wait for timeout on GPS) */
1312 if ((posControl
.flags
.estHeadingStatus
== EST_NONE
) || checkForPositionSensorTimeout() || !validateRTHSanityChecker()) {
1313 return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
;
1316 // If position ok OR within valid timeout - continue
1317 // Wait until target heading is reached for MR (with 15 deg margin for error), or continue for Fixed Wing
1318 if ((ABS(wrap_18000(posControl
.rthState
.homePosition
.yaw
- posControl
.actualState
.yaw
)) < DEGREES_TO_CENTIDEGREES(15)) || STATE(FIXED_WING_LEGACY
)) {
1319 resetLandingDetector();
1320 updateClimbRateToAltitudeController(0, ROC_TO_ALT_RESET
);
1321 return navigationRTHAllowsLanding() ? NAV_FSM_EVENT_SUCCESS
: NAV_FSM_EVENT_SWITCH_TO_RTH_HOVER_ABOVE_HOME
; // success = land
1323 fpVector3_t
* tmpHomePos
= rthGetHomeTargetPosition(RTH_HOME_ENROUTE_FINAL
);
1324 setDesiredPosition(tmpHomePos
, posControl
.rthState
.homePosition
.yaw
, NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_Z
| NAV_POS_UPDATE_HEADING
);
1325 return NAV_FSM_EVENT_NONE
;
1329 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_HOVER_ABOVE_HOME(navigationFSMState_t previousState
)
1331 UNUSED(previousState
);
1333 /* If position sensors unavailable - land immediately (wait for timeout on GPS) */
1334 if (posControl
.flags
.estHeadingStatus
== EST_NONE
|| checkForPositionSensorTimeout() || !validateRTHSanityChecker()) {
1335 return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
;
1338 fpVector3_t
* tmpHomePos
= rthGetHomeTargetPosition(RTH_HOME_FINAL_HOVER
);
1340 if (navConfig()->general
.rth_home_altitude
) {
1341 float timeToReachHomeAltitude
= fabsf(tmpHomePos
->z
- navGetCurrentActualPositionAndVelocity()->pos
.z
) / navConfig()->general
.max_auto_climb_rate
;
1343 if (timeToReachHomeAltitude
< 1) {
1344 // we almost reached the target home altitude so set the desired altitude to the desired home altitude
1345 setDesiredPosition(tmpHomePos
, 0, NAV_POS_UPDATE_Z
);
1347 float altitudeChangeDirection
= tmpHomePos
->z
> navGetCurrentActualPositionAndVelocity()->pos
.z
? 1 : -1;
1348 updateClimbRateToAltitudeController(altitudeChangeDirection
* navConfig()->general
.max_auto_climb_rate
, ROC_TO_ALT_NORMAL
);
1352 setDesiredPosition(tmpHomePos
, 0, NAV_POS_UPDATE_Z
);
1355 return NAV_FSM_EVENT_NONE
;
1358 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_LANDING(navigationFSMState_t previousState
)
1360 UNUSED(previousState
);
1362 //On ROVER and BOAT we immediately switch to the next event
1363 if (!STATE(ALTITUDE_CONTROL
)) {
1364 return NAV_FSM_EVENT_SUCCESS
;
1367 if (!ARMING_FLAG(ARMED
) || isLandingDetected()) {
1368 return NAV_FSM_EVENT_SUCCESS
;
1371 /* If position sensors unavailable - land immediately (wait for timeout on GPS)
1372 * Continue to check for RTH sanity during landing */
1373 if (posControl
.flags
.estHeadingStatus
== EST_NONE
|| checkForPositionSensorTimeout() || !validateRTHSanityChecker()) {
1374 return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
;
1377 float descentVelLimited
= 0;
1379 // A safeguard - if surface altitude sensors is available and it is reading < 50cm altitude - drop to low descend speed
1380 if ((posControl
.flags
.estAglStatus
== EST_TRUSTED
) && posControl
.actualState
.agl
.pos
.z
< 50.0f
) {
1381 // land_descent_rate == 200 : descend speed = 30 cm/s, gentle touchdown
1382 // Do not allow descent velocity slower than -30cm/s so the landing detector works.
1383 descentVelLimited
= navConfig()->general
.land_minalt_vspd
;
1385 // Ramp down descent velocity from 100% at maxAlt altitude to 25% from minAlt to 0cm.
1386 float descentVelScaled
= scaleRangef(navGetCurrentActualPositionAndVelocity()->pos
.z
,
1387 navConfig()->general
.land_slowdown_minalt
, navConfig()->general
.land_slowdown_maxalt
,
1388 navConfig()->general
.land_minalt_vspd
, navConfig()->general
.land_maxalt_vspd
);
1390 descentVelLimited
= constrainf(descentVelScaled
, navConfig()->general
.land_minalt_vspd
, navConfig()->general
.land_maxalt_vspd
);
1393 updateClimbRateToAltitudeController(-descentVelLimited
, ROC_TO_ALT_NORMAL
);
1395 return NAV_FSM_EVENT_NONE
;
1398 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_FINISHING(navigationFSMState_t previousState
)
1400 UNUSED(previousState
);
1402 //On ROVER and BOAT disarm immediately
1403 if (!STATE(ALTITUDE_CONTROL
) || navConfig()->general
.flags
.disarm_on_landing
) {
1404 disarm(DISARM_NAVIGATION
);
1407 return NAV_FSM_EVENT_SUCCESS
;
1410 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_FINISHED(navigationFSMState_t previousState
)
1412 // Stay in this state
1413 UNUSED(previousState
);
1415 if (STATE(ALTITUDE_CONTROL
)) {
1416 updateClimbRateToAltitudeController(-1.1f
* navConfig()->general
.land_minalt_vspd
, ROC_TO_ALT_NORMAL
); // FIXME
1419 // Prevent I-terms growing when already landed
1420 pidResetErrorAccumulators();
1421 return NAV_FSM_EVENT_NONE
;
1424 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_WAYPOINT_INITIALIZE(navigationFSMState_t previousState
)
1426 UNUSED(previousState
);
1428 if (posControl
.waypointCount
== 0 || !posControl
.waypointListValid
) {
1429 return NAV_FSM_EVENT_ERROR
;
1432 // Prepare controllers
1433 resetPositionController();
1435 // Make sure surface tracking is not enabled - RTH uses global altitude, not AGL
1436 resetAltitudeController(false);
1437 setupAltitudeController();
1439 Use p3 as the volatile jump counter, allowing embedded, rearmed jumps
1440 Using p3 minimises the risk of saving an invalid counter if a mission is aborted.
1442 if (posControl
.activeWaypointIndex
== 0 || posControl
.wpMissionRestart
) {
1443 setupJumpCounters();
1444 posControl
.activeWaypointIndex
= 0;
1445 wpHeadingControl
.mode
= NAV_WP_HEAD_MODE_NONE
;
1448 if (navConfig()->general
.flags
.waypoint_mission_restart
== WP_MISSION_SWITCH
) {
1449 posControl
.wpMissionRestart
= posControl
.activeWaypointIndex
? !posControl
.wpMissionRestart
: false;
1451 posControl
.wpMissionRestart
= navConfig()->general
.flags
.waypoint_mission_restart
== WP_MISSION_START
;
1454 return NAV_FSM_EVENT_SUCCESS
; // will switch to NAV_STATE_WAYPOINT_PRE_ACTION
1458 static navigationFSMEvent_t
nextForNonGeoStates(void)
1460 /* simple helper for non-geographical states that just set other data */
1461 const bool isLastWaypoint
= (posControl
.waypointList
[posControl
.activeWaypointIndex
].flag
== NAV_WP_FLAG_LAST
) || (posControl
.activeWaypointIndex
>= (posControl
.waypointCount
- 1));
1463 if (isLastWaypoint
) {
1464 // non-geo state is the last waypoint, switch to finish.
1465 return NAV_FSM_EVENT_SWITCH_TO_WAYPOINT_FINISHED
;
1467 // Finished non-geo, move to next WP
1468 posControl
.activeWaypointIndex
++;
1469 return NAV_FSM_EVENT_NONE
; // re-process the state passing to the next WP
1473 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_WAYPOINT_PRE_ACTION(navigationFSMState_t previousState
)
1475 /* A helper function to do waypoint-specific action */
1476 UNUSED(previousState
);
1478 switch ((navWaypointActions_e
)posControl
.waypointList
[posControl
.activeWaypointIndex
].action
) {
1479 case NAV_WP_ACTION_HOLD_TIME
:
1480 case NAV_WP_ACTION_WAYPOINT
:
1481 case NAV_WP_ACTION_LAND
:
1482 calculateAndSetActiveWaypoint(&posControl
.waypointList
[posControl
.activeWaypointIndex
]);
1483 posControl
.wpInitialDistance
= calculateDistanceToDestination(&posControl
.activeWaypoint
.pos
);
1484 posControl
.wpInitialAltitude
= posControl
.actualState
.abs
.pos
.z
;
1485 return NAV_FSM_EVENT_SUCCESS
; // will switch to NAV_STATE_WAYPOINT_IN_PROGRESS
1487 // We use p3 as the volatile jump counter (p2 is the static value)
1488 case NAV_WP_ACTION_JUMP
:
1489 if(posControl
.waypointList
[posControl
.activeWaypointIndex
].p3
!= -1){
1490 if(posControl
.waypointList
[posControl
.activeWaypointIndex
].p3
== 0){
1492 return nextForNonGeoStates();
1496 posControl
.waypointList
[posControl
.activeWaypointIndex
].p3
--;
1499 posControl
.activeWaypointIndex
= posControl
.waypointList
[posControl
.activeWaypointIndex
].p1
;
1500 return NAV_FSM_EVENT_NONE
; // re-process the state passing to the next WP
1502 case NAV_WP_ACTION_SET_POI
:
1503 if (STATE(MULTIROTOR
)) {
1504 wpHeadingControl
.mode
= NAV_WP_HEAD_MODE_POI
;
1505 mapWaypointToLocalPosition(&wpHeadingControl
.poi_pos
,
1506 &posControl
.waypointList
[posControl
.activeWaypointIndex
], GEO_ALT_RELATIVE
);
1508 return nextForNonGeoStates();
1510 case NAV_WP_ACTION_SET_HEAD
:
1511 if (STATE(MULTIROTOR
)) {
1512 if (posControl
.waypointList
[posControl
.activeWaypointIndex
].p1
< 0 ||
1513 posControl
.waypointList
[posControl
.activeWaypointIndex
].p1
> 359) {
1514 wpHeadingControl
.mode
= NAV_WP_HEAD_MODE_NONE
;
1516 wpHeadingControl
.mode
= NAV_WP_HEAD_MODE_FIXED
;
1517 wpHeadingControl
.heading
= DEGREES_TO_CENTIDEGREES(posControl
.waypointList
[posControl
.activeWaypointIndex
].p1
);
1520 return nextForNonGeoStates();
1522 case NAV_WP_ACTION_RTH
:
1523 posControl
.wpMissionRestart
= true;
1524 return NAV_FSM_EVENT_SWITCH_TO_RTH
;
1530 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_WAYPOINT_IN_PROGRESS(navigationFSMState_t previousState
)
1532 UNUSED(previousState
);
1534 // If no position sensor available - land immediately
1535 if ((posControl
.flags
.estPosStatus
>= EST_USABLE
) && (posControl
.flags
.estHeadingStatus
>= EST_USABLE
)) {
1536 switch ((navWaypointActions_e
)posControl
.waypointList
[posControl
.activeWaypointIndex
].action
) {
1537 case NAV_WP_ACTION_HOLD_TIME
:
1538 case NAV_WP_ACTION_WAYPOINT
:
1539 case NAV_WP_ACTION_LAND
:
1540 if (isWaypointReached(&posControl
.activeWaypoint
, false) || isWaypointMissed(&posControl
.activeWaypoint
)) {
1541 return NAV_FSM_EVENT_SUCCESS
; // will switch to NAV_STATE_WAYPOINT_REACHED
1544 fpVector3_t tmpWaypoint
;
1545 tmpWaypoint
.x
= posControl
.activeWaypoint
.pos
.x
;
1546 tmpWaypoint
.y
= posControl
.activeWaypoint
.pos
.y
;
1547 tmpWaypoint
.z
= scaleRangef(constrainf(posControl
.wpDistance
, posControl
.wpInitialDistance
/ 10.0f
, posControl
.wpInitialDistance
),
1548 posControl
.wpInitialDistance
, posControl
.wpInitialDistance
/ 10.0f
,
1549 posControl
.wpInitialAltitude
, posControl
.activeWaypoint
.pos
.z
);
1550 setDesiredPosition(&tmpWaypoint
, 0, NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_Z
| NAV_POS_UPDATE_BEARING
);
1551 if(STATE(MULTIROTOR
)) {
1552 switch (wpHeadingControl
.mode
) {
1553 case NAV_WP_HEAD_MODE_NONE
:
1555 case NAV_WP_HEAD_MODE_FIXED
:
1556 setDesiredPosition(NULL
, wpHeadingControl
.heading
, NAV_POS_UPDATE_HEADING
);
1558 case NAV_WP_HEAD_MODE_POI
:
1559 setDesiredPosition(&wpHeadingControl
.poi_pos
, 0, NAV_POS_UPDATE_BEARING
);
1563 return NAV_FSM_EVENT_NONE
; // will re-process state in >10ms
1567 case NAV_WP_ACTION_JUMP
:
1568 case NAV_WP_ACTION_SET_HEAD
:
1569 case NAV_WP_ACTION_SET_POI
:
1570 case NAV_WP_ACTION_RTH
:
1574 /* If position sensors unavailable - land immediately (wait for timeout on GPS) */
1575 else if (checkForPositionSensorTimeout() || (posControl
.flags
.estHeadingStatus
== EST_NONE
)) {
1576 return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
;
1579 return NAV_FSM_EVENT_NONE
; // will re-process state in >10ms
1582 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_WAYPOINT_REACHED(navigationFSMState_t previousState
)
1584 UNUSED(previousState
);
1586 switch ((navWaypointActions_e
)posControl
.waypointList
[posControl
.activeWaypointIndex
].action
) {
1587 case NAV_WP_ACTION_WAYPOINT
:
1588 return NAV_FSM_EVENT_SUCCESS
; // NAV_STATE_WAYPOINT_NEXT
1590 case NAV_WP_ACTION_JUMP
:
1591 case NAV_WP_ACTION_SET_HEAD
:
1592 case NAV_WP_ACTION_SET_POI
:
1593 case NAV_WP_ACTION_RTH
:
1596 case NAV_WP_ACTION_LAND
:
1597 return NAV_FSM_EVENT_SWITCH_TO_WAYPOINT_RTH_LAND
;
1599 case NAV_WP_ACTION_HOLD_TIME
:
1600 // Save the current time for the time the waypoint was reached
1601 posControl
.wpReachedTime
= millis();
1602 return NAV_FSM_EVENT_SWITCH_TO_WAYPOINT_HOLD_TIME
;
1608 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_WAYPOINT_HOLD_TIME(navigationFSMState_t previousState
)
1610 UNUSED(previousState
);
1612 /* If position sensors unavailable - land immediately (wait for timeout on GPS) */
1613 if (posControl
.flags
.estHeadingStatus
== EST_NONE
|| checkForPositionSensorTimeout()) {
1614 return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
;
1617 timeMs_t currentTime
= millis();
1619 if (posControl
.waypointList
[posControl
.activeWaypointIndex
].p1
<= 0 ||
1620 (posControl
.wpReachedTime
!= 0 && currentTime
- posControl
.wpReachedTime
>= (timeMs_t
)posControl
.waypointList
[posControl
.activeWaypointIndex
].p1
*1000L)) {
1621 return NAV_FSM_EVENT_SUCCESS
;
1625 return NAV_FSM_EVENT_NONE
; // will re-process state in >10ms
1628 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_WAYPOINT_RTH_LAND(navigationFSMState_t previousState
)
1630 UNUSED(previousState
);
1632 const navigationFSMEvent_t landEvent
= navOnEnteringState_NAV_STATE_RTH_LANDING(previousState
);
1633 if (landEvent
== NAV_FSM_EVENT_SUCCESS
) {
1634 // Landing controller returned success - invoke RTH finishing state and finish the waypoint
1635 navOnEnteringState_NAV_STATE_RTH_FINISHING(previousState
);
1636 return NAV_FSM_EVENT_SUCCESS
;
1639 return NAV_FSM_EVENT_NONE
;
1643 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_WAYPOINT_NEXT(navigationFSMState_t previousState
)
1645 UNUSED(previousState
);
1647 if (isLastMissionWaypoint()) { // Last waypoint reached
1648 return NAV_FSM_EVENT_SWITCH_TO_WAYPOINT_FINISHED
;
1651 // Waypoint reached, do something and move on to next waypoint
1652 posControl
.activeWaypointIndex
++;
1653 return NAV_FSM_EVENT_SUCCESS
; // will switch to NAV_STATE_WAYPOINT_PRE_ACTION
1657 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_WAYPOINT_FINISHED(navigationFSMState_t previousState
)
1659 UNUSED(previousState
);
1661 clearJumpCounters();
1662 posControl
.wpMissionRestart
= true;
1664 /* If position sensors unavailable - land immediately (wait for timeout on GPS) */
1665 if (posControl
.flags
.estHeadingStatus
== EST_NONE
|| checkForPositionSensorTimeout()) {
1666 return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
;
1669 return NAV_FSM_EVENT_NONE
; // will re-process state in >10ms
1672 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_EMERGENCY_LANDING_INITIALIZE(navigationFSMState_t previousState
)
1675 UNUSED(previousState
);
1677 // Emergency landing MAY use common altitude controller if vertical position is valid - initialize it
1678 // Make sure terrain following is not enabled
1679 resetAltitudeController(false);
1681 return NAV_FSM_EVENT_SUCCESS
;
1684 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_EMERGENCY_LANDING_IN_PROGRESS(navigationFSMState_t previousState
)
1687 UNUSED(previousState
);
1688 return NAV_FSM_EVENT_NONE
;
1691 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_EMERGENCY_LANDING_FINISHED(navigationFSMState_t previousState
)
1694 UNUSED(previousState
);
1696 // Prevent I-terms growing when already landed
1697 pidResetErrorAccumulators();
1699 return NAV_FSM_EVENT_SUCCESS
;
1702 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_LAUNCH_INITIALIZE(navigationFSMState_t previousState
)
1704 const timeUs_t currentTimeUs
= micros();
1705 UNUSED(previousState
);
1707 resetFixedWingLaunchController(currentTimeUs
);
1709 return NAV_FSM_EVENT_SUCCESS
; // NAV_STATE_LAUNCH_WAIT
1712 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_LAUNCH_WAIT(navigationFSMState_t previousState
)
1714 const timeUs_t currentTimeUs
= micros();
1715 UNUSED(previousState
);
1717 if (fixedWingLaunchStatus() == FW_LAUNCH_DETECTED
) {
1718 enableFixedWingLaunchController(currentTimeUs
);
1719 return NAV_FSM_EVENT_SUCCESS
; // NAV_STATE_LAUNCH_IN_PROGRESS
1722 //allow to leave NAV_LAUNCH_MODE if it has being enabled as feature by moving sticks with low throttle.
1723 if (feature(FEATURE_FW_LAUNCH
)) {
1724 throttleStatus_e throttleStatus
= calculateThrottleStatus(THROTTLE_STATUS_TYPE_RC
);
1725 if ((throttleStatus
== THROTTLE_LOW
) && (isRollPitchStickDeflected())) {
1726 abortFixedWingLaunch();
1727 return NAV_FSM_EVENT_SWITCH_TO_IDLE
;
1731 return NAV_FSM_EVENT_NONE
;
1734 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_LAUNCH_IN_PROGRESS(navigationFSMState_t previousState
)
1736 UNUSED(previousState
);
1738 if (fixedWingLaunchStatus() >= FW_LAUNCH_ABORTED
) {
1739 return NAV_FSM_EVENT_SUCCESS
;
1742 return NAV_FSM_EVENT_NONE
;
1745 static navigationFSMState_t
navSetNewFSMState(navigationFSMState_t newState
)
1747 navigationFSMState_t previousState
;
1749 previousState
= posControl
.navState
;
1750 if (posControl
.navState
!= newState
) {
1751 posControl
.navState
= newState
;
1752 posControl
.navPersistentId
= navFSM
[newState
].persistentId
;
1754 return previousState
;
1757 static void navProcessFSMEvents(navigationFSMEvent_t injectedEvent
)
1759 const timeMs_t currentMillis
= millis();
1760 navigationFSMState_t previousState
;
1761 static timeMs_t lastStateProcessTime
= 0;
1763 /* Process new injected event if event defined,
1764 * otherwise process timeout event if defined */
1765 if (injectedEvent
!= NAV_FSM_EVENT_NONE
&& navFSM
[posControl
.navState
].onEvent
[injectedEvent
] != NAV_STATE_UNDEFINED
) {
1767 previousState
= navSetNewFSMState(navFSM
[posControl
.navState
].onEvent
[injectedEvent
]);
1768 } else if ((navFSM
[posControl
.navState
].timeoutMs
> 0) && (navFSM
[posControl
.navState
].onEvent
[NAV_FSM_EVENT_TIMEOUT
] != NAV_STATE_UNDEFINED
) &&
1769 ((currentMillis
- lastStateProcessTime
) >= navFSM
[posControl
.navState
].timeoutMs
)) {
1771 previousState
= navSetNewFSMState(navFSM
[posControl
.navState
].onEvent
[NAV_FSM_EVENT_TIMEOUT
]);
1774 if (previousState
) { /* If state updated call new state's entry function */
1775 while (navFSM
[posControl
.navState
].onEntry
) {
1776 navigationFSMEvent_t newEvent
= navFSM
[posControl
.navState
].onEntry(previousState
);
1778 if ((newEvent
!= NAV_FSM_EVENT_NONE
) && (navFSM
[posControl
.navState
].onEvent
[newEvent
] != NAV_STATE_UNDEFINED
)) {
1779 previousState
= navSetNewFSMState(navFSM
[posControl
.navState
].onEvent
[newEvent
]);
1786 lastStateProcessTime
= currentMillis
;
1789 /* Update public system state information */
1790 NAV_Status
.mode
= MW_GPS_MODE_NONE
;
1792 if (ARMING_FLAG(ARMED
)) {
1793 navigationFSMStateFlags_t navStateFlags
= navGetStateFlags(posControl
.navState
);
1795 if (navStateFlags
& NAV_AUTO_RTH
) {
1796 NAV_Status
.mode
= MW_GPS_MODE_RTH
;
1798 else if (navStateFlags
& NAV_AUTO_WP
) {
1799 NAV_Status
.mode
= MW_GPS_MODE_NAV
;
1801 else if (navStateFlags
& NAV_CTL_EMERG
) {
1802 NAV_Status
.mode
= MW_GPS_MODE_EMERG
;
1804 else if (navStateFlags
& NAV_CTL_POS
) {
1805 NAV_Status
.mode
= MW_GPS_MODE_HOLD
;
1809 NAV_Status
.state
= navFSM
[posControl
.navState
].mwState
;
1810 NAV_Status
.error
= navFSM
[posControl
.navState
].mwError
;
1812 NAV_Status
.flags
= 0;
1813 if (posControl
.flags
.isAdjustingPosition
) NAV_Status
.flags
|= MW_NAV_FLAG_ADJUSTING_POSITION
;
1814 if (posControl
.flags
.isAdjustingAltitude
) NAV_Status
.flags
|= MW_NAV_FLAG_ADJUSTING_ALTITUDE
;
1816 NAV_Status
.activeWpNumber
= posControl
.activeWaypointIndex
+ 1;
1817 NAV_Status
.activeWpAction
= 0;
1818 if ((posControl
.activeWaypointIndex
>= 0) && (posControl
.activeWaypointIndex
< NAV_MAX_WAYPOINTS
)) {
1819 NAV_Status
.activeWpAction
= posControl
.waypointList
[posControl
.activeWaypointIndex
].action
;
1823 static fpVector3_t
* rthGetHomeTargetPosition(rthTargetMode_e mode
)
1825 posControl
.rthState
.homeTmpWaypoint
= posControl
.rthState
.homePosition
.pos
;
1828 case RTH_HOME_ENROUTE_INITIAL
:
1829 posControl
.rthState
.homeTmpWaypoint
.z
= posControl
.rthState
.rthInitialAltitude
;
1832 case RTH_HOME_ENROUTE_PROPORTIONAL
:
1834 float rthTotalDistanceToTravel
= posControl
.rthState
.rthInitialDistance
- (STATE(FIXED_WING_LEGACY
) ? navConfig()->fw
.loiter_radius
: 0);
1835 if (rthTotalDistanceToTravel
>= 100) {
1836 float ratioNotTravelled
= constrainf(posControl
.homeDistance
/ rthTotalDistanceToTravel
, 0.0f
, 1.0f
);
1837 posControl
.rthState
.homeTmpWaypoint
.z
= (posControl
.rthState
.rthInitialAltitude
* ratioNotTravelled
) + (posControl
.rthState
.rthFinalAltitude
* (1.0f
- ratioNotTravelled
));
1840 posControl
.rthState
.homeTmpWaypoint
.z
= posControl
.rthState
.rthFinalAltitude
;
1845 case RTH_HOME_ENROUTE_FINAL
:
1846 posControl
.rthState
.homeTmpWaypoint
.z
= posControl
.rthState
.rthFinalAltitude
;
1849 case RTH_HOME_FINAL_HOVER
:
1850 if (navConfig()->general
.rth_home_altitude
) {
1851 posControl
.rthState
.homeTmpWaypoint
.z
= posControl
.rthState
.homePosition
.pos
.z
+ navConfig()->general
.rth_home_altitude
;
1854 // If home altitude not defined - fall back to final ENROUTE altitude
1855 posControl
.rthState
.homeTmpWaypoint
.z
= posControl
.rthState
.rthFinalAltitude
;
1859 case RTH_HOME_FINAL_LAND
:
1860 // if WP mission p2 > 0 use p2 value as landing elevation (in meters !) (otherwise default to takeoff home elevation)
1861 if (FLIGHT_MODE(NAV_WP_MODE
) && posControl
.waypointList
[posControl
.activeWaypointIndex
].action
== NAV_WP_ACTION_LAND
&& posControl
.waypointList
[posControl
.activeWaypointIndex
].p2
!= 0) {
1862 posControl
.rthState
.homeTmpWaypoint
.z
= posControl
.waypointList
[posControl
.activeWaypointIndex
].p2
* 100; // 100 -> m to cm
1863 if (waypointMissionAltConvMode(posControl
.waypointList
[posControl
.activeWaypointIndex
].p3
) == GEO_ALT_ABSOLUTE
) {
1864 posControl
.rthState
.homeTmpWaypoint
.z
-= posControl
.gpsOrigin
.alt
; // correct to relative if absolute SL altitude datum used
1870 return &posControl
.rthState
.homeTmpWaypoint
;
1873 /*-----------------------------------------------------------
1874 * Detects if thrust vector is facing downwards
1875 *-----------------------------------------------------------*/
1876 bool isThrustFacingDownwards(void)
1878 // Tilt angle <= 80 deg; cos(80) = 0.17364817766693034885171662676931
1879 return (calculateCosTiltAngle() >= 0.173648178f
);
1882 /*-----------------------------------------------------------
1883 * Checks if position sensor (GPS) is failing for a specified timeout (if enabled)
1884 *-----------------------------------------------------------*/
1885 bool checkForPositionSensorTimeout(void)
1887 if (navConfig()->general
.pos_failure_timeout
) {
1888 if ((posControl
.flags
.estPosStatus
== EST_NONE
) && ((millis() - posControl
.lastValidPositionTimeMs
) > (1000 * navConfig()->general
.pos_failure_timeout
))) {
1896 // Timeout not defined, never fail
1901 /*-----------------------------------------------------------
1902 * Processes an update to XY-position and velocity
1903 *-----------------------------------------------------------*/
1904 void updateActualHorizontalPositionAndVelocity(bool estPosValid
, bool estVelValid
, float newX
, float newY
, float newVelX
, float newVelY
)
1906 posControl
.actualState
.abs
.pos
.x
= newX
;
1907 posControl
.actualState
.abs
.pos
.y
= newY
;
1908 posControl
.actualState
.abs
.vel
.x
= newVelX
;
1909 posControl
.actualState
.abs
.vel
.y
= newVelY
;
1911 posControl
.actualState
.agl
.pos
.x
= newX
;
1912 posControl
.actualState
.agl
.pos
.y
= newY
;
1913 posControl
.actualState
.agl
.vel
.x
= newVelX
;
1914 posControl
.actualState
.agl
.vel
.y
= newVelY
;
1916 posControl
.actualState
.velXY
= fast_fsqrtf(sq(newVelX
) + sq(newVelY
));
1918 // CASE 1: POS & VEL valid
1919 if (estPosValid
&& estVelValid
) {
1920 posControl
.flags
.estPosStatus
= EST_TRUSTED
;
1921 posControl
.flags
.estVelStatus
= EST_TRUSTED
;
1922 posControl
.flags
.horizontalPositionDataNew
= true;
1923 posControl
.lastValidPositionTimeMs
= millis();
1925 // CASE 1: POS invalid, VEL valid
1926 else if (!estPosValid
&& estVelValid
) {
1927 posControl
.flags
.estPosStatus
= EST_USABLE
; // Pos usable, but not trusted
1928 posControl
.flags
.estVelStatus
= EST_TRUSTED
;
1929 posControl
.flags
.horizontalPositionDataNew
= true;
1930 posControl
.lastValidPositionTimeMs
= millis();
1932 // CASE 3: can't use pos/vel data
1934 posControl
.flags
.estPosStatus
= EST_NONE
;
1935 posControl
.flags
.estVelStatus
= EST_NONE
;
1936 posControl
.flags
.horizontalPositionDataNew
= false;
1939 //Update blackbox data
1940 navLatestActualPosition
[X
] = newX
;
1941 navLatestActualPosition
[Y
] = newY
;
1942 navActualVelocity
[X
] = constrain(newVelX
, -32678, 32767);
1943 navActualVelocity
[Y
] = constrain(newVelY
, -32678, 32767);
1946 /*-----------------------------------------------------------
1947 * Processes an update to Z-position and velocity
1948 *-----------------------------------------------------------*/
1949 void updateActualAltitudeAndClimbRate(bool estimateValid
, float newAltitude
, float newVelocity
, float surfaceDistance
, float surfaceVelocity
, navigationEstimateStatus_e surfaceStatus
)
1951 posControl
.actualState
.abs
.pos
.z
= newAltitude
;
1952 posControl
.actualState
.abs
.vel
.z
= newVelocity
;
1954 posControl
.actualState
.agl
.pos
.z
= surfaceDistance
;
1955 posControl
.actualState
.agl
.vel
.z
= surfaceVelocity
;
1957 // Update altitude that would be used when executing RTH
1958 if (estimateValid
) {
1959 updateDesiredRTHAltitude();
1961 // If we acquired new surface reference - changing from NONE/USABLE -> TRUSTED
1962 if ((surfaceStatus
== EST_TRUSTED
) && (posControl
.flags
.estAglStatus
!= EST_TRUSTED
)) {
1963 // If we are in terrain-following modes - signal that we should update the surface tracking setpoint
1964 // NONE/USABLE means that we were flying blind, now we should lock to surface
1965 //updateSurfaceTrackingSetpoint();
1968 posControl
.flags
.estAglStatus
= surfaceStatus
; // Could be TRUSTED or USABLE
1969 posControl
.flags
.estAltStatus
= EST_TRUSTED
;
1970 posControl
.flags
.verticalPositionDataNew
= true;
1971 posControl
.lastValidAltitudeTimeMs
= millis();
1974 posControl
.flags
.estAltStatus
= EST_NONE
;
1975 posControl
.flags
.estAglStatus
= EST_NONE
;
1976 posControl
.flags
.verticalPositionDataNew
= false;
1979 if (ARMING_FLAG(ARMED
)) {
1980 if ((posControl
.flags
.estAglStatus
== EST_TRUSTED
) && surfaceDistance
> 0) {
1981 if (posControl
.actualState
.surfaceMin
> 0) {
1982 posControl
.actualState
.surfaceMin
= MIN(posControl
.actualState
.surfaceMin
, surfaceDistance
);
1985 posControl
.actualState
.surfaceMin
= surfaceDistance
;
1990 posControl
.actualState
.surfaceMin
= -1;
1993 //Update blackbox data
1994 navLatestActualPosition
[Z
] = navGetCurrentActualPositionAndVelocity()->pos
.z
;
1995 navActualVelocity
[Z
] = constrain(navGetCurrentActualPositionAndVelocity()->vel
.z
, -32678, 32767);
1998 /*-----------------------------------------------------------
1999 * Processes an update to estimated heading
2000 *-----------------------------------------------------------*/
2001 void updateActualHeading(bool headingValid
, int32_t newHeading
)
2003 /* Update heading. Check if we're acquiring a valid heading for the
2004 * first time and update home heading accordingly.
2006 navigationEstimateStatus_e newEstHeading
= headingValid
? EST_TRUSTED
: EST_NONE
;
2007 if (newEstHeading
>= EST_USABLE
&& posControl
.flags
.estHeadingStatus
< EST_USABLE
&&
2008 (posControl
.rthState
.homeFlags
& (NAV_HOME_VALID_XY
| NAV_HOME_VALID_Z
)) &&
2009 (posControl
.rthState
.homeFlags
& NAV_HOME_VALID_HEADING
) == 0) {
2011 // Home was stored using the fake heading (assuming boot as 0deg). Calculate
2012 // the offset from the fake to the actual yaw and apply the same rotation
2013 // to the home point.
2014 int32_t fakeToRealYawOffset
= newHeading
- posControl
.actualState
.yaw
;
2016 posControl
.rthState
.homePosition
.yaw
+= fakeToRealYawOffset
;
2017 if (posControl
.rthState
.homePosition
.yaw
< 0) {
2018 posControl
.rthState
.homePosition
.yaw
+= DEGREES_TO_CENTIDEGREES(360);
2020 if (posControl
.rthState
.homePosition
.yaw
>= DEGREES_TO_CENTIDEGREES(360)) {
2021 posControl
.rthState
.homePosition
.yaw
-= DEGREES_TO_CENTIDEGREES(360);
2023 posControl
.rthState
.homeFlags
|= NAV_HOME_VALID_HEADING
;
2025 posControl
.actualState
.yaw
= newHeading
;
2026 posControl
.flags
.estHeadingStatus
= newEstHeading
;
2028 /* Precompute sin/cos of yaw angle */
2029 posControl
.actualState
.sinYaw
= sin_approx(CENTIDEGREES_TO_RADIANS(newHeading
));
2030 posControl
.actualState
.cosYaw
= cos_approx(CENTIDEGREES_TO_RADIANS(newHeading
));
2033 /*-----------------------------------------------------------
2034 * Returns pointer to currently used position (ABS or AGL) depending on surface tracking status
2035 *-----------------------------------------------------------*/
2036 const navEstimatedPosVel_t
* navGetCurrentActualPositionAndVelocity(void)
2038 return posControl
.flags
.isTerrainFollowEnabled
? &posControl
.actualState
.agl
: &posControl
.actualState
.abs
;
2041 /*-----------------------------------------------------------
2042 * Calculates distance and bearing to destination point
2043 *-----------------------------------------------------------*/
2044 static uint32_t calculateDistanceFromDelta(float deltaX
, float deltaY
)
2046 return fast_fsqrtf(sq(deltaX
) + sq(deltaY
));
2049 static int32_t calculateBearingFromDelta(float deltaX
, float deltaY
)
2051 return wrap_36000(RADIANS_TO_CENTIDEGREES(atan2_approx(deltaY
, deltaX
)));
2054 uint32_t calculateDistanceToDestination(const fpVector3_t
* destinationPos
)
2056 const navEstimatedPosVel_t
*posvel
= navGetCurrentActualPositionAndVelocity();
2057 const float deltaX
= destinationPos
->x
- posvel
->pos
.x
;
2058 const float deltaY
= destinationPos
->y
- posvel
->pos
.y
;
2060 return calculateDistanceFromDelta(deltaX
, deltaY
);
2063 int32_t calculateBearingToDestination(const fpVector3_t
* destinationPos
)
2065 const navEstimatedPosVel_t
*posvel
= navGetCurrentActualPositionAndVelocity();
2066 const float deltaX
= destinationPos
->x
- posvel
->pos
.x
;
2067 const float deltaY
= destinationPos
->y
- posvel
->pos
.y
;
2069 return calculateBearingFromDelta(deltaX
, deltaY
);
2072 bool navCalculatePathToDestination(navDestinationPath_t
*result
, const fpVector3_t
* destinationPos
)
2074 if (posControl
.flags
.estPosStatus
== EST_NONE
||
2075 posControl
.flags
.estHeadingStatus
== EST_NONE
) {
2080 const navEstimatedPosVel_t
*posvel
= navGetCurrentActualPositionAndVelocity();
2081 const float deltaX
= destinationPos
->x
- posvel
->pos
.x
;
2082 const float deltaY
= destinationPos
->y
- posvel
->pos
.y
;
2084 result
->distance
= calculateDistanceFromDelta(deltaX
, deltaY
);
2085 result
->bearing
= calculateBearingFromDelta(deltaX
, deltaY
);
2089 /*-----------------------------------------------------------
2090 * Check if waypoint is/was reached. Assume that waypoint-yaw stores initial bearing
2091 *-----------------------------------------------------------*/
2092 bool isWaypointMissed(const navWaypointPosition_t
* waypoint
)
2094 int32_t bearingError
= calculateBearingToDestination(&waypoint
->pos
) - waypoint
->yaw
;
2095 bearingError
= wrap_18000(bearingError
);
2097 return ABS(bearingError
) > 10000; // TRUE if we passed the waypoint by 100 degrees
2100 static bool isWaypointPositionReached(const fpVector3_t
* pos
, const bool isWaypointHome
)
2102 // We consider waypoint reached if within specified radius
2103 posControl
.wpDistance
= calculateDistanceToDestination(pos
);
2105 if (STATE(FIXED_WING_LEGACY
) && isWaypointHome
) {
2106 // Airplane will do a circular loiter over home and might never approach it closer than waypoint_radius - need extra check
2107 return (posControl
.wpDistance
<= navConfig()->general
.waypoint_radius
)
2108 || (posControl
.wpDistance
<= (navConfig()->fw
.loiter_radius
* 1.10f
)); // 10% margin of desired circular loiter radius
2111 return (posControl
.wpDistance
<= navConfig()->general
.waypoint_radius
);
2115 bool isWaypointReached(const navWaypointPosition_t
* waypoint
, const bool isWaypointHome
)
2117 return isWaypointPositionReached(&waypoint
->pos
, isWaypointHome
);
2120 static void updateHomePositionCompatibility(void)
2122 geoConvertLocalToGeodetic(&GPS_home
, &posControl
.gpsOrigin
, &posControl
.rthState
.homePosition
.pos
);
2123 GPS_distanceToHome
= posControl
.homeDistance
* 0.01f
;
2124 GPS_directionToHome
= posControl
.homeDirection
* 0.01f
;
2127 // Backdoor for RTH estimator
2128 float getFinalRTHAltitude(void)
2130 return posControl
.rthState
.rthFinalAltitude
;
2133 /*-----------------------------------------------------------
2134 * Update the RTH Altitudes
2135 *-----------------------------------------------------------*/
2136 static void updateDesiredRTHAltitude(void)
2138 if (ARMING_FLAG(ARMED
)) {
2139 if (!((navGetStateFlags(posControl
.navState
) & NAV_AUTO_RTH
)
2140 || ((navGetStateFlags(posControl
.navState
) & NAV_AUTO_WP
) && posControl
.waypointList
[posControl
.activeWaypointIndex
].action
== NAV_WP_ACTION_RTH
))) {
2141 switch (navConfig()->general
.flags
.rth_climb_first_stage_mode
) {
2142 case NAV_RTH_CLIMB_STAGE_AT_LEAST
:
2143 posControl
.rthState
.rthClimbStageAltitude
= posControl
.rthState
.homePosition
.pos
.z
+ navConfig()->general
.rth_climb_first_stage_altitude
;
2145 case NAV_RTH_CLIMB_STAGE_EXTRA
:
2146 posControl
.rthState
.rthClimbStageAltitude
= posControl
.actualState
.abs
.pos
.z
+ navConfig()->general
.rth_climb_first_stage_altitude
;
2150 switch (navConfig()->general
.flags
.rth_alt_control_mode
) {
2151 case NAV_RTH_NO_ALT
:
2152 posControl
.rthState
.rthInitialAltitude
= posControl
.actualState
.abs
.pos
.z
;
2153 posControl
.rthState
.rthFinalAltitude
= posControl
.rthState
.rthInitialAltitude
;
2156 case NAV_RTH_EXTRA_ALT
: // Maintain current altitude + predefined safety margin
2157 posControl
.rthState
.rthInitialAltitude
= posControl
.actualState
.abs
.pos
.z
+ navConfig()->general
.rth_altitude
;
2158 posControl
.rthState
.rthFinalAltitude
= posControl
.rthState
.rthInitialAltitude
;
2161 case NAV_RTH_MAX_ALT
:
2162 posControl
.rthState
.rthInitialAltitude
= MAX(posControl
.rthState
.rthInitialAltitude
, posControl
.actualState
.abs
.pos
.z
);
2163 if (navConfig()->general
.rth_altitude
> 0) {
2164 posControl
.rthState
.rthInitialAltitude
= MAX(posControl
.rthState
.rthInitialAltitude
, posControl
.rthState
.homePosition
.pos
.z
+ navConfig()->general
.rth_altitude
);
2166 posControl
.rthState
.rthFinalAltitude
= posControl
.rthState
.rthInitialAltitude
;
2169 case NAV_RTH_AT_LEAST_ALT
: // Climb to at least some predefined altitude above home
2170 posControl
.rthState
.rthInitialAltitude
= MAX(posControl
.rthState
.homePosition
.pos
.z
+ navConfig()->general
.rth_altitude
, posControl
.actualState
.abs
.pos
.z
);
2171 posControl
.rthState
.rthFinalAltitude
= posControl
.rthState
.rthInitialAltitude
;
2174 case NAV_RTH_AT_LEAST_ALT_LINEAR_DESCENT
:
2175 posControl
.rthState
.rthInitialAltitude
= MAX(posControl
.rthState
.homePosition
.pos
.z
+ navConfig()->general
.rth_altitude
, posControl
.actualState
.abs
.pos
.z
);
2176 posControl
.rthState
.rthFinalAltitude
= posControl
.rthState
.homePosition
.pos
.z
+ navConfig()->general
.rth_altitude
;
2179 case NAV_RTH_CONST_ALT
: // Climb/descend to predefined altitude above home
2181 posControl
.rthState
.rthInitialAltitude
= posControl
.rthState
.homePosition
.pos
.z
+ navConfig()->general
.rth_altitude
;
2182 posControl
.rthState
.rthFinalAltitude
= posControl
.rthState
.rthInitialAltitude
;
2186 posControl
.rthState
.rthClimbStageAltitude
= posControl
.actualState
.abs
.pos
.z
;
2187 posControl
.rthState
.rthInitialAltitude
= posControl
.actualState
.abs
.pos
.z
;
2188 posControl
.rthState
.rthFinalAltitude
= posControl
.actualState
.abs
.pos
.z
;
2192 /*-----------------------------------------------------------
2193 * RTH sanity test logic
2194 *-----------------------------------------------------------*/
2195 void initializeRTHSanityChecker(void)
2197 const timeMs_t currentTimeMs
= millis();
2199 posControl
.rthSanityChecker
.lastCheckTime
= currentTimeMs
;
2200 posControl
.rthSanityChecker
.rthSanityOK
= true;
2201 posControl
.rthSanityChecker
.minimalDistanceToHome
= calculateDistanceToDestination(&posControl
.rthState
.homePosition
.pos
);
2204 bool validateRTHSanityChecker(void)
2206 const timeMs_t currentTimeMs
= millis();
2208 // Ability to disable sanity checker
2209 if (navConfig()->general
.rth_abort_threshold
== 0) {
2213 // Check at 10Hz rate
2214 if ((currentTimeMs
- posControl
.rthSanityChecker
.lastCheckTime
) > 100) {
2215 const float currentDistanceToHome
= calculateDistanceToDestination(&posControl
.rthState
.homePosition
.pos
);
2216 posControl
.rthSanityChecker
.lastCheckTime
= currentTimeMs
;
2218 if (currentDistanceToHome
< posControl
.rthSanityChecker
.minimalDistanceToHome
) {
2219 posControl
.rthSanityChecker
.minimalDistanceToHome
= currentDistanceToHome
;
2221 // If while doing RTH we got even farther away from home - RTH is doing something crazy
2222 posControl
.rthSanityChecker
.rthSanityOK
= (currentDistanceToHome
- posControl
.rthSanityChecker
.minimalDistanceToHome
) < navConfig()->general
.rth_abort_threshold
;
2226 return posControl
.rthSanityChecker
.rthSanityOK
;
2229 /*-----------------------------------------------------------
2230 * Reset home position to current position
2231 *-----------------------------------------------------------*/
2232 void setHomePosition(const fpVector3_t
* pos
, int32_t yaw
, navSetWaypointFlags_t useMask
, navigationHomeFlags_t homeFlags
)
2235 if ((useMask
& NAV_POS_UPDATE_XY
) != 0) {
2236 posControl
.rthState
.homePosition
.pos
.x
= pos
->x
;
2237 posControl
.rthState
.homePosition
.pos
.y
= pos
->y
;
2238 if (homeFlags
& NAV_HOME_VALID_XY
) {
2239 posControl
.rthState
.homeFlags
|= NAV_HOME_VALID_XY
;
2241 posControl
.rthState
.homeFlags
&= ~NAV_HOME_VALID_XY
;
2246 if ((useMask
& NAV_POS_UPDATE_Z
) != 0) {
2247 posControl
.rthState
.homePosition
.pos
.z
= pos
->z
;
2248 if (homeFlags
& NAV_HOME_VALID_Z
) {
2249 posControl
.rthState
.homeFlags
|= NAV_HOME_VALID_Z
;
2251 posControl
.rthState
.homeFlags
&= ~NAV_HOME_VALID_Z
;
2256 if ((useMask
& NAV_POS_UPDATE_HEADING
) != 0) {
2258 posControl
.rthState
.homePosition
.yaw
= yaw
;
2259 if (homeFlags
& NAV_HOME_VALID_HEADING
) {
2260 posControl
.rthState
.homeFlags
|= NAV_HOME_VALID_HEADING
;
2262 posControl
.rthState
.homeFlags
&= ~NAV_HOME_VALID_HEADING
;
2266 posControl
.homeDistance
= 0;
2267 posControl
.homeDirection
= 0;
2269 // Update target RTH altitude as a waypoint above home
2270 updateDesiredRTHAltitude();
2272 updateHomePositionCompatibility();
2273 ENABLE_STATE(GPS_FIX_HOME
);
2276 static navigationHomeFlags_t
navigationActualStateHomeValidity(void)
2278 navigationHomeFlags_t flags
= 0;
2280 if (posControl
.flags
.estPosStatus
>= EST_USABLE
) {
2281 flags
|= NAV_HOME_VALID_XY
| NAV_HOME_VALID_Z
;
2284 if (posControl
.flags
.estHeadingStatus
>= EST_USABLE
) {
2285 flags
|= NAV_HOME_VALID_HEADING
;
2291 #if defined(USE_SAFE_HOME)
2293 void checkSafeHomeState(bool shouldBeEnabled
)
2295 if (navConfig()->general
.flags
.safehome_usage_mode
== SAFEHOME_USAGE_OFF
) {
2296 shouldBeEnabled
= false;
2297 } else if (navConfig()->general
.flags
.safehome_usage_mode
== SAFEHOME_USAGE_RTH_FS
&& shouldBeEnabled
) {
2298 // if safehomes are only used with failsafe and we're trying to enable safehome
2299 // then enable the safehome only with failsafe
2300 shouldBeEnabled
= posControl
.flags
.forcedRTHActivated
;
2302 // no safe homes found when arming or safehome feature in the correct state, then we don't need to do anything
2303 if (safehome_distance
== 0 || (safehome_applied
== shouldBeEnabled
)) {
2306 if (shouldBeEnabled
) {
2307 // set home to safehome
2308 setHomePosition(&nearestSafeHome
, 0, NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_Z
| NAV_POS_UPDATE_HEADING
, navigationActualStateHomeValidity());
2309 safehome_applied
= true;
2311 // set home to original arming point
2312 setHomePosition(&original_rth_home
, 0, NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_Z
| NAV_POS_UPDATE_HEADING
, navigationActualStateHomeValidity());
2313 safehome_applied
= false;
2315 // if we've changed the home position, update the distance and direction
2316 updateHomePosition();
2319 /***********************************************************
2320 * See if there are any safehomes near where we are arming.
2321 * If so, save the nearest one in case we need it later for RTH.
2322 **********************************************************/
2323 bool findNearestSafeHome(void)
2325 safehome_index
= -1;
2326 uint32_t nearest_safehome_distance
= navConfig()->general
.safehome_max_distance
+ 1;
2327 uint32_t distance_to_current
;
2328 fpVector3_t currentSafeHome
;
2329 gpsLocation_t shLLH
;
2331 for (uint8_t i
= 0; i
< MAX_SAFE_HOMES
; i
++) {
2332 if (!safeHomeConfig(i
)->enabled
)
2335 shLLH
.lat
= safeHomeConfig(i
)->lat
;
2336 shLLH
.lon
= safeHomeConfig(i
)->lon
;
2337 geoConvertGeodeticToLocal(¤tSafeHome
, &posControl
.gpsOrigin
, &shLLH
, GEO_ALT_RELATIVE
);
2338 distance_to_current
= calculateDistanceToDestination(¤tSafeHome
);
2339 if (distance_to_current
< nearest_safehome_distance
) {
2340 // this safehome is the nearest so far - keep track of it.
2342 nearest_safehome_distance
= distance_to_current
;
2343 nearestSafeHome
.x
= currentSafeHome
.x
;
2344 nearestSafeHome
.y
= currentSafeHome
.y
;
2345 nearestSafeHome
.z
= currentSafeHome
.z
;
2348 if (safehome_index
>= 0) {
2349 safehome_distance
= nearest_safehome_distance
;
2351 safehome_distance
= 0;
2353 return safehome_distance
> 0;
2357 /*-----------------------------------------------------------
2358 * Update home position, calculate distance and bearing to home
2359 *-----------------------------------------------------------*/
2360 void updateHomePosition(void)
2362 // Disarmed and have a valid position, constantly update home
2363 if (!ARMING_FLAG(ARMED
)) {
2364 if (posControl
.flags
.estPosStatus
>= EST_USABLE
) {
2365 const navigationHomeFlags_t validHomeFlags
= NAV_HOME_VALID_XY
| NAV_HOME_VALID_Z
;
2366 bool setHome
= (posControl
.rthState
.homeFlags
& validHomeFlags
) != validHomeFlags
;
2367 switch ((nav_reset_type_e
)positionEstimationConfig()->reset_home_type
) {
2368 case NAV_RESET_NEVER
:
2370 case NAV_RESET_ON_FIRST_ARM
:
2371 setHome
|= !ARMING_FLAG(WAS_EVER_ARMED
);
2373 case NAV_RESET_ON_EACH_ARM
:
2378 #if defined(USE_SAFE_HOME)
2379 findNearestSafeHome();
2381 setHomePosition(&posControl
.actualState
.abs
.pos
, posControl
.actualState
.yaw
, NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_Z
| NAV_POS_UPDATE_HEADING
, navigationActualStateHomeValidity());
2382 // save the current location in case it is replaced by a safehome or HOME_RESET
2383 original_rth_home
.x
= posControl
.rthState
.homePosition
.pos
.x
;
2384 original_rth_home
.y
= posControl
.rthState
.homePosition
.pos
.y
;
2385 original_rth_home
.z
= posControl
.rthState
.homePosition
.pos
.z
;
2390 static bool isHomeResetAllowed
= false;
2392 // If pilot so desires he may reset home position to current position
2393 if (IS_RC_MODE_ACTIVE(BOXHOMERESET
)) {
2394 if (isHomeResetAllowed
&& !FLIGHT_MODE(FAILSAFE_MODE
) && !FLIGHT_MODE(NAV_RTH_MODE
) && !FLIGHT_MODE(NAV_WP_MODE
) && (posControl
.flags
.estPosStatus
>= EST_USABLE
)) {
2395 const navSetWaypointFlags_t homeUpdateFlags
= STATE(GPS_FIX_HOME
) ? (NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_HEADING
) : (NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_Z
| NAV_POS_UPDATE_HEADING
);
2396 setHomePosition(&posControl
.actualState
.abs
.pos
, posControl
.actualState
.yaw
, homeUpdateFlags
, navigationActualStateHomeValidity());
2397 isHomeResetAllowed
= false;
2401 isHomeResetAllowed
= true;
2404 // Update distance and direction to home if armed (home is not updated when armed)
2405 if (STATE(GPS_FIX_HOME
)) {
2406 fpVector3_t
* tmpHomePos
= rthGetHomeTargetPosition(RTH_HOME_FINAL_LAND
);
2407 posControl
.homeDistance
= calculateDistanceToDestination(tmpHomePos
);
2408 posControl
.homeDirection
= calculateBearingToDestination(tmpHomePos
);
2409 updateHomePositionCompatibility();
2414 /* -----------------------------------------------------------
2415 * Override RTH preset altitude and Climb First option
2416 * using Pitch/Roll stick held for > 1 seconds
2417 * Climb First override limited to Fixed Wing only
2418 *-----------------------------------------------------------*/
2419 static bool rthAltControlStickOverrideCheck(unsigned axis
)
2421 if (!navConfig()->general
.flags
.rth_alt_control_override
|| posControl
.flags
.forcedRTHActivated
|| (axis
== ROLL
&& STATE(MULTIROTOR
))) {
2424 static timeMs_t rthOverrideStickHoldStartTime
[2];
2426 if (rxGetChannelValue(axis
) > rxConfig()->maxcheck
) {
2427 timeDelta_t holdTime
= millis() - rthOverrideStickHoldStartTime
[axis
];
2429 if (!rthOverrideStickHoldStartTime
[axis
]) {
2430 rthOverrideStickHoldStartTime
[axis
] = millis();
2431 } else if (ABS(1500 - holdTime
) < 500) { // 1s delay to activate, activation duration limited to 1 sec
2432 if (axis
== PITCH
) { // PITCH down to override preset altitude reset to current altitude
2433 posControl
.rthState
.rthInitialAltitude
= posControl
.actualState
.abs
.pos
.z
;
2434 posControl
.rthState
.rthFinalAltitude
= posControl
.rthState
.rthInitialAltitude
;
2436 } else if (axis
== ROLL
) { // ROLL right to override climb first
2441 rthOverrideStickHoldStartTime
[axis
] = 0;
2447 /* ---------------------------------------------------
2448 * If climb stage is being used, see if it is time to
2449 * transiton in to turn.
2450 * Limited to fixed wing only.
2451 * --------------------------------------------------- */
2452 bool rthClimbStageActiveAndComplete() {
2453 if ((STATE(FIXED_WING_LEGACY
) || STATE(AIRPLANE
)) && (navConfig()->general
.rth_climb_first_stage_altitude
> 0)) {
2454 if (posControl
.actualState
.abs
.pos
.z
>= posControl
.rthState
.rthClimbStageAltitude
) {
2462 /*-----------------------------------------------------------
2463 * Update flight statistics
2464 *-----------------------------------------------------------*/
2465 static void updateNavigationFlightStatistics(void)
2467 static timeMs_t previousTimeMs
= 0;
2468 const timeMs_t currentTimeMs
= millis();
2469 const timeDelta_t timeDeltaMs
= currentTimeMs
- previousTimeMs
;
2470 previousTimeMs
= currentTimeMs
;
2472 if (ARMING_FLAG(ARMED
)) {
2473 posControl
.totalTripDistance
+= posControl
.actualState
.velXY
* MS2S(timeDeltaMs
);
2477 uint32_t getTotalTravelDistance(void)
2479 return lrintf(posControl
.totalTripDistance
);
2482 /*-----------------------------------------------------------
2483 * Calculate platform-specific hold position (account for deceleration)
2484 *-----------------------------------------------------------*/
2485 void calculateInitialHoldPosition(fpVector3_t
* pos
)
2487 if (STATE(FIXED_WING_LEGACY
)) { // FIXED_WING_LEGACY
2488 calculateFixedWingInitialHoldPosition(pos
);
2491 calculateMulticopterInitialHoldPosition(pos
);
2495 /*-----------------------------------------------------------
2496 * Set active XYZ-target and desired heading
2497 *-----------------------------------------------------------*/
2498 void setDesiredPosition(const fpVector3_t
* pos
, int32_t yaw
, navSetWaypointFlags_t useMask
)
2500 // XY-position update is allowed only when not braking in NAV_CRUISE_BRAKING
2501 if ((useMask
& NAV_POS_UPDATE_XY
) != 0 && !STATE(NAV_CRUISE_BRAKING
)) {
2502 posControl
.desiredState
.pos
.x
= pos
->x
;
2503 posControl
.desiredState
.pos
.y
= pos
->y
;
2507 if ((useMask
& NAV_POS_UPDATE_Z
) != 0) {
2508 updateClimbRateToAltitudeController(0, ROC_TO_ALT_RESET
); // Reset RoC/RoD -> altitude controller
2509 posControl
.desiredState
.pos
.z
= pos
->z
;
2513 if ((useMask
& NAV_POS_UPDATE_HEADING
) != 0) {
2515 posControl
.desiredState
.yaw
= yaw
;
2517 else if ((useMask
& NAV_POS_UPDATE_BEARING
) != 0) {
2518 posControl
.desiredState
.yaw
= calculateBearingToDestination(pos
);
2520 else if ((useMask
& NAV_POS_UPDATE_BEARING_TAIL_FIRST
) != 0) {
2521 posControl
.desiredState
.yaw
= wrap_36000(calculateBearingToDestination(pos
) - 18000);
2525 void calculateFarAwayTarget(fpVector3_t
* farAwayPos
, int32_t yaw
, int32_t distance
)
2527 farAwayPos
->x
= navGetCurrentActualPositionAndVelocity()->pos
.x
+ distance
* cos_approx(CENTIDEGREES_TO_RADIANS(yaw
));
2528 farAwayPos
->y
= navGetCurrentActualPositionAndVelocity()->pos
.y
+ distance
* sin_approx(CENTIDEGREES_TO_RADIANS(yaw
));
2529 farAwayPos
->z
= navGetCurrentActualPositionAndVelocity()->pos
.z
;
2532 void calculateNewCruiseTarget(fpVector3_t
* origin
, int32_t yaw
, int32_t distance
)
2534 origin
->x
= origin
->x
+ distance
* cos_approx(CENTIDEGREES_TO_RADIANS(yaw
));
2535 origin
->y
= origin
->y
+ distance
* sin_approx(CENTIDEGREES_TO_RADIANS(yaw
));
2536 origin
->z
= origin
->z
;
2539 /*-----------------------------------------------------------
2541 *-----------------------------------------------------------*/
2542 void resetLandingDetector(void)
2544 if (STATE(FIXED_WING_LEGACY
)) { // FIXED_WING_LEGACY
2545 resetFixedWingLandingDetector();
2548 resetMulticopterLandingDetector();
2552 bool isLandingDetected(void)
2554 bool landingDetected
;
2556 if (STATE(FIXED_WING_LEGACY
)) { // FIXED_WING_LEGACY
2557 landingDetected
= isFixedWingLandingDetected();
2560 landingDetected
= isMulticopterLandingDetected();
2563 return landingDetected
;
2566 /*-----------------------------------------------------------
2567 * Z-position controller
2568 *-----------------------------------------------------------*/
2569 void updateClimbRateToAltitudeController(float desiredClimbRate
, climbRateToAltitudeControllerMode_e mode
)
2571 static timeUs_t lastUpdateTimeUs
;
2572 timeUs_t currentTimeUs
= micros();
2574 // Terrain following uses different altitude measurement
2575 const float altitudeToUse
= navGetCurrentActualPositionAndVelocity()->pos
.z
;
2577 if (mode
== ROC_TO_ALT_RESET
) {
2578 lastUpdateTimeUs
= currentTimeUs
;
2579 posControl
.desiredState
.pos
.z
= altitudeToUse
;
2584 * If max altitude is set, reset climb rate if altitude is reached and climb rate is > 0
2585 * In other words, when altitude is reached, allow it only to shrink
2587 if (navConfig()->general
.max_altitude
> 0 &&
2588 altitudeToUse
>= navConfig()->general
.max_altitude
&&
2589 desiredClimbRate
> 0
2591 desiredClimbRate
= 0;
2594 if (STATE(FIXED_WING_LEGACY
)) {
2595 // Fixed wing climb rate controller is open-loop. We simply move the known altitude target
2596 float timeDelta
= US2S(currentTimeUs
- lastUpdateTimeUs
);
2598 if (timeDelta
<= HZ2S(MIN_POSITION_UPDATE_RATE_HZ
)) {
2599 posControl
.desiredState
.pos
.z
+= desiredClimbRate
* timeDelta
;
2600 posControl
.desiredState
.pos
.z
= constrainf(posControl
.desiredState
.pos
.z
, altitudeToUse
- 500, altitudeToUse
+ 500); // FIXME: calculate sanity limits in a smarter way
2604 // Multicopter climb-rate control is closed-loop, it's possible to directly calculate desired altitude setpoint to yield the required RoC/RoD
2605 posControl
.desiredState
.pos
.z
= altitudeToUse
+ (desiredClimbRate
/ posControl
.pids
.pos
[Z
].param
.kP
);
2608 lastUpdateTimeUs
= currentTimeUs
;
2612 static void resetAltitudeController(bool useTerrainFollowing
)
2614 // Set terrain following flag
2615 posControl
.flags
.isTerrainFollowEnabled
= useTerrainFollowing
;
2617 if (STATE(FIXED_WING_LEGACY
)) {
2618 resetFixedWingAltitudeController();
2621 resetMulticopterAltitudeController();
2625 static void setupAltitudeController(void)
2627 if (STATE(FIXED_WING_LEGACY
)) {
2628 setupFixedWingAltitudeController();
2631 setupMulticopterAltitudeController();
2635 static bool adjustAltitudeFromRCInput(void)
2637 if (STATE(FIXED_WING_LEGACY
)) {
2638 return adjustFixedWingAltitudeFromRCInput();
2641 return adjustMulticopterAltitudeFromRCInput();
2645 /*-----------------------------------------------------------
2646 * Jump Counter support functions
2647 *-----------------------------------------------------------*/
2648 static void setupJumpCounters(void)
2650 for (uint8_t wp
= 0; wp
< posControl
.waypointCount
; wp
++) {
2651 if (posControl
.waypointList
[wp
].action
== NAV_WP_ACTION_JUMP
){
2652 posControl
.waypointList
[wp
].p3
= posControl
.waypointList
[wp
].p2
;
2657 static void resetJumpCounter(void)
2659 // reset the volatile counter from the set / static value
2660 posControl
.waypointList
[posControl
.activeWaypointIndex
].p3
=
2661 posControl
.waypointList
[posControl
.activeWaypointIndex
].p2
;
2664 static void clearJumpCounters(void)
2666 for (uint8_t wp
= 0; wp
< posControl
.waypointCount
; wp
++) {
2667 if (posControl
.waypointList
[wp
].action
== NAV_WP_ACTION_JUMP
) {
2668 posControl
.waypointList
[wp
].p3
= 0;
2675 /*-----------------------------------------------------------
2676 * Heading controller (pass-through to MAG mode)
2677 *-----------------------------------------------------------*/
2678 static void resetHeadingController(void)
2680 if (STATE(FIXED_WING_LEGACY
)) {
2681 resetFixedWingHeadingController();
2684 resetMulticopterHeadingController();
2688 static bool adjustHeadingFromRCInput(void)
2690 if (STATE(FIXED_WING_LEGACY
)) {
2691 return adjustFixedWingHeadingFromRCInput();
2694 return adjustMulticopterHeadingFromRCInput();
2698 /*-----------------------------------------------------------
2699 * XY Position controller
2700 *-----------------------------------------------------------*/
2701 static void resetPositionController(void)
2703 if (STATE(FIXED_WING_LEGACY
)) {
2704 resetFixedWingPositionController();
2707 resetMulticopterPositionController();
2708 resetMulticopterBrakingMode();
2712 static bool adjustPositionFromRCInput(void)
2716 if (STATE(FIXED_WING_LEGACY
)) {
2717 retValue
= adjustFixedWingPositionFromRCInput();
2721 const int16_t rcPitchAdjustment
= applyDeadbandRescaled(rcCommand
[PITCH
], rcControlsConfig()->pos_hold_deadband
, -500, 500);
2722 const int16_t rcRollAdjustment
= applyDeadbandRescaled(rcCommand
[ROLL
], rcControlsConfig()->pos_hold_deadband
, -500, 500);
2724 retValue
= adjustMulticopterPositionFromRCInput(rcPitchAdjustment
, rcRollAdjustment
);
2730 /*-----------------------------------------------------------
2732 *-----------------------------------------------------------*/
2733 void resetGCSFlags(void)
2735 posControl
.flags
.isGCSAssistedNavigationReset
= false;
2736 posControl
.flags
.isGCSAssistedNavigationEnabled
= false;
2739 void getWaypoint(uint8_t wpNumber
, navWaypoint_t
* wpData
)
2741 /* Default waypoint to send */
2742 wpData
->action
= NAV_WP_ACTION_RTH
;
2749 wpData
->flag
= NAV_WP_FLAG_LAST
;
2751 // WP #0 - special waypoint - HOME
2752 if (wpNumber
== 0) {
2753 if (STATE(GPS_FIX_HOME
)) {
2754 wpData
->lat
= GPS_home
.lat
;
2755 wpData
->lon
= GPS_home
.lon
;
2756 wpData
->alt
= GPS_home
.alt
;
2759 // WP #255 - special waypoint - directly get actualPosition
2760 else if (wpNumber
== 255) {
2761 gpsLocation_t wpLLH
;
2763 geoConvertLocalToGeodetic(&wpLLH
, &posControl
.gpsOrigin
, &navGetCurrentActualPositionAndVelocity()->pos
);
2765 wpData
->lat
= wpLLH
.lat
;
2766 wpData
->lon
= wpLLH
.lon
;
2767 wpData
->alt
= wpLLH
.alt
;
2769 // WP #254 - special waypoint - get desiredPosition that was set by ground control station if in 3D-guided mode
2770 else if (wpNumber
== 254) {
2771 navigationFSMStateFlags_t navStateFlags
= navGetStateFlags(posControl
.navState
);
2773 if ((posControl
.gpsOrigin
.valid
) && (navStateFlags
& NAV_CTL_ALT
) && (navStateFlags
& NAV_CTL_POS
)) {
2774 gpsLocation_t wpLLH
;
2776 geoConvertLocalToGeodetic(&wpLLH
, &posControl
.gpsOrigin
, &posControl
.desiredState
.pos
);
2778 wpData
->lat
= wpLLH
.lat
;
2779 wpData
->lon
= wpLLH
.lon
;
2780 wpData
->alt
= wpLLH
.alt
;
2783 // WP #1 - #60 - common waypoints - pre-programmed mission
2784 else if ((wpNumber
>= 1) && (wpNumber
<= NAV_MAX_WAYPOINTS
)) {
2785 if (wpNumber
<= posControl
.waypointCount
) {
2786 *wpData
= posControl
.waypointList
[wpNumber
- 1];
2787 if(wpData
->action
== NAV_WP_ACTION_JUMP
) {
2788 wpData
->p1
+= 1; // make WP # (vice index)
2795 void setWaypoint(uint8_t wpNumber
, const navWaypoint_t
* wpData
)
2797 gpsLocation_t wpLLH
;
2798 navWaypointPosition_t wpPos
;
2800 // Pre-fill structure to convert to local coordinates
2801 wpLLH
.lat
= wpData
->lat
;
2802 wpLLH
.lon
= wpData
->lon
;
2803 wpLLH
.alt
= wpData
->alt
;
2805 // WP #0 - special waypoint - HOME
2806 if ((wpNumber
== 0) && ARMING_FLAG(ARMED
) && (posControl
.flags
.estPosStatus
>= EST_USABLE
) && posControl
.gpsOrigin
.valid
&& posControl
.flags
.isGCSAssistedNavigationEnabled
) {
2807 // Forcibly set home position. Note that this is only valid if already armed, otherwise home will be reset instantly
2808 geoConvertGeodeticToLocal(&wpPos
.pos
, &posControl
.gpsOrigin
, &wpLLH
, GEO_ALT_RELATIVE
);
2809 setHomePosition(&wpPos
.pos
, 0, NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_Z
| NAV_POS_UPDATE_HEADING
, NAV_HOME_VALID_ALL
);
2811 // WP #255 - special waypoint - directly set desiredPosition
2812 // Only valid when armed and in poshold mode
2813 else if ((wpNumber
== 255) && (wpData
->action
== NAV_WP_ACTION_WAYPOINT
) &&
2814 ARMING_FLAG(ARMED
) && (posControl
.flags
.estPosStatus
== EST_TRUSTED
) && posControl
.gpsOrigin
.valid
&& posControl
.flags
.isGCSAssistedNavigationEnabled
&&
2815 (posControl
.navState
== NAV_STATE_POSHOLD_3D_IN_PROGRESS
)) {
2816 // Convert to local coordinates
2817 geoConvertGeodeticToLocal(&wpPos
.pos
, &posControl
.gpsOrigin
, &wpLLH
, GEO_ALT_RELATIVE
);
2819 navSetWaypointFlags_t waypointUpdateFlags
= NAV_POS_UPDATE_XY
;
2821 // If we received global altitude == 0, use current altitude
2822 if (wpData
->alt
!= 0) {
2823 waypointUpdateFlags
|= NAV_POS_UPDATE_Z
;
2826 if (wpData
->p1
> 0 && wpData
->p1
< 360) {
2827 waypointUpdateFlags
|= NAV_POS_UPDATE_HEADING
;
2830 setDesiredPosition(&wpPos
.pos
, DEGREES_TO_CENTIDEGREES(wpData
->p1
), waypointUpdateFlags
);
2832 // WP #1 - #NAV_MAX_WAYPOINTS - common waypoints - pre-programmed mission
2833 else if ((wpNumber
>= 1) && (wpNumber
<= NAV_MAX_WAYPOINTS
) && !ARMING_FLAG(ARMED
)) {
2834 if (wpData
->action
== NAV_WP_ACTION_WAYPOINT
|| wpData
->action
== NAV_WP_ACTION_JUMP
|| wpData
->action
== NAV_WP_ACTION_RTH
|| wpData
->action
== NAV_WP_ACTION_HOLD_TIME
|| wpData
->action
== NAV_WP_ACTION_LAND
|| wpData
->action
== NAV_WP_ACTION_SET_POI
|| wpData
->action
== NAV_WP_ACTION_SET_HEAD
) {
2835 // Only allow upload next waypoint (continue upload mission) or first waypoint (new mission)
2836 static int8_t nonGeoWaypointCount
= 0;
2838 if (wpNumber
== (posControl
.waypointCount
+ 1) || wpNumber
== 1) {
2839 if (wpNumber
== 1) {
2840 resetWaypointList();
2842 posControl
.waypointList
[wpNumber
- 1] = *wpData
;
2843 if(wpData
->action
== NAV_WP_ACTION_SET_POI
|| wpData
->action
== NAV_WP_ACTION_SET_HEAD
|| wpData
->action
== NAV_WP_ACTION_JUMP
) {
2844 nonGeoWaypointCount
+= 1;
2845 if(wpData
->action
== NAV_WP_ACTION_JUMP
) {
2846 posControl
.waypointList
[wpNumber
- 1].p1
-= 1; // make index (vice WP #)
2850 posControl
.waypointCount
= wpNumber
;
2851 posControl
.waypointListValid
= (wpData
->flag
== NAV_WP_FLAG_LAST
);
2852 posControl
.geoWaypointCount
= posControl
.waypointCount
- nonGeoWaypointCount
;
2853 if (posControl
.waypointListValid
) {
2854 nonGeoWaypointCount
= 0;
2861 void resetWaypointList(void)
2863 /* Can only reset waypoint list if not armed */
2864 if (!ARMING_FLAG(ARMED
)) {
2865 posControl
.waypointCount
= 0;
2866 posControl
.waypointListValid
= false;
2867 posControl
.geoWaypointCount
= 0;
2868 #ifdef USE_MULTI_MISSION
2869 posControl
.loadedMultiMissionIndex
= 0;
2870 posControl
.loadedMultiMissionStartWP
= 0;
2871 posControl
.loadedMultiMissionWPCount
= 0;
2876 bool isWaypointListValid(void)
2878 return posControl
.waypointListValid
;
2881 int getWaypointCount(void)
2883 return posControl
.waypointCount
;
2885 #ifdef USE_MULTI_MISSION
2886 void selectMultiMissionIndex(int8_t increment
)
2888 if (posControl
.multiMissionCount
> 1) { // stick selection only active when multi mission loaded
2889 navConfigMutable()->general
.waypoint_multi_mission_index
= constrain(navConfigMutable()->general
.waypoint_multi_mission_index
+ increment
, 0, posControl
.multiMissionCount
);
2893 void setMultiMissionOnArm(void)
2895 if (posControl
.multiMissionCount
> 1 && posControl
.loadedMultiMissionWPCount
) {
2896 posControl
.waypointCount
= posControl
.loadedMultiMissionWPCount
;
2898 for (int8_t i
= 0; i
< NAV_MAX_WAYPOINTS
; i
++) {
2899 posControl
.waypointList
[i
] = posControl
.waypointList
[i
+ posControl
.loadedMultiMissionStartWP
];
2900 if (i
== posControl
.waypointCount
- 1) {
2905 posControl
.loadedMultiMissionStartWP
= 0;
2906 posControl
.loadedMultiMissionWPCount
= 0;
2910 bool checkMissionCount(int8_t waypoint
)
2912 if (nonVolatileWaypointList(waypoint
)->flag
== NAV_WP_FLAG_LAST
) {
2913 posControl
.multiMissionCount
+= 1; // count up no missions in multi mission WP file
2914 if (waypoint
!= NAV_MAX_WAYPOINTS
- 1) {
2915 return (nonVolatileWaypointList(waypoint
+ 1)->flag
== NAV_WP_FLAG_LAST
&&
2916 nonVolatileWaypointList(waypoint
+ 1)->action
==NAV_WP_ACTION_RTH
);
2917 // end of multi mission file if successive NAV_WP_FLAG_LAST and default action (RTH)
2922 #endif // multi mission
2923 #ifdef NAV_NON_VOLATILE_WAYPOINT_STORAGE
2924 bool loadNonVolatileWaypointList(bool clearIfLoaded
)
2926 #ifdef USE_MULTI_MISSION
2927 /* multi_mission_index 0 only used for non NVM missions - don't load.
2928 * Don't load if mission planner WP count > 0 */
2929 if (ARMING_FLAG(ARMED
) || !navConfig()->general
.waypoint_multi_mission_index
|| posControl
.wpPlannerActiveWPIndex
) {
2931 if (ARMING_FLAG(ARMED
) || posControl
.wpPlannerActiveWPIndex
) {
2936 // if forced and waypoints are already loaded, just unload them.
2937 if (clearIfLoaded
&& posControl
.waypointCount
> 0) {
2938 resetWaypointList();
2941 #ifdef USE_MULTI_MISSION
2942 /* Reset multi mission index to 1 if exceeds number of available missions */
2943 if (navConfig()->general
.waypoint_multi_mission_index
> posControl
.multiMissionCount
) {
2944 navConfigMutable()->general
.waypoint_multi_mission_index
= 1;
2946 posControl
.multiMissionCount
= 0;
2947 posControl
.loadedMultiMissionWPCount
= 0;
2948 int8_t loadedMultiMissionGeoWPCount
;
2950 for (int i
= 0; i
< NAV_MAX_WAYPOINTS
; i
++) {
2951 setWaypoint(i
+ 1, nonVolatileWaypointList(i
));
2952 #ifdef USE_MULTI_MISSION
2953 /* store details of selected mission */
2954 if ((posControl
.multiMissionCount
+ 1 == navConfig()->general
.waypoint_multi_mission_index
)) {
2956 if (posControl
.loadedMultiMissionWPCount
== 0) {
2957 posControl
.loadedMultiMissionWPCount
= 1; // start marker only, value here unimportant (but not 0)
2958 posControl
.loadedMultiMissionStartWP
= i
;
2959 loadedMultiMissionGeoWPCount
= posControl
.geoWaypointCount
;
2962 if (posControl
.waypointList
[i
].flag
== NAV_WP_FLAG_LAST
) {
2963 posControl
.loadedMultiMissionWPCount
= i
- posControl
.loadedMultiMissionStartWP
+ 1;
2964 loadedMultiMissionGeoWPCount
= posControl
.geoWaypointCount
- loadedMultiMissionGeoWPCount
+ 1;
2968 /* count up number of missions */
2969 if (checkMissionCount(i
)) {
2974 posControl
.geoWaypointCount
= loadedMultiMissionGeoWPCount
;
2975 posControl
.loadedMultiMissionIndex
= posControl
.multiMissionCount
? navConfig()->general
.waypoint_multi_mission_index
: 0;
2977 /* Mission sanity check failed - reset the list
2978 * Also reset if no selected mission loaded (shouldn't happen) */
2979 if (!posControl
.waypointListValid
|| !posControl
.loadedMultiMissionWPCount
) {
2981 // check this is the last waypoint
2982 if (nonVolatileWaypointList(i
)->flag
== NAV_WP_FLAG_LAST
) {
2986 // Mission sanity check failed - reset the list
2987 if (!posControl
.waypointListValid
) {
2989 resetWaypointList();
2992 return posControl
.waypointListValid
;
2995 bool saveNonVolatileWaypointList(void)
2997 if (ARMING_FLAG(ARMED
) || !posControl
.waypointListValid
)
3000 for (int i
= 0; i
< NAV_MAX_WAYPOINTS
; i
++) {
3001 getWaypoint(i
+ 1, nonVolatileWaypointListMutable(i
));
3003 #ifdef USE_MULTI_MISSION
3004 navConfigMutable()->general
.waypoint_multi_mission_index
= 1; // reset selected mission to 1 when new entries saved
3006 saveConfigAndNotify();
3012 #if defined(USE_SAFE_HOME)
3014 void resetSafeHomes(void)
3016 memset(safeHomeConfigMutable(0), 0, sizeof(navSafeHome_t
) * MAX_SAFE_HOMES
);
3020 static void mapWaypointToLocalPosition(fpVector3_t
* localPos
, const navWaypoint_t
* waypoint
, geoAltitudeConversionMode_e altConv
)
3022 gpsLocation_t wpLLH
;
3024 /* Default to home position if lat & lon = 0 or HOME flag set
3025 * Applicable to WAYPOINT, HOLD_TIME & LANDING WP types */
3026 if ((waypoint
->lat
== 0 && waypoint
->lon
== 0) || waypoint
->flag
== NAV_WP_FLAG_HOME
) {
3027 wpLLH
.lat
= GPS_home
.lat
;
3028 wpLLH
.lon
= GPS_home
.lon
;
3030 wpLLH
.lat
= waypoint
->lat
;
3031 wpLLH
.lon
= waypoint
->lon
;
3033 wpLLH
.alt
= waypoint
->alt
;
3035 geoConvertGeodeticToLocal(localPos
, &posControl
.gpsOrigin
, &wpLLH
, altConv
);
3038 static void calculateAndSetActiveWaypointToLocalPosition(const fpVector3_t
* pos
)
3040 posControl
.activeWaypoint
.pos
= *pos
;
3042 // Calculate initial bearing towards waypoint and store it in waypoint yaw parameter (this will further be used to detect missed waypoints)
3043 posControl
.activeWaypoint
.yaw
= calculateBearingToDestination(pos
);
3045 // Set desired position to next waypoint (XYZ-controller)
3046 setDesiredPosition(&posControl
.activeWaypoint
.pos
, posControl
.activeWaypoint
.yaw
, NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_Z
| NAV_POS_UPDATE_HEADING
);
3049 geoAltitudeConversionMode_e
waypointMissionAltConvMode(geoAltitudeDatumFlag_e datumFlag
)
3051 return datumFlag
== NAV_WP_MSL_DATUM
? GEO_ALT_ABSOLUTE
: GEO_ALT_RELATIVE
;
3054 static void calculateAndSetActiveWaypoint(const navWaypoint_t
* waypoint
)
3056 fpVector3_t localPos
;
3057 mapWaypointToLocalPosition(&localPos
, waypoint
, waypointMissionAltConvMode(waypoint
->p3
));
3058 calculateAndSetActiveWaypointToLocalPosition(&localPos
);
3061 /* Checks if active waypoint is last in mission */
3062 bool isLastMissionWaypoint(void)
3064 return FLIGHT_MODE(NAV_WP_MODE
) && (posControl
.activeWaypointIndex
>= (posControl
.waypointCount
- 1) ||
3065 (posControl
.waypointList
[posControl
.activeWaypointIndex
].flag
== NAV_WP_FLAG_LAST
));
3068 /* Checks if Nav hold position is active */
3069 bool isNavHoldPositionActive(void)
3071 if (FLIGHT_MODE(NAV_WP_MODE
)) { // WP mode last WP hold and Timed hold positions
3072 return isLastMissionWaypoint() || NAV_Status
.state
== MW_NAV_STATE_HOLD_TIMED
;
3074 // RTH spiral climb and Home positions and POSHOLD position
3075 return FLIGHT_MODE(NAV_RTH_MODE
) || FLIGHT_MODE(NAV_POSHOLD_MODE
);
3078 float getActiveWaypointSpeed(void)
3080 if (posControl
.flags
.isAdjustingPosition
) {
3081 // In manual control mode use different cap for speed
3082 return navConfig()->general
.max_manual_speed
;
3085 uint16_t waypointSpeed
= navConfig()->general
.auto_speed
;
3087 if (navGetStateFlags(posControl
.navState
) & NAV_AUTO_WP
) {
3088 if (posControl
.waypointCount
> 0 && (posControl
.waypointList
[posControl
.activeWaypointIndex
].action
== NAV_WP_ACTION_WAYPOINT
|| posControl
.waypointList
[posControl
.activeWaypointIndex
].action
== NAV_WP_ACTION_HOLD_TIME
|| posControl
.waypointList
[posControl
.activeWaypointIndex
].action
== NAV_WP_ACTION_LAND
)) {
3089 float wpSpecificSpeed
= 0.0f
;
3090 if(posControl
.waypointList
[posControl
.activeWaypointIndex
].action
== NAV_WP_ACTION_HOLD_TIME
)
3091 wpSpecificSpeed
= posControl
.waypointList
[posControl
.activeWaypointIndex
].p2
; // P1 is hold time
3093 wpSpecificSpeed
= posControl
.waypointList
[posControl
.activeWaypointIndex
].p1
; // default case
3095 if (wpSpecificSpeed
>= 50.0f
&& wpSpecificSpeed
<= navConfig()->general
.max_auto_speed
) {
3096 waypointSpeed
= wpSpecificSpeed
;
3097 } else if (wpSpecificSpeed
> navConfig()->general
.max_auto_speed
) {
3098 waypointSpeed
= navConfig()->general
.max_auto_speed
;
3103 return waypointSpeed
;
3107 /*-----------------------------------------------------------
3108 * Process adjustments to alt, pos and yaw controllers
3109 *-----------------------------------------------------------*/
3110 static void processNavigationRCAdjustments(void)
3112 /* Process pilot's RC input. Disable all pilot's input when in FAILSAFE_MODE */
3113 navigationFSMStateFlags_t navStateFlags
= navGetStateFlags(posControl
.navState
);
3114 if ((navStateFlags
& NAV_RC_ALT
) && (!FLIGHT_MODE(FAILSAFE_MODE
))) {
3115 posControl
.flags
.isAdjustingAltitude
= adjustAltitudeFromRCInput();
3118 posControl
.flags
.isAdjustingAltitude
= false;
3121 if (navStateFlags
& NAV_RC_POS
) {
3122 if (!FLIGHT_MODE(FAILSAFE_MODE
)) {
3123 posControl
.flags
.isAdjustingPosition
= adjustPositionFromRCInput();
3126 if (!STATE(FIXED_WING_LEGACY
)) {
3127 resetMulticopterBrakingMode();
3132 posControl
.flags
.isAdjustingPosition
= false;
3135 if ((navStateFlags
& NAV_RC_YAW
) && (!FLIGHT_MODE(FAILSAFE_MODE
))) {
3136 posControl
.flags
.isAdjustingHeading
= adjustHeadingFromRCInput();
3139 posControl
.flags
.isAdjustingHeading
= false;
3143 /*-----------------------------------------------------------
3144 * A main function to call position controllers at loop rate
3145 *-----------------------------------------------------------*/
3146 void applyWaypointNavigationAndAltitudeHold(void)
3148 const timeUs_t currentTimeUs
= micros();
3150 //Updata blackbox data
3152 if (posControl
.flags
.estAltStatus
== EST_TRUSTED
) navFlags
|= (1 << 0);
3153 if (posControl
.flags
.estAglStatus
== EST_TRUSTED
) navFlags
|= (1 << 1);
3154 if (posControl
.flags
.estPosStatus
== EST_TRUSTED
) navFlags
|= (1 << 2);
3155 if (posControl
.flags
.isTerrainFollowEnabled
) navFlags
|= (1 << 3);
3156 #if defined(NAV_GPS_GLITCH_DETECTION)
3157 if (isGPSGlitchDetected()) navFlags
|= (1 << 4);
3159 if (posControl
.flags
.estHeadingStatus
== EST_TRUSTED
) navFlags
|= (1 << 5);
3161 // Reset all navigation requests - NAV controllers will set them if necessary
3162 DISABLE_STATE(NAV_MOTOR_STOP_OR_IDLE
);
3164 // No navigation when disarmed
3165 if (!ARMING_FLAG(ARMED
)) {
3166 // If we are disarmed, abort forced RTH or Emergency Landing
3167 posControl
.flags
.forcedRTHActivated
= false;
3168 posControl
.flags
.forcedEmergLandingActivated
= false;
3169 // ensure WP missions always restart from first waypoint after disarm
3170 posControl
.activeWaypointIndex
= 0;
3175 posControl
.flags
.horizontalPositionDataConsumed
= false;
3176 posControl
.flags
.verticalPositionDataConsumed
= false;
3178 /* Process controllers */
3179 navigationFSMStateFlags_t navStateFlags
= navGetStateFlags(posControl
.navState
);
3180 if (STATE(ROVER
) || STATE(BOAT
)) {
3181 applyRoverBoatNavigationController(navStateFlags
, currentTimeUs
);
3182 } else if (STATE(FIXED_WING_LEGACY
)) {
3183 applyFixedWingNavigationController(navStateFlags
, currentTimeUs
);
3186 applyMulticopterNavigationController(navStateFlags
, currentTimeUs
);
3189 /* Consume position data */
3190 if (posControl
.flags
.horizontalPositionDataConsumed
)
3191 posControl
.flags
.horizontalPositionDataNew
= false;
3193 if (posControl
.flags
.verticalPositionDataConsumed
)
3194 posControl
.flags
.verticalPositionDataNew
= false;
3196 //Update blackbox data
3197 if (posControl
.flags
.isAdjustingPosition
) navFlags
|= (1 << 6);
3198 if (posControl
.flags
.isAdjustingAltitude
) navFlags
|= (1 << 7);
3199 if (posControl
.flags
.isAdjustingHeading
) navFlags
|= (1 << 8);
3201 navTargetPosition
[X
] = lrintf(posControl
.desiredState
.pos
.x
);
3202 navTargetPosition
[Y
] = lrintf(posControl
.desiredState
.pos
.y
);
3203 navTargetPosition
[Z
] = lrintf(posControl
.desiredState
.pos
.z
);
3206 /*-----------------------------------------------------------
3207 * Set CF's FLIGHT_MODE from current NAV_MODE
3208 *-----------------------------------------------------------*/
3209 void switchNavigationFlightModes(void)
3211 const flightModeFlags_e enabledNavFlightModes
= navGetMappedFlightModes(posControl
.navState
);
3212 const flightModeFlags_e disabledFlightModes
= (NAV_ALTHOLD_MODE
| NAV_RTH_MODE
| NAV_POSHOLD_MODE
| NAV_WP_MODE
| NAV_LAUNCH_MODE
| NAV_COURSE_HOLD_MODE
) & (~enabledNavFlightModes
);
3213 DISABLE_FLIGHT_MODE(disabledFlightModes
);
3214 ENABLE_FLIGHT_MODE(enabledNavFlightModes
);
3217 /*-----------------------------------------------------------
3218 * desired NAV_MODE from combination of FLIGHT_MODE flags
3219 *-----------------------------------------------------------*/
3220 static bool canActivateAltHoldMode(void)
3222 return (posControl
.flags
.estAltStatus
>= EST_USABLE
);
3225 static bool canActivatePosHoldMode(void)
3227 return (posControl
.flags
.estPosStatus
>= EST_USABLE
) && (posControl
.flags
.estVelStatus
== EST_TRUSTED
) && (posControl
.flags
.estHeadingStatus
>= EST_USABLE
);
3230 static bool canActivateNavigationModes(void)
3232 return (posControl
.flags
.estPosStatus
== EST_TRUSTED
) && (posControl
.flags
.estVelStatus
== EST_TRUSTED
) && (posControl
.flags
.estHeadingStatus
>= EST_USABLE
);
3235 static bool isWaypointMissionValid(void)
3237 return posControl
.waypointListValid
&& (posControl
.waypointCount
> 0);
3240 static navigationFSMEvent_t
selectNavEventFromBoxModeInput(void)
3242 static bool canActivateWaypoint
= false;
3243 static bool canActivateLaunchMode
= false;
3245 //We can switch modes only when ARMED
3246 if (ARMING_FLAG(ARMED
)) {
3247 // Ask failsafe system if we can use navigation system
3248 if (failsafeBypassNavigation()) {
3249 return NAV_FSM_EVENT_SWITCH_TO_IDLE
;
3252 // Flags if we can activate certain nav modes (check if we have required sensors and they provide valid data)
3253 const bool canActivateAltHold
= canActivateAltHoldMode();
3254 const bool canActivatePosHold
= canActivatePosHoldMode();
3255 const bool canActivateNavigation
= canActivateNavigationModes();
3256 const bool isExecutingRTH
= navGetStateFlags(posControl
.navState
) & NAV_AUTO_RTH
;
3257 checkSafeHomeState(isExecutingRTH
|| posControl
.flags
.forcedRTHActivated
);
3259 /* Emergency landing triggered by failsafe when Failsafe procedure set to Landing */
3260 if (posControl
.flags
.forcedEmergLandingActivated
) {
3261 return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
;
3264 /* Keep Emergency landing mode active once triggered.
3265 * If caused by sensor failure - landing auto cancelled if sensors working again or when WP and RTH deselected or if Althold selected.
3266 * If caused by RTH Sanity Checking - landing cancelled if RTH deselected.
3267 * Remains active if failsafe active regardless of mode selections */
3269 if (navigationIsExecutingAnEmergencyLanding()) {
3270 bool autonomousNavIsPossible
= canActivateNavigation
&& canActivateAltHold
&& STATE(GPS_FIX_HOME
);
3271 bool emergLandingCancel
= (!autonomousNavIsPossible
&&
3272 ((IS_RC_MODE_ACTIVE(BOXNAVALTHOLD
) && canActivateAltHold
) || !(IS_RC_MODE_ACTIVE(BOXNAVWP
) || IS_RC_MODE_ACTIVE(BOXNAVRTH
)))) ||
3273 (autonomousNavIsPossible
&& !IS_RC_MODE_ACTIVE(BOXNAVRTH
));
3275 if ((!posControl
.rthSanityChecker
.rthSanityOK
|| !autonomousNavIsPossible
) && (!emergLandingCancel
|| FLIGHT_MODE(FAILSAFE_MODE
))) {
3276 return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
;
3279 posControl
.rthSanityChecker
.rthSanityOK
= true;
3281 // Keep canActivateWaypoint flag at FALSE if there is no mission loaded
3282 // Also block WP mission if we are executing RTH
3283 if (!isWaypointMissionValid() || isExecutingRTH
) {
3284 canActivateWaypoint
= false;
3287 /* Airplane specific modes */
3288 if (STATE(AIRPLANE
)) {
3289 // LAUNCH mode has priority over any other NAV mode
3290 if (isNavLaunchEnabled()) { // FIXME: Only available for fixed wing aircrafts now
3291 if (canActivateLaunchMode
) {
3292 canActivateLaunchMode
= false;
3293 return NAV_FSM_EVENT_SWITCH_TO_LAUNCH
;
3295 else if FLIGHT_MODE(NAV_LAUNCH_MODE
) {
3296 // Make sure we don't bail out to IDLE
3297 return NAV_FSM_EVENT_NONE
;
3301 // If we were in LAUNCH mode - force switch to IDLE only if the throttle is low
3302 if (FLIGHT_MODE(NAV_LAUNCH_MODE
)) {
3303 throttleStatus_e throttleStatus
= calculateThrottleStatus(THROTTLE_STATUS_TYPE_RC
);
3304 if (throttleStatus
!= THROTTLE_LOW
)
3305 return NAV_FSM_EVENT_NONE
;
3307 return NAV_FSM_EVENT_SWITCH_TO_IDLE
;
3311 /* Soaring mode, disables altitude control in Position hold and Course hold modes.
3312 * Pitch allowed to freefloat within defined Angle mode deadband */
3313 if (IS_RC_MODE_ACTIVE(BOXSOARING
) && (FLIGHT_MODE(NAV_POSHOLD_MODE
) || FLIGHT_MODE(NAV_COURSE_HOLD_MODE
))) {
3314 if (!FLIGHT_MODE(SOARING_MODE
)) {
3315 ENABLE_FLIGHT_MODE(SOARING_MODE
);
3318 DISABLE_FLIGHT_MODE(SOARING_MODE
);
3322 // Failsafe_RTH (can override MANUAL)
3323 if (posControl
.flags
.forcedRTHActivated
) {
3324 // If we request forced RTH - attempt to activate it no matter what
3325 // This might switch to emergency landing controller if GPS is unavailable
3326 return NAV_FSM_EVENT_SWITCH_TO_RTH
;
3329 /* Pilot-triggered RTH (can override MANUAL), also fall-back for WP if there is no mission loaded
3330 * Prevent MANUAL falling back to RTH if selected during active mission (canActivateWaypoint is set false on MANUAL selection)
3331 * Also prevent WP falling back to RTH if WP mission planner is active */
3332 const bool blockWPFallback
= IS_RC_MODE_ACTIVE(BOXMANUAL
) || posControl
.flags
.wpMissionPlannerActive
;
3333 if (IS_RC_MODE_ACTIVE(BOXNAVRTH
) || (IS_RC_MODE_ACTIVE(BOXNAVWP
) && !canActivateWaypoint
&& !blockWPFallback
)) {
3334 // Check for isExecutingRTH to prevent switching our from RTH in case of a brief GPS loss
3335 // If don't keep this, loss of any of the canActivateNavigation && canActivateAltHold
3336 // will kick us out of RTH state machine via NAV_FSM_EVENT_SWITCH_TO_IDLE and will prevent any of the fall-back
3337 // logic to kick in (waiting for GPS on airplanes, switch to emergency landing etc)
3338 if (isExecutingRTH
|| (canActivateNavigation
&& canActivateAltHold
&& STATE(GPS_FIX_HOME
))) {
3339 return NAV_FSM_EVENT_SWITCH_TO_RTH
;
3343 // MANUAL mode has priority over WP/PH/AH
3344 if (IS_RC_MODE_ACTIVE(BOXMANUAL
)) {
3345 canActivateWaypoint
= false; // Block WP mode if we are in PASSTHROUGH mode
3346 return NAV_FSM_EVENT_SWITCH_TO_IDLE
;
3349 // Pilot-activated waypoint mission. Fall-back to RTH in case of no mission loaded
3350 // Block activation if using WP Mission Planner
3351 if (IS_RC_MODE_ACTIVE(BOXNAVWP
) && !posControl
.flags
.wpMissionPlannerActive
) {
3352 if (FLIGHT_MODE(NAV_WP_MODE
) || (canActivateWaypoint
&& canActivateNavigation
&& canActivateAltHold
&& STATE(GPS_FIX_HOME
)))
3353 return NAV_FSM_EVENT_SWITCH_TO_WAYPOINT
;
3356 // Arm the state variable if the WP BOX mode is not enabled
3357 canActivateWaypoint
= true;
3360 if (IS_RC_MODE_ACTIVE(BOXNAVPOSHOLD
)) {
3361 if (FLIGHT_MODE(NAV_POSHOLD_MODE
) || (canActivatePosHold
&& canActivateAltHold
))
3362 return NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
;
3365 // CRUISE has priority over COURSE_HOLD and AH
3366 if (IS_RC_MODE_ACTIVE(BOXNAVCRUISE
)) {
3367 if ((FLIGHT_MODE(NAV_COURSE_HOLD_MODE
) && FLIGHT_MODE(NAV_ALTHOLD_MODE
)) || (canActivatePosHold
&& canActivateAltHold
))
3368 return NAV_FSM_EVENT_SWITCH_TO_CRUISE
;
3371 // PH has priority over COURSE_HOLD
3372 // CRUISE has priority on AH
3373 if (IS_RC_MODE_ACTIVE(BOXNAVCOURSEHOLD
)) {
3375 if (IS_RC_MODE_ACTIVE(BOXNAVALTHOLD
) && ((FLIGHT_MODE(NAV_COURSE_HOLD_MODE
) && FLIGHT_MODE(NAV_ALTHOLD_MODE
)) || (canActivatePosHold
&& canActivateAltHold
)))
3376 return NAV_FSM_EVENT_SWITCH_TO_CRUISE
;
3378 if (FLIGHT_MODE(NAV_COURSE_HOLD_MODE
) || (canActivatePosHold
))
3379 return NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD
;
3383 if (IS_RC_MODE_ACTIVE(BOXNAVALTHOLD
)) {
3384 if ((FLIGHT_MODE(NAV_ALTHOLD_MODE
)) || (canActivateAltHold
))
3385 return NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
;
3389 canActivateWaypoint
= false;
3391 // Launch mode can be activated if feature FW_LAUNCH is enabled or BOX is turned on prior to arming (avoid switching to LAUNCH in flight)
3392 canActivateLaunchMode
= isNavLaunchEnabled();
3395 return NAV_FSM_EVENT_SWITCH_TO_IDLE
;
3398 /*-----------------------------------------------------------
3399 * An indicator that throttle tilt compensation is forced
3400 *-----------------------------------------------------------*/
3401 bool navigationRequiresThrottleTiltCompensation(void)
3403 return !STATE(FIXED_WING_LEGACY
) && (navGetStateFlags(posControl
.navState
) & NAV_REQUIRE_THRTILT
);
3406 /*-----------------------------------------------------------
3407 * An indicator that ANGLE mode must be forced per NAV requirement
3408 *-----------------------------------------------------------*/
3409 bool navigationRequiresAngleMode(void)
3411 const navigationFSMStateFlags_t currentState
= navGetStateFlags(posControl
.navState
);
3412 return (currentState
& NAV_REQUIRE_ANGLE
) || ((currentState
& NAV_REQUIRE_ANGLE_FW
) && STATE(FIXED_WING_LEGACY
));
3415 /*-----------------------------------------------------------
3416 * An indicator that TURN ASSISTANCE is required for navigation
3417 *-----------------------------------------------------------*/
3418 bool navigationRequiresTurnAssistance(void)
3420 const navigationFSMStateFlags_t currentState
= navGetStateFlags(posControl
.navState
);
3421 if (STATE(FIXED_WING_LEGACY
)) {
3422 // For airplanes turn assistant is always required when controlling position
3423 return (currentState
& (NAV_CTL_POS
| NAV_CTL_ALT
));
3431 * An indicator that NAV is in charge of heading control (a signal to disable other heading controllers)
3433 int8_t navigationGetHeadingControlState(void)
3435 // For airplanes report as manual heading control
3436 if (STATE(FIXED_WING_LEGACY
)) {
3437 return NAV_HEADING_CONTROL_MANUAL
;
3440 // For multirotors it depends on navigation system mode
3441 if (navGetStateFlags(posControl
.navState
) & NAV_REQUIRE_MAGHOLD
) {
3442 if (posControl
.flags
.isAdjustingHeading
) {
3443 return NAV_HEADING_CONTROL_MANUAL
;
3446 return NAV_HEADING_CONTROL_AUTO
;
3450 return NAV_HEADING_CONTROL_NONE
;
3454 bool navigationTerrainFollowingEnabled(void)
3456 return posControl
.flags
.isTerrainFollowEnabled
;
3459 uint32_t distanceToFirstWP(void)
3461 fpVector3_t startingWaypointPos
;
3462 #ifdef USE_MULTI_MISSION
3463 mapWaypointToLocalPosition(&startingWaypointPos
, &posControl
.waypointList
[posControl
.loadedMultiMissionStartWP
], GEO_ALT_RELATIVE
);
3465 mapWaypointToLocalPosition(&startingWaypointPos
, &posControl
.waypointList
[0], GEO_ALT_RELATIVE
);
3467 return calculateDistanceToDestination(&startingWaypointPos
);
3470 navArmingBlocker_e
navigationIsBlockingArming(bool *usedBypass
)
3472 const bool navBoxModesEnabled
= IS_RC_MODE_ACTIVE(BOXNAVRTH
) || IS_RC_MODE_ACTIVE(BOXNAVWP
) || IS_RC_MODE_ACTIVE(BOXNAVPOSHOLD
) || (STATE(FIXED_WING_LEGACY
) && IS_RC_MODE_ACTIVE(BOXNAVALTHOLD
)) || (STATE(FIXED_WING_LEGACY
) && (IS_RC_MODE_ACTIVE(BOXNAVCOURSEHOLD
) || IS_RC_MODE_ACTIVE(BOXNAVCRUISE
)));
3473 const bool navLaunchComboModesEnabled
= isNavLaunchEnabled() && (IS_RC_MODE_ACTIVE(BOXNAVRTH
) || IS_RC_MODE_ACTIVE(BOXNAVWP
) || IS_RC_MODE_ACTIVE(BOXNAVALTHOLD
) || IS_RC_MODE_ACTIVE(BOXNAVCOURSEHOLD
) || IS_RC_MODE_ACTIVE(BOXNAVCRUISE
));
3476 *usedBypass
= false;
3479 if (navConfig()->general
.flags
.extra_arming_safety
== NAV_EXTRA_ARMING_SAFETY_OFF
) {
3480 return NAV_ARMING_BLOCKER_NONE
;
3483 // Apply extra arming safety only if pilot has any of GPS modes configured
3484 if ((isUsingNavigationModes() || failsafeMayRequireNavigationMode()) && !((posControl
.flags
.estPosStatus
>= EST_USABLE
) && STATE(GPS_FIX_HOME
))) {
3485 if (navConfig()->general
.flags
.extra_arming_safety
== NAV_EXTRA_ARMING_SAFETY_ALLOW_BYPASS
&&
3486 (STATE(NAV_EXTRA_ARMING_SAFETY_BYPASSED
) || checkStickPosition(YAW_HI
))) {
3490 return NAV_ARMING_BLOCKER_NONE
;
3492 return NAV_ARMING_BLOCKER_MISSING_GPS_FIX
;
3495 // Don't allow arming if any of NAV modes is active
3496 if (!ARMING_FLAG(ARMED
) && navBoxModesEnabled
&& !navLaunchComboModesEnabled
) {
3497 return NAV_ARMING_BLOCKER_NAV_IS_ALREADY_ACTIVE
;
3500 // Don't allow arming if first waypoint is farther than configured safe distance
3501 if ((posControl
.waypointCount
> 0) && (navConfig()->general
.waypoint_safe_distance
!= 0)) {
3502 if (distanceToFirstWP() > navConfig()->general
.waypoint_safe_distance
&& !checkStickPosition(YAW_HI
)) {
3503 return NAV_ARMING_BLOCKER_FIRST_WAYPOINT_TOO_FAR
;
3508 * Don't allow arming if any of JUMP waypoint has invalid settings
3509 * First WP can't be JUMP
3510 * Can't jump to immediately adjacent WPs (pointless)
3511 * Can't jump beyond WP list
3512 * Only jump to geo-referenced WP types
3514 #ifdef USE_MULTI_MISSION
3515 // Only perform check when mission loaded at start of posControl.waypointList
3516 if (posControl
.waypointCount
&& !posControl
.loadedMultiMissionStartWP
) {
3518 if (posControl
.waypointCount
) {
3520 for (uint8_t wp
= 0; wp
< posControl
.waypointCount
; wp
++){
3521 if (posControl
.waypointList
[wp
].action
== NAV_WP_ACTION_JUMP
){
3522 if((wp
== 0) || ((posControl
.waypointList
[wp
].p1
> (wp
-2)) && (posControl
.waypointList
[wp
].p1
< (wp
+2)) ) || (posControl
.waypointList
[wp
].p1
>= posControl
.waypointCount
) || (posControl
.waypointList
[wp
].p2
< -1)) {
3523 return NAV_ARMING_BLOCKER_JUMP_WAYPOINT_ERROR
;
3525 /* check for target geo-ref sanity */
3526 uint16_t target
= posControl
.waypointList
[wp
].p1
;
3527 if(!(posControl
.waypointList
[target
].action
== NAV_WP_ACTION_WAYPOINT
|| posControl
.waypointList
[target
].action
== NAV_WP_ACTION_HOLD_TIME
|| posControl
.waypointList
[target
].action
== NAV_WP_ACTION_LAND
)) {
3528 return NAV_ARMING_BLOCKER_JUMP_WAYPOINT_ERROR
;
3534 return NAV_ARMING_BLOCKER_NONE
;
3537 bool navigationPositionEstimateIsHealthy(void)
3539 return (posControl
.flags
.estPosStatus
>= EST_USABLE
) && STATE(GPS_FIX_HOME
);
3543 * Indicate ready/not ready status
3545 static void updateReadyStatus(void)
3547 static bool posReadyBeepDone
= false;
3549 /* Beep out READY_BEEP once when position lock is firstly acquired and HOME set */
3550 if (navigationPositionEstimateIsHealthy() && !posReadyBeepDone
) {
3551 beeper(BEEPER_READY_BEEP
);
3552 posReadyBeepDone
= true;
3556 void updateFlightBehaviorModifiers(void)
3558 if (posControl
.flags
.isGCSAssistedNavigationEnabled
&& !IS_RC_MODE_ACTIVE(BOXGCSNAV
)) {
3559 posControl
.flags
.isGCSAssistedNavigationReset
= true;
3562 posControl
.flags
.isGCSAssistedNavigationEnabled
= IS_RC_MODE_ACTIVE(BOXGCSNAV
);
3565 /* On the fly WP mission planner mode allows WP missions to be setup during navigation.
3566 * Uses the WP mode switch to save WP at current location (WP mode disabled when active)
3567 * Mission can be flown after mission planner mode switched off and saved after disarm. */
3569 void updateWpMissionPlanner(void)
3571 static timeMs_t resetTimerStart
= 0;
3572 if (IS_RC_MODE_ACTIVE(BOXPLANWPMISSION
) && !(FLIGHT_MODE(NAV_WP_MODE
) || isWaypointMissionRTHActive())) {
3573 const bool positionTrusted
= posControl
.flags
.estAltStatus
== EST_TRUSTED
&& posControl
.flags
.estPosStatus
== EST_TRUSTED
&& STATE(GPS_FIX
);
3575 posControl
.flags
.wpMissionPlannerActive
= true;
3576 if (millis() - resetTimerStart
< 1000 && navConfig()->general
.flags
.mission_planner_reset
) {
3577 posControl
.waypointCount
= posControl
.wpPlannerActiveWPIndex
= 0;
3578 posControl
.waypointListValid
= false;
3579 posControl
.wpMissionPlannerStatus
= WP_PLAN_WAIT
;
3581 if (positionTrusted
&& posControl
.wpMissionPlannerStatus
!= WP_PLAN_FULL
) {
3582 missionPlannerSetWaypoint();
3584 posControl
.wpMissionPlannerStatus
= posControl
.wpMissionPlannerStatus
== WP_PLAN_FULL
? WP_PLAN_FULL
: WP_PLAN_WAIT
;
3586 } else if (posControl
.flags
.wpMissionPlannerActive
) {
3587 posControl
.flags
.wpMissionPlannerActive
= false;
3588 posControl
.activeWaypointIndex
= 0;
3589 resetTimerStart
= millis();
3593 void missionPlannerSetWaypoint(void)
3595 static bool boxWPModeIsReset
= true;
3597 boxWPModeIsReset
= !boxWPModeIsReset
? !IS_RC_MODE_ACTIVE(BOXNAVWP
) : boxWPModeIsReset
; // only able to save new WP when WP mode reset
3598 posControl
.wpMissionPlannerStatus
= boxWPModeIsReset
? boxWPModeIsReset
: posControl
.wpMissionPlannerStatus
; // hold save status until WP mode reset
3600 if (!boxWPModeIsReset
|| !IS_RC_MODE_ACTIVE(BOXNAVWP
)) {
3604 if (!posControl
.wpPlannerActiveWPIndex
) { // reset existing mission data before adding first WP
3605 resetWaypointList();
3608 gpsLocation_t wpLLH
;
3609 geoConvertLocalToGeodetic(&wpLLH
, &posControl
.gpsOrigin
, &navGetCurrentActualPositionAndVelocity()->pos
);
3611 posControl
.waypointList
[posControl
.wpPlannerActiveWPIndex
].action
= 1;
3612 posControl
.waypointList
[posControl
.wpPlannerActiveWPIndex
].lat
= wpLLH
.lat
;
3613 posControl
.waypointList
[posControl
.wpPlannerActiveWPIndex
].lon
= wpLLH
.lon
;
3614 posControl
.waypointList
[posControl
.wpPlannerActiveWPIndex
].alt
= wpLLH
.alt
;
3615 posControl
.waypointList
[posControl
.wpPlannerActiveWPIndex
].p1
= posControl
.waypointList
[posControl
.wpPlannerActiveWPIndex
].p2
= 0;
3616 posControl
.waypointList
[posControl
.wpPlannerActiveWPIndex
].p3
= 1; // use absolute altitude datum
3617 posControl
.waypointList
[posControl
.wpPlannerActiveWPIndex
].flag
= NAV_WP_FLAG_LAST
;
3618 posControl
.waypointListValid
= true;
3620 if (posControl
.wpPlannerActiveWPIndex
) {
3621 posControl
.waypointList
[posControl
.wpPlannerActiveWPIndex
- 1].flag
= 0; // rollling reset of previous end of mission flag when new WP added
3624 posControl
.wpPlannerActiveWPIndex
+= 1;
3625 posControl
.waypointCount
= posControl
.geoWaypointCount
= posControl
.wpPlannerActiveWPIndex
;
3626 posControl
.wpMissionPlannerStatus
= posControl
.waypointCount
== NAV_MAX_WAYPOINTS
? WP_PLAN_FULL
: WP_PLAN_OK
;
3627 boxWPModeIsReset
= false;
3631 * Process NAV mode transition and WP/RTH state machine
3632 * Update rate: RX (data driven or 50Hz)
3634 void updateWaypointsAndNavigationMode(void)
3636 /* Initiate home position update */
3637 updateHomePosition();
3639 /* Update flight statistics */
3640 updateNavigationFlightStatistics();
3642 /* Update NAV ready status */
3643 updateReadyStatus();
3645 // Update flight behaviour modifiers
3646 updateFlightBehaviorModifiers();
3648 // Process switch to a different navigation mode (if needed)
3649 navProcessFSMEvents(selectNavEventFromBoxModeInput());
3651 // Process pilot's RC input to adjust behaviour
3652 processNavigationRCAdjustments();
3654 // Map navMode back to enabled flight modes
3655 switchNavigationFlightModes();
3657 // Update WP mission planner
3658 updateWpMissionPlanner();
3660 //Update Blackbox data
3661 navCurrentState
= (int16_t)posControl
.navPersistentId
;
3664 /*-----------------------------------------------------------
3665 * NAV main control functions
3666 *-----------------------------------------------------------*/
3667 void navigationUsePIDs(void)
3669 /** Multicopter PIDs */
3670 // Brake time parameter
3671 posControl
.posDecelerationTime
= (float)navConfig()->mc
.posDecelerationTime
/ 100.0f
;
3673 // Position controller expo (taret vel expo for MC)
3674 posControl
.posResponseExpo
= constrainf((float)navConfig()->mc
.posResponseExpo
/ 100.0f
, 0.0f
, 1.0f
);
3676 // Initialize position hold P-controller
3677 for (int axis
= 0; axis
< 2; axis
++) {
3679 &posControl
.pids
.pos
[axis
],
3680 (float)pidProfile()->bank_mc
.pid
[PID_POS_XY
].P
/ 100.0f
,
3687 navPidInit(&posControl
.pids
.vel
[axis
], (float)pidProfile()->bank_mc
.pid
[PID_VEL_XY
].P
/ 20.0f
,
3688 (float)pidProfile()->bank_mc
.pid
[PID_VEL_XY
].I
/ 100.0f
,
3689 (float)pidProfile()->bank_mc
.pid
[PID_VEL_XY
].D
/ 100.0f
,
3690 (float)pidProfile()->bank_mc
.pid
[PID_VEL_XY
].FF
/ 100.0f
,
3691 pidProfile()->navVelXyDTermLpfHz
3696 * Set coefficients used in MC VEL_XY
3698 multicopterPosXyCoefficients
.dTermAttenuation
= pidProfile()->navVelXyDtermAttenuation
/ 100.0f
;
3699 multicopterPosXyCoefficients
.dTermAttenuationStart
= pidProfile()->navVelXyDtermAttenuationStart
/ 100.0f
;
3700 multicopterPosXyCoefficients
.dTermAttenuationEnd
= pidProfile()->navVelXyDtermAttenuationEnd
/ 100.0f
;
3702 #ifdef USE_MR_BRAKING_MODE
3703 multicopterPosXyCoefficients
.breakingBoostFactor
= (float) navConfig()->mc
.braking_boost_factor
/ 100.0f
;
3706 // Initialize altitude hold PID-controllers (pos_z, vel_z, acc_z
3708 &posControl
.pids
.pos
[Z
],
3709 (float)pidProfile()->bank_mc
.pid
[PID_POS_Z
].P
/ 100.0f
,
3716 navPidInit(&posControl
.pids
.vel
[Z
], (float)pidProfile()->bank_mc
.pid
[PID_VEL_Z
].P
/ 66.7f
,
3717 (float)pidProfile()->bank_mc
.pid
[PID_VEL_Z
].I
/ 20.0f
,
3718 (float)pidProfile()->bank_mc
.pid
[PID_VEL_Z
].D
/ 100.0f
,
3723 // Initialize surface tracking PID
3724 navPidInit(&posControl
.pids
.surface
, 2.0f
,
3731 /** Airplane PIDs */
3732 // Initialize fixed wing PID controllers
3733 navPidInit(&posControl
.pids
.fw_nav
, (float)pidProfile()->bank_fw
.pid
[PID_POS_XY
].P
/ 100.0f
,
3734 (float)pidProfile()->bank_fw
.pid
[PID_POS_XY
].I
/ 100.0f
,
3735 (float)pidProfile()->bank_fw
.pid
[PID_POS_XY
].D
/ 100.0f
,
3740 navPidInit(&posControl
.pids
.fw_alt
, (float)pidProfile()->bank_fw
.pid
[PID_POS_Z
].P
/ 10.0f
,
3741 (float)pidProfile()->bank_fw
.pid
[PID_POS_Z
].I
/ 10.0f
,
3742 (float)pidProfile()->bank_fw
.pid
[PID_POS_Z
].D
/ 10.0f
,
3747 navPidInit(&posControl
.pids
.fw_heading
, (float)pidProfile()->bank_fw
.pid
[PID_POS_HEADING
].P
/ 10.0f
,
3748 (float)pidProfile()->bank_fw
.pid
[PID_POS_HEADING
].I
/ 10.0f
,
3749 (float)pidProfile()->bank_fw
.pid
[PID_POS_HEADING
].D
/ 100.0f
,
3755 void navigationInit(void)
3758 posControl
.navState
= NAV_STATE_IDLE
;
3760 posControl
.flags
.horizontalPositionDataNew
= false;
3761 posControl
.flags
.verticalPositionDataNew
= false;
3763 posControl
.flags
.estAltStatus
= EST_NONE
;
3764 posControl
.flags
.estPosStatus
= EST_NONE
;
3765 posControl
.flags
.estVelStatus
= EST_NONE
;
3766 posControl
.flags
.estHeadingStatus
= EST_NONE
;
3767 posControl
.flags
.estAglStatus
= EST_NONE
;
3769 posControl
.flags
.forcedRTHActivated
= false;
3770 posControl
.flags
.forcedEmergLandingActivated
= false;
3771 posControl
.waypointCount
= 0;
3772 posControl
.activeWaypointIndex
= 0;
3773 posControl
.waypointListValid
= false;
3774 posControl
.wpPlannerActiveWPIndex
= 0;
3775 posControl
.flags
.wpMissionPlannerActive
= false;
3776 #ifdef USE_MULTI_MISSION
3777 posControl
.multiMissionCount
= 0;
3778 posControl
.loadedMultiMissionStartWP
= 0;
3780 /* Set initial surface invalid */
3781 posControl
.actualState
.surfaceMin
= -1.0f
;
3783 /* Reset statistics */
3784 posControl
.totalTripDistance
= 0.0f
;
3786 /* Use system config */
3787 navigationUsePIDs();
3790 mixerConfig()->platformType
== PLATFORM_BOAT
||
3791 mixerConfig()->platformType
== PLATFORM_ROVER
||
3792 navConfig()->fw
.useFwNavYawControl
3794 ENABLE_STATE(FW_HEADING_USE_YAW
);
3796 DISABLE_STATE(FW_HEADING_USE_YAW
);
3798 #if defined(NAV_NON_VOLATILE_WAYPOINT_STORAGE)
3799 /* configure WP missions at boot */
3800 #ifdef USE_MULTI_MISSION
3801 for (int8_t i
= 0; i
< NAV_MAX_WAYPOINTS
; i
++) { // check number missions in NVM
3802 if (checkMissionCount(i
)) {
3806 /* set index to 1 if saved mission index > available missions */
3807 if (navConfig()->general
.waypoint_multi_mission_index
> posControl
.multiMissionCount
) {
3808 navConfigMutable()->general
.waypoint_multi_mission_index
= 1;
3811 /* load mission on boot */
3812 if (navConfig()->general
.waypoint_load_on_boot
) {
3813 loadNonVolatileWaypointList(false);
3818 /*-----------------------------------------------------------
3819 * Access to estimated position/velocity data
3820 *-----------------------------------------------------------*/
3821 float getEstimatedActualVelocity(int axis
)
3823 return navGetCurrentActualPositionAndVelocity()->vel
.v
[axis
];
3826 float getEstimatedActualPosition(int axis
)
3828 return navGetCurrentActualPositionAndVelocity()->pos
.v
[axis
];
3831 /*-----------------------------------------------------------
3832 * Ability to execute RTH on external event
3833 *-----------------------------------------------------------*/
3834 void activateForcedRTH(void)
3836 abortFixedWingLaunch();
3837 posControl
.flags
.forcedRTHActivated
= true;
3838 checkSafeHomeState(true);
3839 navProcessFSMEvents(selectNavEventFromBoxModeInput());
3842 void abortForcedRTH(void)
3844 // Disable failsafe RTH and make sure we back out of navigation mode to IDLE
3845 // If any navigation mode was active prior to RTH it will be re-enabled with next RX update
3846 posControl
.flags
.forcedRTHActivated
= false;
3847 checkSafeHomeState(false);
3848 navProcessFSMEvents(NAV_FSM_EVENT_SWITCH_TO_IDLE
);
3851 rthState_e
getStateOfForcedRTH(void)
3853 /* If forced RTH activated and in AUTO_RTH or EMERG state */
3854 if (posControl
.flags
.forcedRTHActivated
&& (navGetStateFlags(posControl
.navState
) & (NAV_AUTO_RTH
| NAV_CTL_EMERG
))) {
3855 if (posControl
.navState
== NAV_STATE_RTH_FINISHED
|| posControl
.navState
== NAV_STATE_EMERGENCY_LANDING_FINISHED
) {
3856 return RTH_HAS_LANDED
;
3859 return RTH_IN_PROGRESS
;
3867 /*-----------------------------------------------------------
3868 * Ability to execute Emergency Landing on external event
3869 *-----------------------------------------------------------*/
3870 void activateForcedEmergLanding(void)
3872 abortFixedWingLaunch();
3873 posControl
.flags
.forcedEmergLandingActivated
= true;
3874 navProcessFSMEvents(selectNavEventFromBoxModeInput());
3877 void abortForcedEmergLanding(void)
3879 // Disable emergency landing and make sure we back out of navigation mode to IDLE
3880 // If any navigation mode was active prior to emergency landing it will be re-enabled with next RX update
3881 posControl
.flags
.forcedEmergLandingActivated
= false;
3882 navProcessFSMEvents(NAV_FSM_EVENT_SWITCH_TO_IDLE
);
3885 emergLandState_e
getStateOfForcedEmergLanding(void)
3887 /* If forced emergency landing activated and in EMERG state */
3888 if (posControl
.flags
.forcedEmergLandingActivated
&& (navGetStateFlags(posControl
.navState
) & NAV_CTL_EMERG
)) {
3889 if (posControl
.navState
== NAV_STATE_EMERGENCY_LANDING_FINISHED
) {
3890 return EMERG_LAND_HAS_LANDED
;
3892 return EMERG_LAND_IN_PROGRESS
;
3895 return EMERG_LAND_IDLE
;
3899 bool isWaypointMissionRTHActive(void)
3901 return FLIGHT_MODE(NAV_RTH_MODE
) && IS_RC_MODE_ACTIVE(BOXNAVWP
) && !(IS_RC_MODE_ACTIVE(BOXNAVRTH
) || posControl
.flags
.forcedRTHActivated
);
3904 bool navigationIsExecutingAnEmergencyLanding(void)
3906 return navGetCurrentStateFlags() & NAV_CTL_EMERG
;
3909 bool navigationInAutomaticThrottleMode(void)
3911 navigationFSMStateFlags_t stateFlags
= navGetCurrentStateFlags();
3912 return (stateFlags
& (NAV_CTL_ALT
| NAV_CTL_EMERG
| NAV_CTL_LAUNCH
| NAV_CTL_LAND
));
3915 bool navigationIsControllingThrottle(void)
3917 // Note that this makes a detour into mixer code to evaluate actual motor status
3918 return navigationInAutomaticThrottleMode() && getMotorStatus() != MOTOR_STOPPED_USER
&& !FLIGHT_MODE(SOARING_MODE
);
3921 bool navigationIsControllingAltitude(void) {
3922 navigationFSMStateFlags_t stateFlags
= navGetCurrentStateFlags();
3923 return (stateFlags
& NAV_CTL_ALT
);
3926 bool navigationIsFlyingAutonomousMode(void)
3928 navigationFSMStateFlags_t stateFlags
= navGetCurrentStateFlags();
3929 return (stateFlags
& (NAV_AUTO_RTH
| NAV_AUTO_WP
));
3932 bool navigationRTHAllowsLanding(void)
3934 // WP mission RTH landing setting
3935 if (isWaypointMissionRTHActive() && isWaypointMissionValid()) {
3936 return posControl
.waypointList
[posControl
.waypointCount
- 1].p1
> 0;
3939 // normal RTH landing setting
3940 navRTHAllowLanding_e allow
= navConfig()->general
.flags
.rth_allow_landing
;
3941 return allow
== NAV_RTH_ALLOW_LANDING_ALWAYS
||
3942 (allow
== NAV_RTH_ALLOW_LANDING_FS_ONLY
&& FLIGHT_MODE(FAILSAFE_MODE
));
3945 bool isNavLaunchEnabled(void)
3947 return IS_RC_MODE_ACTIVE(BOXNAVLAUNCH
) || feature(FEATURE_FW_LAUNCH
);
3950 int32_t navigationGetHomeHeading(void)
3952 return posControl
.rthState
.homePosition
.yaw
;
3956 float calculateAverageSpeed() {
3957 float flightTime
= getFlightTime();
3958 if (flightTime
== 0.0f
) return 0;
3959 return (float)getTotalTravelDistance() / (flightTime
* 100);
3962 const navigationPIDControllers_t
* getNavigationPIDControllers(void) {
3963 return &posControl
.pids
;
3966 bool isAdjustingPosition(void) {
3967 return posControl
.flags
.isAdjustingPosition
;
3970 bool isAdjustingHeading(void) {
3971 return posControl
.flags
.isAdjustingHeading
;
3974 int32_t getCruiseHeadingAdjustment(void) {
3975 return wrap_18000(posControl
.cruise
.yaw
- posControl
.cruise
.previousYaw
);