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/multifunction.h"
40 #include "fc/rc_controls.h"
41 #include "fc/rc_modes.h"
42 #include "fc/runtime_config.h"
43 #ifdef USE_MULTI_MISSION
44 #include "fc/rc_adjustments.h"
47 #include "fc/settings.h"
49 #include "flight/imu.h"
50 #include "flight/mixer_profile.h"
51 #include "flight/pid.h"
53 #include "io/beeper.h"
56 #include "navigation/navigation.h"
57 #include "navigation/navigation_private.h"
61 #include "sensors/sensors.h"
62 #include "sensors/acceleration.h"
63 #include "sensors/boardalignment.h"
64 #include "sensors/battery.h"
65 #include "sensors/gyro.h"
67 #include "programming/global_variables.h"
70 #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)
71 #define MR_RTH_CLIMB_MARGIN_MIN_CM 100 // start cruising home this amount of cm *before* reaching the cruise altitude (while continuing the ascend)
72 #define MR_RTH_CLIMB_MARGIN_PERCENT 15 // on high RTH altitudes use even bigger margin - percent of the altitude set
73 #define MR_RTH_LAND_MARGIN_CM 2000 // pause landing if this amount of cm *before* remaining to the home point (2D distance)
76 #define FW_RTH_CLIMB_OVERSHOOT_CM 100
77 #define FW_RTH_CLIMB_MARGIN_MIN_CM 100
78 #define FW_RTH_CLIMB_MARGIN_PERCENT 15
80 /*-----------------------------------------------------------
81 * Compatibility for home position
82 *-----------------------------------------------------------*/
83 gpsLocation_t GPS_home
;
84 uint32_t GPS_distanceToHome
; // distance to home point in meters
85 int16_t GPS_directionToHome
; // direction to home point in degrees
87 radar_pois_t radar_pois
[RADAR_MAX_POIS
];
89 #if defined(USE_SAFE_HOME)
90 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
, 2);
100 PG_REGISTER_WITH_RESET_TEMPLATE(navConfig_t
, navConfig
, PG_NAV_CONFIG
, 5);
102 PG_RESET_TEMPLATE(navConfig_t
, navConfig
,
106 .extra_arming_safety
= SETTING_NAV_EXTRA_ARMING_SAFETY_DEFAULT
,
107 .user_control_mode
= SETTING_NAV_USER_CONTROL_MODE_DEFAULT
,
108 .rth_alt_control_mode
= SETTING_NAV_RTH_ALT_MODE_DEFAULT
,
109 .rth_climb_first
= SETTING_NAV_RTH_CLIMB_FIRST_DEFAULT
, // Climb first, turn after reaching safe altitude
110 .rth_climb_first_stage_mode
= SETTING_NAV_RTH_CLIMB_FIRST_STAGE_MODE_DEFAULT
, // To determine how rth_climb_first_stage_altitude is used
111 .rth_climb_ignore_emerg
= SETTING_NAV_RTH_CLIMB_IGNORE_EMERG_DEFAULT
, // Ignore GPS loss on initial climb
112 .rth_tail_first
= SETTING_NAV_RTH_TAIL_FIRST_DEFAULT
,
113 .disarm_on_landing
= SETTING_NAV_DISARM_ON_LANDING_DEFAULT
,
114 .rth_allow_landing
= SETTING_NAV_RTH_ALLOW_LANDING_DEFAULT
,
115 .rth_alt_control_override
= SETTING_NAV_RTH_ALT_CONTROL_OVERRIDE_DEFAULT
, // Override RTH Altitude and Climb First using Pitch and Roll stick
116 .nav_overrides_motor_stop
= SETTING_NAV_OVERRIDES_MOTOR_STOP_DEFAULT
,
117 .safehome_usage_mode
= SETTING_SAFEHOME_USAGE_MODE_DEFAULT
,
118 .mission_planner_reset
= SETTING_NAV_MISSION_PLANNER_RESET_DEFAULT
, // Allow mode switch toggle to reset Mission Planner WPs
119 .waypoint_mission_restart
= SETTING_NAV_WP_MISSION_RESTART_DEFAULT
, // WP mission restart action
120 .soaring_motor_stop
= SETTING_NAV_FW_SOARING_MOTOR_STOP_DEFAULT
, // stops motor when Saoring mode enabled
121 .rth_trackback_mode
= SETTING_NAV_RTH_TRACKBACK_MODE_DEFAULT
, // RTH trackback useage mode
122 .rth_use_linear_descent
= SETTING_NAV_RTH_USE_LINEAR_DESCENT_DEFAULT
, // Use linear descent during RTH
123 .landing_bump_detection
= SETTING_NAV_LANDING_BUMP_DETECTION_DEFAULT
, // Detect landing based on touchdown G bump
126 // General navigation parameters
127 .pos_failure_timeout
= SETTING_NAV_POSITION_TIMEOUT_DEFAULT
, // 5 sec
128 .waypoint_radius
= SETTING_NAV_WP_RADIUS_DEFAULT
, // 2m diameter
129 .waypoint_safe_distance
= SETTING_NAV_WP_MAX_SAFE_DISTANCE_DEFAULT
, // Metres - first waypoint should be closer than this
130 #ifdef USE_MULTI_MISSION
131 .waypoint_multi_mission_index
= SETTING_NAV_WP_MULTI_MISSION_INDEX_DEFAULT
, // mission index selected from multi mission WP entry
133 .waypoint_load_on_boot
= SETTING_NAV_WP_LOAD_ON_BOOT_DEFAULT
, // load waypoints automatically during boot
134 .auto_speed
= SETTING_NAV_AUTO_SPEED_DEFAULT
, // speed in autonomous modes (3 m/s = 10.8 km/h)
135 .max_auto_speed
= SETTING_NAV_MAX_AUTO_SPEED_DEFAULT
, // max allowed speed autonomous modes
136 .max_auto_climb_rate
= SETTING_NAV_AUTO_CLIMB_RATE_DEFAULT
, // 5 m/s
137 .max_manual_speed
= SETTING_NAV_MANUAL_SPEED_DEFAULT
,
138 .max_manual_climb_rate
= SETTING_NAV_MANUAL_CLIMB_RATE_DEFAULT
,
139 .land_slowdown_minalt
= SETTING_NAV_LAND_SLOWDOWN_MINALT_DEFAULT
, // altitude in centimeters
140 .land_slowdown_maxalt
= SETTING_NAV_LAND_SLOWDOWN_MAXALT_DEFAULT
, // altitude in meters
141 .land_minalt_vspd
= SETTING_NAV_LAND_MINALT_VSPD_DEFAULT
, // centimeters/s
142 .land_maxalt_vspd
= SETTING_NAV_LAND_MAXALT_VSPD_DEFAULT
, // centimeters/s
143 .emerg_descent_rate
= SETTING_NAV_EMERG_LANDING_SPEED_DEFAULT
, // centimeters/s
144 .min_rth_distance
= SETTING_NAV_MIN_RTH_DISTANCE_DEFAULT
, // centimeters, if closer than this land immediately
145 .rth_altitude
= SETTING_NAV_RTH_ALTITUDE_DEFAULT
, // altitude in centimeters
146 .rth_home_altitude
= SETTING_NAV_RTH_HOME_ALTITUDE_DEFAULT
, // altitude in centimeters
147 .rth_climb_first_stage_altitude
= SETTING_NAV_RTH_CLIMB_FIRST_STAGE_ALTITUDE_DEFAULT
, // altitude in centimetres, 0= off
148 .rth_abort_threshold
= SETTING_NAV_RTH_ABORT_THRESHOLD_DEFAULT
, // centimeters - 500m should be safe for all aircraft
149 .max_terrain_follow_altitude
= SETTING_NAV_MAX_TERRAIN_FOLLOW_ALT_DEFAULT
, // max altitude in centimeters in terrain following mode
150 .safehome_max_distance
= SETTING_SAFEHOME_MAX_DISTANCE_DEFAULT
, // Max distance that a safehome is from the arming point
151 .max_altitude
= SETTING_NAV_MAX_ALTITUDE_DEFAULT
,
152 .rth_trackback_distance
= SETTING_NAV_RTH_TRACKBACK_DISTANCE_DEFAULT
, // Max distance allowed for RTH trackback
153 .waypoint_enforce_altitude
= SETTING_NAV_WP_ENFORCE_ALTITUDE_DEFAULT
, // Forces set wp altitude to be achieved
154 .land_detect_sensitivity
= SETTING_NAV_LAND_DETECT_SENSITIVITY_DEFAULT
, // Changes sensitivity of landing detection
155 .auto_disarm_delay
= SETTING_NAV_AUTO_DISARM_DELAY_DEFAULT
, // 2000 ms - time delay to disarm when auto disarm after landing enabled
156 .rth_linear_descent_start_distance
= SETTING_NAV_RTH_LINEAR_DESCENT_START_DISTANCE_DEFAULT
,
157 .cruise_yaw_rate
= SETTING_NAV_CRUISE_YAW_RATE_DEFAULT
, // 20dps
162 .max_bank_angle
= SETTING_NAV_MC_BANK_ANGLE_DEFAULT
, // degrees
164 #ifdef USE_MR_BRAKING_MODE
165 .braking_speed_threshold
= SETTING_NAV_MC_BRAKING_SPEED_THRESHOLD_DEFAULT
, // Braking can become active above 1m/s
166 .braking_disengage_speed
= SETTING_NAV_MC_BRAKING_DISENGAGE_SPEED_DEFAULT
, // Stop when speed goes below 0.75m/s
167 .braking_timeout
= SETTING_NAV_MC_BRAKING_TIMEOUT_DEFAULT
, // Timeout barking after 2s
168 .braking_boost_factor
= SETTING_NAV_MC_BRAKING_BOOST_FACTOR_DEFAULT
, // A 100% boost by default
169 .braking_boost_timeout
= SETTING_NAV_MC_BRAKING_BOOST_TIMEOUT_DEFAULT
, // Timout boost after 750ms
170 .braking_boost_speed_threshold
= SETTING_NAV_MC_BRAKING_BOOST_SPEED_THRESHOLD_DEFAULT
, // Boost can happen only above 1.5m/s
171 .braking_boost_disengage_speed
= SETTING_NAV_MC_BRAKING_BOOST_DISENGAGE_SPEED_DEFAULT
, // Disable boost at 1m/s
172 .braking_bank_angle
= SETTING_NAV_MC_BRAKING_BANK_ANGLE_DEFAULT
, // Max braking angle
175 .posDecelerationTime
= SETTING_NAV_MC_POS_DECELERATION_TIME_DEFAULT
, // posDecelerationTime * 100
176 .posResponseExpo
= SETTING_NAV_MC_POS_EXPO_DEFAULT
, // posResponseExpo * 100
177 .slowDownForTurning
= SETTING_NAV_MC_WP_SLOWDOWN_DEFAULT
,
178 .althold_throttle_type
= SETTING_NAV_MC_ALTHOLD_THROTTLE_DEFAULT
, // STICK
183 .max_bank_angle
= SETTING_NAV_FW_BANK_ANGLE_DEFAULT
, // degrees
184 .max_climb_angle
= SETTING_NAV_FW_CLIMB_ANGLE_DEFAULT
, // degrees
185 .max_dive_angle
= SETTING_NAV_FW_DIVE_ANGLE_DEFAULT
, // degrees
186 .cruise_speed
= SETTING_NAV_FW_CRUISE_SPEED_DEFAULT
, // cm/s
187 .control_smoothness
= SETTING_NAV_FW_CONTROL_SMOOTHNESS_DEFAULT
,
188 .pitch_to_throttle_smooth
= SETTING_NAV_FW_PITCH2THR_SMOOTHING_DEFAULT
,
189 .pitch_to_throttle_thresh
= SETTING_NAV_FW_PITCH2THR_THRESHOLD_DEFAULT
,
190 .minThrottleDownPitchAngle
= SETTING_FW_MIN_THROTTLE_DOWN_PITCH_DEFAULT
,
191 .loiter_radius
= SETTING_NAV_FW_LOITER_RADIUS_DEFAULT
, // 75m
192 .loiter_direction
= SETTING_FW_LOITER_DIRECTION_DEFAULT
,
195 .land_dive_angle
= SETTING_NAV_FW_LAND_DIVE_ANGLE_DEFAULT
, // 2 degrees dive by default
198 .launch_velocity_thresh
= SETTING_NAV_FW_LAUNCH_VELOCITY_DEFAULT
, // 3 m/s
199 .launch_accel_thresh
= SETTING_NAV_FW_LAUNCH_ACCEL_DEFAULT
, // cm/s/s (1.9*G)
200 .launch_time_thresh
= SETTING_NAV_FW_LAUNCH_DETECT_TIME_DEFAULT
, // 40ms
201 .launch_motor_timer
= SETTING_NAV_FW_LAUNCH_MOTOR_DELAY_DEFAULT
, // ms
202 .launch_idle_motor_timer
= SETTING_NAV_FW_LAUNCH_IDLE_MOTOR_DELAY_DEFAULT
, // ms
203 .launch_motor_spinup_time
= SETTING_NAV_FW_LAUNCH_SPINUP_TIME_DEFAULT
, // ms, time to gredually increase throttle from idle to launch
204 .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
205 .launch_min_time
= SETTING_NAV_FW_LAUNCH_MIN_TIME_DEFAULT
, // ms, min time in launch mode
206 .launch_timeout
= SETTING_NAV_FW_LAUNCH_TIMEOUT_DEFAULT
, // ms, timeout for launch procedure
207 .launch_max_altitude
= SETTING_NAV_FW_LAUNCH_MAX_ALTITUDE_DEFAULT
, // cm, altitude where to consider launch ended
208 .launch_climb_angle
= SETTING_NAV_FW_LAUNCH_CLIMB_ANGLE_DEFAULT
, // 18 degrees
209 .launch_max_angle
= SETTING_NAV_FW_LAUNCH_MAX_ANGLE_DEFAULT
, // 45 deg
210 .launch_manual_throttle
= SETTING_NAV_FW_LAUNCH_MANUAL_THROTTLE_DEFAULT
,// OFF
211 .launch_abort_deadband
= SETTING_NAV_FW_LAUNCH_ABORT_DEADBAND_DEFAULT
, // 100 us
213 .allow_manual_thr_increase
= SETTING_NAV_FW_ALLOW_MANUAL_THR_INCREASE_DEFAULT
,
214 .useFwNavYawControl
= SETTING_NAV_USE_FW_YAW_CONTROL_DEFAULT
,
215 .yawControlDeadband
= SETTING_NAV_FW_YAW_DEADBAND_DEFAULT
,
216 .soaring_pitch_deadband
= SETTING_NAV_FW_SOARING_PITCH_DEADBAND_DEFAULT
,// pitch angle mode deadband when Saoring mode enabled
217 .wp_tracking_accuracy
= SETTING_NAV_FW_WP_TRACKING_ACCURACY_DEFAULT
, // 0, improves course tracking accuracy during FW WP missions
218 .wp_tracking_max_angle
= SETTING_NAV_FW_WP_TRACKING_MAX_ANGLE_DEFAULT
, // 60 degs
219 .wp_turn_smoothing
= SETTING_NAV_FW_WP_TURN_SMOOTHING_DEFAULT
, // 0, smooths turns during FW WP mode missions
224 static navWapointHeading_t wpHeadingControl
;
225 navigationPosControl_t posControl
;
226 navSystemStatus_t NAV_Status
;
227 static bool landingDetectorIsActive
;
229 EXTENDED_FASTRAM multicopterPosXyCoefficients_t multicopterPosXyCoefficients
;
232 int16_t navCurrentState
;
233 int16_t navActualVelocity
[3];
234 int16_t navDesiredVelocity
[3];
235 int32_t navTargetPosition
[3];
236 int32_t navLatestActualPosition
[3];
237 int16_t navActualHeading
;
238 uint16_t navDesiredHeading
;
239 int16_t navActualSurface
;
243 int16_t navAccNEU
[3];
244 //End of blackbox states
246 static fpVector3_t
* rthGetHomeTargetPosition(rthTargetMode_e mode
);
247 static void updateDesiredRTHAltitude(void);
248 static void resetAltitudeController(bool useTerrainFollowing
);
249 static void resetPositionController(void);
250 static void setupAltitudeController(void);
251 static void resetHeadingController(void);
252 void resetGCSFlags(void);
254 static void setupJumpCounters(void);
255 static void resetJumpCounter(void);
256 static void clearJumpCounters(void);
258 static void calculateAndSetActiveWaypoint(const navWaypoint_t
* waypoint
);
259 static void calculateAndSetActiveWaypointToLocalPosition(const fpVector3_t
* pos
);
260 void calculateInitialHoldPosition(fpVector3_t
* pos
);
261 void calculateFarAwayTarget(fpVector3_t
* farAwayPos
, int32_t bearing
, int32_t distance
);
262 static bool isWaypointReached(const fpVector3_t
* waypointPos
, const int32_t * waypointBearing
);
263 bool isWaypointAltitudeReached(void);
264 static void mapWaypointToLocalPosition(fpVector3_t
* localPos
, const navWaypoint_t
* waypoint
, geoAltitudeConversionMode_e altConv
);
265 static navigationFSMEvent_t
nextForNonGeoStates(void);
266 static bool isWaypointMissionValid(void);
267 void missionPlannerSetWaypoint(void);
269 void initializeRTHSanityChecker(void);
270 bool validateRTHSanityChecker(void);
271 void updateHomePosition(void);
272 bool abortLaunchAllowed(void);
274 static bool rthAltControlStickOverrideCheck(unsigned axis
);
276 static void updateRthTrackback(bool forceSaveTrackPoint
);
277 static fpVector3_t
* rthGetTrackbackPos(void);
279 /*************************************************************************************************/
280 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_IDLE(navigationFSMState_t previousState
);
281 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_ALTHOLD_INITIALIZE(navigationFSMState_t previousState
);
282 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_ALTHOLD_IN_PROGRESS(navigationFSMState_t previousState
);
283 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_POSHOLD_3D_INITIALIZE(navigationFSMState_t previousState
);
284 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_POSHOLD_3D_IN_PROGRESS(navigationFSMState_t previousState
);
285 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_COURSE_HOLD_INITIALIZE(navigationFSMState_t previousState
);
286 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_COURSE_HOLD_IN_PROGRESS(navigationFSMState_t previousState
);
287 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_COURSE_HOLD_ADJUSTING(navigationFSMState_t previousState
);
288 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_CRUISE_INITIALIZE(navigationFSMState_t previousState
);
289 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_CRUISE_IN_PROGRESS(navigationFSMState_t previousState
);
290 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_CRUISE_ADJUSTING(navigationFSMState_t previousState
);
291 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_INITIALIZE(navigationFSMState_t previousState
);
292 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_CLIMB_TO_SAFE_ALT(navigationFSMState_t previousState
);
293 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_TRACKBACK(navigationFSMState_t previousState
);
294 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_HEAD_HOME(navigationFSMState_t previousState
);
295 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_HOVER_PRIOR_TO_LANDING(navigationFSMState_t previousState
);
296 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_HOVER_ABOVE_HOME(navigationFSMState_t previousState
);
297 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_LANDING(navigationFSMState_t previousState
);
298 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_FINISHING(navigationFSMState_t previousState
);
299 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_FINISHED(navigationFSMState_t previousState
);
300 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_WAYPOINT_INITIALIZE(navigationFSMState_t previousState
);
301 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_WAYPOINT_PRE_ACTION(navigationFSMState_t previousState
);
302 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_WAYPOINT_IN_PROGRESS(navigationFSMState_t previousState
);
303 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_WAYPOINT_REACHED(navigationFSMState_t previousState
);
304 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_WAYPOINT_HOLD_TIME(navigationFSMState_t previousState
);
305 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_WAYPOINT_NEXT(navigationFSMState_t previousState
);
306 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_WAYPOINT_FINISHED(navigationFSMState_t previousState
);
307 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_WAYPOINT_RTH_LAND(navigationFSMState_t previousState
);
308 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_EMERGENCY_LANDING_INITIALIZE(navigationFSMState_t previousState
);
309 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_EMERGENCY_LANDING_IN_PROGRESS(navigationFSMState_t previousState
);
310 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_EMERGENCY_LANDING_FINISHED(navigationFSMState_t previousState
);
311 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_LAUNCH_INITIALIZE(navigationFSMState_t previousState
);
312 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_LAUNCH_WAIT(navigationFSMState_t previousState
);
313 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_LAUNCH_IN_PROGRESS(navigationFSMState_t previousState
);
314 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_MIXERAT_INITIALIZE(navigationFSMState_t previousState
);
315 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_MIXERAT_IN_PROGRESS(navigationFSMState_t previousState
);
316 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_MIXERAT_ABORT(navigationFSMState_t previousState
);
318 static const navigationFSMStateDescriptor_t navFSM
[NAV_STATE_COUNT
] = {
319 /** Idle state ******************************************************/
321 .persistentId
= NAV_PERSISTENT_ID_IDLE
,
322 .onEntry
= navOnEnteringState_NAV_STATE_IDLE
,
325 .mapToFlightModes
= 0,
326 .mwState
= MW_NAV_STATE_NONE
,
327 .mwError
= MW_NAV_ERROR_NONE
,
329 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
330 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
331 [NAV_FSM_EVENT_SWITCH_TO_RTH
] = NAV_STATE_RTH_INITIALIZE
,
332 [NAV_FSM_EVENT_SWITCH_TO_WAYPOINT
] = NAV_STATE_WAYPOINT_INITIALIZE
,
333 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
334 [NAV_FSM_EVENT_SWITCH_TO_LAUNCH
] = NAV_STATE_LAUNCH_INITIALIZE
,
335 [NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD
] = NAV_STATE_COURSE_HOLD_INITIALIZE
,
336 [NAV_FSM_EVENT_SWITCH_TO_CRUISE
] = NAV_STATE_CRUISE_INITIALIZE
,
337 [NAV_FSM_EVENT_SWITCH_TO_MIXERAT
] = NAV_STATE_MIXERAT_INITIALIZE
,
341 /** ALTHOLD mode ***************************************************/
342 [NAV_STATE_ALTHOLD_INITIALIZE
] = {
343 .persistentId
= NAV_PERSISTENT_ID_ALTHOLD_INITIALIZE
,
344 .onEntry
= navOnEnteringState_NAV_STATE_ALTHOLD_INITIALIZE
,
346 .stateFlags
= NAV_CTL_ALT
| NAV_REQUIRE_ANGLE_FW
| NAV_REQUIRE_THRTILT
,
347 .mapToFlightModes
= NAV_ALTHOLD_MODE
,
348 .mwState
= MW_NAV_STATE_NONE
,
349 .mwError
= MW_NAV_ERROR_NONE
,
351 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_ALTHOLD_IN_PROGRESS
,
352 [NAV_FSM_EVENT_ERROR
] = NAV_STATE_IDLE
,
353 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
357 [NAV_STATE_ALTHOLD_IN_PROGRESS
] = {
358 .persistentId
= NAV_PERSISTENT_ID_ALTHOLD_IN_PROGRESS
,
359 .onEntry
= navOnEnteringState_NAV_STATE_ALTHOLD_IN_PROGRESS
,
361 .stateFlags
= NAV_CTL_ALT
| NAV_REQUIRE_ANGLE_FW
| NAV_REQUIRE_THRTILT
| NAV_RC_ALT
,
362 .mapToFlightModes
= NAV_ALTHOLD_MODE
,
363 .mwState
= MW_NAV_STATE_NONE
,
364 .mwError
= MW_NAV_ERROR_NONE
,
366 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_ALTHOLD_IN_PROGRESS
, // re-process the state
367 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
368 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
369 [NAV_FSM_EVENT_SWITCH_TO_RTH
] = NAV_STATE_RTH_INITIALIZE
,
370 [NAV_FSM_EVENT_SWITCH_TO_WAYPOINT
] = NAV_STATE_WAYPOINT_INITIALIZE
,
371 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
372 [NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD
] = NAV_STATE_COURSE_HOLD_INITIALIZE
,
373 [NAV_FSM_EVENT_SWITCH_TO_CRUISE
] = NAV_STATE_CRUISE_INITIALIZE
,
377 /** POSHOLD_3D mode ************************************************/
378 [NAV_STATE_POSHOLD_3D_INITIALIZE
] = {
379 .persistentId
= NAV_PERSISTENT_ID_POSHOLD_3D_INITIALIZE
,
380 .onEntry
= navOnEnteringState_NAV_STATE_POSHOLD_3D_INITIALIZE
,
382 .stateFlags
= NAV_CTL_ALT
| NAV_CTL_POS
| NAV_REQUIRE_ANGLE
| NAV_REQUIRE_THRTILT
,
383 .mapToFlightModes
= NAV_ALTHOLD_MODE
| NAV_POSHOLD_MODE
,
384 .mwState
= MW_NAV_STATE_HOLD_INFINIT
,
385 .mwError
= MW_NAV_ERROR_NONE
,
387 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_POSHOLD_3D_IN_PROGRESS
,
388 [NAV_FSM_EVENT_ERROR
] = NAV_STATE_IDLE
,
389 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
393 [NAV_STATE_POSHOLD_3D_IN_PROGRESS
] = {
394 .persistentId
= NAV_PERSISTENT_ID_POSHOLD_3D_IN_PROGRESS
,
395 .onEntry
= navOnEnteringState_NAV_STATE_POSHOLD_3D_IN_PROGRESS
,
397 .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
,
398 .mapToFlightModes
= NAV_ALTHOLD_MODE
| NAV_POSHOLD_MODE
,
399 .mwState
= MW_NAV_STATE_HOLD_INFINIT
,
400 .mwError
= MW_NAV_ERROR_NONE
,
402 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_POSHOLD_3D_IN_PROGRESS
, // re-process the state
403 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
404 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
405 [NAV_FSM_EVENT_SWITCH_TO_RTH
] = NAV_STATE_RTH_INITIALIZE
,
406 [NAV_FSM_EVENT_SWITCH_TO_WAYPOINT
] = NAV_STATE_WAYPOINT_INITIALIZE
,
407 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
408 [NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD
] = NAV_STATE_COURSE_HOLD_INITIALIZE
,
409 [NAV_FSM_EVENT_SWITCH_TO_CRUISE
] = NAV_STATE_CRUISE_INITIALIZE
,
412 /** CRUISE_HOLD mode ************************************************/
413 [NAV_STATE_COURSE_HOLD_INITIALIZE
] = {
414 .persistentId
= NAV_PERSISTENT_ID_COURSE_HOLD_INITIALIZE
,
415 .onEntry
= navOnEnteringState_NAV_STATE_COURSE_HOLD_INITIALIZE
,
417 .stateFlags
= NAV_REQUIRE_ANGLE
,
418 .mapToFlightModes
= NAV_COURSE_HOLD_MODE
,
419 .mwState
= MW_NAV_STATE_NONE
,
420 .mwError
= MW_NAV_ERROR_NONE
,
422 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_COURSE_HOLD_IN_PROGRESS
,
423 [NAV_FSM_EVENT_ERROR
] = NAV_STATE_IDLE
,
424 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
428 [NAV_STATE_COURSE_HOLD_IN_PROGRESS
] = {
429 .persistentId
= NAV_PERSISTENT_ID_COURSE_HOLD_IN_PROGRESS
,
430 .onEntry
= navOnEnteringState_NAV_STATE_COURSE_HOLD_IN_PROGRESS
,
432 .stateFlags
= NAV_CTL_POS
| NAV_CTL_YAW
| NAV_REQUIRE_ANGLE
| NAV_REQUIRE_MAGHOLD
| NAV_RC_POS
| NAV_RC_YAW
,
433 .mapToFlightModes
= NAV_COURSE_HOLD_MODE
,
434 .mwState
= MW_NAV_STATE_NONE
,
435 .mwError
= MW_NAV_ERROR_NONE
,
437 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_COURSE_HOLD_IN_PROGRESS
, // re-process the state
438 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
439 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
440 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
441 [NAV_FSM_EVENT_SWITCH_TO_CRUISE
] = NAV_STATE_CRUISE_INITIALIZE
,
442 [NAV_FSM_EVENT_SWITCH_TO_COURSE_ADJ
] = NAV_STATE_COURSE_HOLD_ADJUSTING
,
443 [NAV_FSM_EVENT_SWITCH_TO_RTH
] = NAV_STATE_RTH_INITIALIZE
,
444 [NAV_FSM_EVENT_SWITCH_TO_WAYPOINT
] = NAV_STATE_WAYPOINT_INITIALIZE
,
445 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
449 [NAV_STATE_COURSE_HOLD_ADJUSTING
] = {
450 .persistentId
= NAV_PERSISTENT_ID_COURSE_HOLD_ADJUSTING
,
451 .onEntry
= navOnEnteringState_NAV_STATE_COURSE_HOLD_ADJUSTING
,
453 .stateFlags
= NAV_REQUIRE_ANGLE
| NAV_RC_POS
,
454 .mapToFlightModes
= NAV_COURSE_HOLD_MODE
,
455 .mwState
= MW_NAV_STATE_NONE
,
456 .mwError
= MW_NAV_ERROR_NONE
,
458 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_COURSE_HOLD_IN_PROGRESS
,
459 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_COURSE_HOLD_ADJUSTING
,
460 [NAV_FSM_EVENT_ERROR
] = NAV_STATE_IDLE
,
461 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
462 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
463 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
464 [NAV_FSM_EVENT_SWITCH_TO_CRUISE
] = NAV_STATE_CRUISE_INITIALIZE
,
465 [NAV_FSM_EVENT_SWITCH_TO_RTH
] = NAV_STATE_RTH_INITIALIZE
,
466 [NAV_FSM_EVENT_SWITCH_TO_WAYPOINT
] = NAV_STATE_WAYPOINT_INITIALIZE
,
467 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
471 /** CRUISE_3D mode ************************************************/
472 [NAV_STATE_CRUISE_INITIALIZE
] = {
473 .persistentId
= NAV_PERSISTENT_ID_CRUISE_INITIALIZE
,
474 .onEntry
= navOnEnteringState_NAV_STATE_CRUISE_INITIALIZE
,
476 .stateFlags
= NAV_REQUIRE_ANGLE
,
477 .mapToFlightModes
= NAV_ALTHOLD_MODE
| NAV_COURSE_HOLD_MODE
,
478 .mwState
= MW_NAV_STATE_NONE
,
479 .mwError
= MW_NAV_ERROR_NONE
,
481 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_CRUISE_IN_PROGRESS
,
482 [NAV_FSM_EVENT_ERROR
] = NAV_STATE_IDLE
,
483 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
487 [NAV_STATE_CRUISE_IN_PROGRESS
] = {
488 .persistentId
= NAV_PERSISTENT_ID_CRUISE_IN_PROGRESS
,
489 .onEntry
= navOnEnteringState_NAV_STATE_CRUISE_IN_PROGRESS
,
491 .stateFlags
= NAV_CTL_ALT
| NAV_CTL_POS
| NAV_CTL_YAW
| NAV_REQUIRE_ANGLE
| NAV_REQUIRE_MAGHOLD
| NAV_REQUIRE_THRTILT
| NAV_RC_POS
| NAV_RC_YAW
| NAV_RC_ALT
,
492 .mapToFlightModes
= NAV_ALTHOLD_MODE
| NAV_COURSE_HOLD_MODE
,
493 .mwState
= MW_NAV_STATE_NONE
,
494 .mwError
= MW_NAV_ERROR_NONE
,
496 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_CRUISE_IN_PROGRESS
, // re-process the state
497 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
498 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
499 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
500 [NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD
] = NAV_STATE_COURSE_HOLD_INITIALIZE
,
501 [NAV_FSM_EVENT_SWITCH_TO_COURSE_ADJ
] = NAV_STATE_CRUISE_ADJUSTING
,
502 [NAV_FSM_EVENT_SWITCH_TO_RTH
] = NAV_STATE_RTH_INITIALIZE
,
503 [NAV_FSM_EVENT_SWITCH_TO_WAYPOINT
] = NAV_STATE_WAYPOINT_INITIALIZE
,
504 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
508 [NAV_STATE_CRUISE_ADJUSTING
] = {
509 .persistentId
= NAV_PERSISTENT_ID_CRUISE_ADJUSTING
,
510 .onEntry
= navOnEnteringState_NAV_STATE_CRUISE_ADJUSTING
,
512 .stateFlags
= NAV_CTL_ALT
| NAV_REQUIRE_ANGLE
| NAV_RC_POS
| NAV_RC_ALT
,
513 .mapToFlightModes
= NAV_ALTHOLD_MODE
| NAV_COURSE_HOLD_MODE
,
514 .mwState
= MW_NAV_STATE_NONE
,
515 .mwError
= MW_NAV_ERROR_NONE
,
517 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_CRUISE_IN_PROGRESS
,
518 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_CRUISE_ADJUSTING
,
519 [NAV_FSM_EVENT_ERROR
] = NAV_STATE_IDLE
,
520 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
521 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
522 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
523 [NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD
] = NAV_STATE_COURSE_HOLD_INITIALIZE
,
524 [NAV_FSM_EVENT_SWITCH_TO_RTH
] = NAV_STATE_RTH_INITIALIZE
,
525 [NAV_FSM_EVENT_SWITCH_TO_WAYPOINT
] = NAV_STATE_WAYPOINT_INITIALIZE
,
526 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
530 /** RTH_3D mode ************************************************/
531 [NAV_STATE_RTH_INITIALIZE
] = {
532 .persistentId
= NAV_PERSISTENT_ID_RTH_INITIALIZE
,
533 .onEntry
= navOnEnteringState_NAV_STATE_RTH_INITIALIZE
,
535 .stateFlags
= NAV_CTL_ALT
| NAV_CTL_POS
| NAV_CTL_YAW
| NAV_REQUIRE_ANGLE
| NAV_REQUIRE_MAGHOLD
| NAV_REQUIRE_THRTILT
| NAV_AUTO_RTH
,
536 .mapToFlightModes
= NAV_RTH_MODE
| NAV_ALTHOLD_MODE
,
537 .mwState
= MW_NAV_STATE_RTH_START
,
538 .mwError
= MW_NAV_ERROR_NONE
,
540 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_RTH_INITIALIZE
, // re-process the state
541 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_RTH_CLIMB_TO_SAFE_ALT
,
542 [NAV_FSM_EVENT_SWITCH_TO_NAV_STATE_RTH_TRACKBACK
] = NAV_STATE_RTH_TRACKBACK
,
543 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
544 [NAV_FSM_EVENT_SWITCH_TO_RTH_LANDING
] = NAV_STATE_RTH_HOVER_PRIOR_TO_LANDING
,
545 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
549 [NAV_STATE_RTH_CLIMB_TO_SAFE_ALT
] = {
550 .persistentId
= NAV_PERSISTENT_ID_RTH_CLIMB_TO_SAFE_ALT
,
551 .onEntry
= navOnEnteringState_NAV_STATE_RTH_CLIMB_TO_SAFE_ALT
,
553 .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
554 .mapToFlightModes
= NAV_RTH_MODE
| NAV_ALTHOLD_MODE
,
555 .mwState
= MW_NAV_STATE_RTH_CLIMB
,
556 .mwError
= MW_NAV_ERROR_WAIT_FOR_RTH_ALT
,
558 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_RTH_CLIMB_TO_SAFE_ALT
, // re-process the state
559 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_RTH_HEAD_HOME
,
560 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
561 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
562 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
563 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
564 [NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD
] = NAV_STATE_COURSE_HOLD_INITIALIZE
,
565 [NAV_FSM_EVENT_SWITCH_TO_CRUISE
] = NAV_STATE_CRUISE_INITIALIZE
,
569 [NAV_STATE_RTH_TRACKBACK
] = {
570 .persistentId
= NAV_PERSISTENT_ID_RTH_TRACKBACK
,
571 .onEntry
= navOnEnteringState_NAV_STATE_RTH_TRACKBACK
,
573 .stateFlags
= NAV_CTL_ALT
| NAV_CTL_POS
| NAV_CTL_YAW
| NAV_REQUIRE_ANGLE
| NAV_REQUIRE_MAGHOLD
| NAV_REQUIRE_THRTILT
| NAV_AUTO_RTH
,
574 .mapToFlightModes
= NAV_RTH_MODE
| NAV_ALTHOLD_MODE
,
575 .mwState
= MW_NAV_STATE_RTH_ENROUTE
,
576 .mwError
= MW_NAV_ERROR_NONE
,
578 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_RTH_TRACKBACK
, // re-process the state
579 [NAV_FSM_EVENT_SWITCH_TO_NAV_STATE_RTH_INITIALIZE
] = NAV_STATE_RTH_INITIALIZE
,
580 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
581 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
582 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
583 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
584 [NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD
] = NAV_STATE_COURSE_HOLD_INITIALIZE
,
585 [NAV_FSM_EVENT_SWITCH_TO_CRUISE
] = NAV_STATE_CRUISE_INITIALIZE
,
589 [NAV_STATE_RTH_HEAD_HOME
] = {
590 .persistentId
= NAV_PERSISTENT_ID_RTH_HEAD_HOME
,
591 .onEntry
= navOnEnteringState_NAV_STATE_RTH_HEAD_HOME
,
593 .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
,
594 .mapToFlightModes
= NAV_RTH_MODE
| NAV_ALTHOLD_MODE
,
595 .mwState
= MW_NAV_STATE_RTH_ENROUTE
,
596 .mwError
= MW_NAV_ERROR_NONE
,
598 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_RTH_HEAD_HOME
, // re-process the state
599 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_RTH_HOVER_PRIOR_TO_LANDING
,
600 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
601 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
602 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
603 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
604 [NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD
] = NAV_STATE_COURSE_HOLD_INITIALIZE
,
605 [NAV_FSM_EVENT_SWITCH_TO_CRUISE
] = NAV_STATE_CRUISE_INITIALIZE
,
606 [NAV_FSM_EVENT_SWITCH_TO_MIXERAT
] = NAV_STATE_MIXERAT_INITIALIZE
,
610 [NAV_STATE_RTH_HOVER_PRIOR_TO_LANDING
] = {
611 .persistentId
= NAV_PERSISTENT_ID_RTH_HOVER_PRIOR_TO_LANDING
,
612 .onEntry
= navOnEnteringState_NAV_STATE_RTH_HOVER_PRIOR_TO_LANDING
,
614 .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
,
615 .mapToFlightModes
= NAV_RTH_MODE
| NAV_ALTHOLD_MODE
,
616 .mwState
= MW_NAV_STATE_LAND_SETTLE
,
617 .mwError
= MW_NAV_ERROR_NONE
,
619 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_RTH_HOVER_PRIOR_TO_LANDING
,
620 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_RTH_LANDING
,
621 [NAV_FSM_EVENT_SWITCH_TO_RTH_HOVER_ABOVE_HOME
] = NAV_STATE_RTH_HOVER_ABOVE_HOME
,
622 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
623 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
624 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
625 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
626 [NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD
] = NAV_STATE_COURSE_HOLD_INITIALIZE
,
627 [NAV_FSM_EVENT_SWITCH_TO_CRUISE
] = NAV_STATE_CRUISE_INITIALIZE
,
631 [NAV_STATE_RTH_HOVER_ABOVE_HOME
] = {
632 .persistentId
= NAV_PERSISTENT_ID_RTH_HOVER_ABOVE_HOME
,
633 .onEntry
= navOnEnteringState_NAV_STATE_RTH_HOVER_ABOVE_HOME
,
635 .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
,
636 .mapToFlightModes
= NAV_RTH_MODE
| NAV_ALTHOLD_MODE
,
637 .mwState
= MW_NAV_STATE_HOVER_ABOVE_HOME
,
638 .mwError
= MW_NAV_ERROR_NONE
,
640 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_RTH_HOVER_ABOVE_HOME
,
641 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
642 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
643 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
644 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
645 [NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD
] = NAV_STATE_COURSE_HOLD_INITIALIZE
,
646 [NAV_FSM_EVENT_SWITCH_TO_CRUISE
] = NAV_STATE_CRUISE_INITIALIZE
,
650 [NAV_STATE_RTH_LANDING
] = {
651 .persistentId
= NAV_PERSISTENT_ID_RTH_LANDING
,
652 .onEntry
= navOnEnteringState_NAV_STATE_RTH_LANDING
,
654 .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
,
655 .mapToFlightModes
= NAV_RTH_MODE
| NAV_ALTHOLD_MODE
,
656 .mwState
= MW_NAV_STATE_LAND_IN_PROGRESS
,
657 .mwError
= MW_NAV_ERROR_LANDING
,
659 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_RTH_LANDING
, // re-process state
660 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_RTH_FINISHING
,
661 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
662 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
663 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
664 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
665 [NAV_FSM_EVENT_SWITCH_TO_MIXERAT
] = NAV_STATE_MIXERAT_INITIALIZE
,
669 [NAV_STATE_RTH_FINISHING
] = {
670 .persistentId
= NAV_PERSISTENT_ID_RTH_FINISHING
,
671 .onEntry
= navOnEnteringState_NAV_STATE_RTH_FINISHING
,
673 .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
,
674 .mapToFlightModes
= NAV_RTH_MODE
| NAV_ALTHOLD_MODE
,
675 .mwState
= MW_NAV_STATE_LAND_IN_PROGRESS
,
676 .mwError
= MW_NAV_ERROR_LANDING
,
678 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_RTH_FINISHED
,
679 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
683 [NAV_STATE_RTH_FINISHED
] = {
684 .persistentId
= NAV_PERSISTENT_ID_RTH_FINISHED
,
685 .onEntry
= navOnEnteringState_NAV_STATE_RTH_FINISHED
,
687 .stateFlags
= NAV_CTL_ALT
| NAV_CTL_LAND
| NAV_REQUIRE_ANGLE
| NAV_REQUIRE_THRTILT
| NAV_AUTO_RTH
,
688 .mapToFlightModes
= NAV_RTH_MODE
| NAV_ALTHOLD_MODE
,
689 .mwState
= MW_NAV_STATE_LANDED
,
690 .mwError
= MW_NAV_ERROR_NONE
,
692 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_RTH_FINISHED
, // re-process state
693 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
694 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
695 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
696 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
700 /** WAYPOINT mode ************************************************/
701 [NAV_STATE_WAYPOINT_INITIALIZE
] = {
702 .persistentId
= NAV_PERSISTENT_ID_WAYPOINT_INITIALIZE
,
703 .onEntry
= navOnEnteringState_NAV_STATE_WAYPOINT_INITIALIZE
,
705 .stateFlags
= NAV_CTL_ALT
| NAV_CTL_POS
| NAV_CTL_YAW
| NAV_REQUIRE_ANGLE
| NAV_REQUIRE_MAGHOLD
| NAV_REQUIRE_THRTILT
| NAV_AUTO_WP
,
706 .mapToFlightModes
= NAV_WP_MODE
| NAV_ALTHOLD_MODE
,
707 .mwState
= MW_NAV_STATE_PROCESS_NEXT
,
708 .mwError
= MW_NAV_ERROR_NONE
,
710 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_WAYPOINT_PRE_ACTION
,
711 [NAV_FSM_EVENT_ERROR
] = NAV_STATE_IDLE
,
712 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
713 [NAV_FSM_EVENT_SWITCH_TO_WAYPOINT_FINISHED
] = NAV_STATE_WAYPOINT_FINISHED
,
717 [NAV_STATE_WAYPOINT_PRE_ACTION
] = {
718 .persistentId
= NAV_PERSISTENT_ID_WAYPOINT_PRE_ACTION
,
719 .onEntry
= navOnEnteringState_NAV_STATE_WAYPOINT_PRE_ACTION
,
721 .stateFlags
= NAV_CTL_ALT
| NAV_CTL_POS
| NAV_CTL_YAW
| NAV_REQUIRE_ANGLE
| NAV_REQUIRE_MAGHOLD
| NAV_REQUIRE_THRTILT
| NAV_AUTO_WP
,
722 .mapToFlightModes
= NAV_WP_MODE
| NAV_ALTHOLD_MODE
,
723 .mwState
= MW_NAV_STATE_PROCESS_NEXT
,
724 .mwError
= MW_NAV_ERROR_NONE
,
726 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_WAYPOINT_PRE_ACTION
, // re-process the state (for JUMP)
727 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_WAYPOINT_IN_PROGRESS
,
728 [NAV_FSM_EVENT_ERROR
] = NAV_STATE_IDLE
,
729 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
730 [NAV_FSM_EVENT_SWITCH_TO_RTH
] = NAV_STATE_RTH_INITIALIZE
,
731 [NAV_FSM_EVENT_SWITCH_TO_WAYPOINT_FINISHED
] = NAV_STATE_WAYPOINT_FINISHED
,
735 [NAV_STATE_WAYPOINT_IN_PROGRESS
] = {
736 .persistentId
= NAV_PERSISTENT_ID_WAYPOINT_IN_PROGRESS
,
737 .onEntry
= navOnEnteringState_NAV_STATE_WAYPOINT_IN_PROGRESS
,
739 .stateFlags
= NAV_CTL_ALT
| NAV_CTL_POS
| NAV_CTL_YAW
| NAV_REQUIRE_ANGLE
| NAV_REQUIRE_MAGHOLD
| NAV_REQUIRE_THRTILT
| NAV_AUTO_WP
,
740 .mapToFlightModes
= NAV_WP_MODE
| NAV_ALTHOLD_MODE
,
741 .mwState
= MW_NAV_STATE_WP_ENROUTE
,
742 .mwError
= MW_NAV_ERROR_NONE
,
744 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_WAYPOINT_IN_PROGRESS
, // re-process the state
745 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_WAYPOINT_REACHED
, // successfully reached waypoint
746 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
747 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
748 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
749 [NAV_FSM_EVENT_SWITCH_TO_RTH
] = NAV_STATE_RTH_INITIALIZE
,
750 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
751 [NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD
] = NAV_STATE_COURSE_HOLD_INITIALIZE
,
752 [NAV_FSM_EVENT_SWITCH_TO_CRUISE
] = NAV_STATE_CRUISE_INITIALIZE
,
756 [NAV_STATE_WAYPOINT_REACHED
] = {
757 .persistentId
= NAV_PERSISTENT_ID_WAYPOINT_REACHED
,
758 .onEntry
= navOnEnteringState_NAV_STATE_WAYPOINT_REACHED
,
760 .stateFlags
= NAV_CTL_ALT
| NAV_CTL_POS
| NAV_CTL_YAW
| NAV_REQUIRE_ANGLE
| NAV_REQUIRE_MAGHOLD
| NAV_REQUIRE_THRTILT
| NAV_AUTO_WP
,
761 .mapToFlightModes
= NAV_WP_MODE
| NAV_ALTHOLD_MODE
,
762 .mwState
= MW_NAV_STATE_PROCESS_NEXT
,
763 .mwError
= MW_NAV_ERROR_NONE
,
765 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_WAYPOINT_REACHED
, // re-process state
766 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_WAYPOINT_NEXT
,
767 [NAV_FSM_EVENT_SWITCH_TO_WAYPOINT_HOLD_TIME
] = NAV_STATE_WAYPOINT_HOLD_TIME
,
768 [NAV_FSM_EVENT_SWITCH_TO_WAYPOINT_FINISHED
] = NAV_STATE_WAYPOINT_FINISHED
,
769 [NAV_FSM_EVENT_SWITCH_TO_WAYPOINT_RTH_LAND
] = NAV_STATE_WAYPOINT_RTH_LAND
,
770 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
771 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
772 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
773 [NAV_FSM_EVENT_SWITCH_TO_RTH
] = NAV_STATE_RTH_INITIALIZE
,
774 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
775 [NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD
] = NAV_STATE_COURSE_HOLD_INITIALIZE
,
776 [NAV_FSM_EVENT_SWITCH_TO_CRUISE
] = NAV_STATE_CRUISE_INITIALIZE
,
780 [NAV_STATE_WAYPOINT_HOLD_TIME
] = {
781 .persistentId
= NAV_PERSISTENT_ID_WAYPOINT_HOLD_TIME
, // There is no state for timed hold?
782 .onEntry
= navOnEnteringState_NAV_STATE_WAYPOINT_HOLD_TIME
,
784 .stateFlags
= NAV_CTL_ALT
| NAV_CTL_POS
| NAV_CTL_YAW
| NAV_REQUIRE_ANGLE
| NAV_REQUIRE_MAGHOLD
| NAV_REQUIRE_THRTILT
| NAV_AUTO_WP
,
785 .mapToFlightModes
= NAV_WP_MODE
| NAV_ALTHOLD_MODE
,
786 .mwState
= MW_NAV_STATE_HOLD_TIMED
,
787 .mwError
= MW_NAV_ERROR_NONE
,
789 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_WAYPOINT_HOLD_TIME
, // re-process the state
790 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_WAYPOINT_NEXT
, // successfully reached waypoint
791 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
792 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
793 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
794 [NAV_FSM_EVENT_SWITCH_TO_RTH
] = NAV_STATE_RTH_INITIALIZE
,
795 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
796 [NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD
] = NAV_STATE_COURSE_HOLD_INITIALIZE
,
797 [NAV_FSM_EVENT_SWITCH_TO_CRUISE
] = NAV_STATE_CRUISE_INITIALIZE
,
801 [NAV_STATE_WAYPOINT_RTH_LAND
] = {
802 .persistentId
= NAV_PERSISTENT_ID_WAYPOINT_RTH_LAND
,
803 .onEntry
= navOnEnteringState_NAV_STATE_WAYPOINT_RTH_LAND
,
805 .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
,
806 .mapToFlightModes
= NAV_WP_MODE
| NAV_ALTHOLD_MODE
,
807 .mwState
= MW_NAV_STATE_LAND_IN_PROGRESS
,
808 .mwError
= MW_NAV_ERROR_LANDING
,
810 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_WAYPOINT_RTH_LAND
, // re-process state
811 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_WAYPOINT_FINISHED
,
812 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
813 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
814 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
815 [NAV_FSM_EVENT_SWITCH_TO_RTH
] = NAV_STATE_RTH_INITIALIZE
,
816 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
817 [NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD
] = NAV_STATE_COURSE_HOLD_INITIALIZE
,
818 [NAV_FSM_EVENT_SWITCH_TO_CRUISE
] = NAV_STATE_CRUISE_INITIALIZE
,
819 [NAV_FSM_EVENT_SWITCH_TO_MIXERAT
] = NAV_STATE_MIXERAT_INITIALIZE
,
823 [NAV_STATE_WAYPOINT_NEXT
] = {
824 .persistentId
= NAV_PERSISTENT_ID_WAYPOINT_NEXT
,
825 .onEntry
= navOnEnteringState_NAV_STATE_WAYPOINT_NEXT
,
827 .stateFlags
= NAV_CTL_ALT
| NAV_CTL_POS
| NAV_CTL_YAW
| NAV_REQUIRE_ANGLE
| NAV_REQUIRE_MAGHOLD
| NAV_REQUIRE_THRTILT
| NAV_AUTO_WP
,
828 .mapToFlightModes
= NAV_WP_MODE
| NAV_ALTHOLD_MODE
,
829 .mwState
= MW_NAV_STATE_PROCESS_NEXT
,
830 .mwError
= MW_NAV_ERROR_NONE
,
832 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_WAYPOINT_PRE_ACTION
,
833 [NAV_FSM_EVENT_SWITCH_TO_WAYPOINT_FINISHED
] = NAV_STATE_WAYPOINT_FINISHED
,
837 [NAV_STATE_WAYPOINT_FINISHED
] = {
838 .persistentId
= NAV_PERSISTENT_ID_WAYPOINT_FINISHED
,
839 .onEntry
= navOnEnteringState_NAV_STATE_WAYPOINT_FINISHED
,
841 .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
,
842 .mapToFlightModes
= NAV_WP_MODE
| NAV_ALTHOLD_MODE
,
843 .mwState
= MW_NAV_STATE_WP_ENROUTE
,
844 .mwError
= MW_NAV_ERROR_FINISH
,
846 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_WAYPOINT_FINISHED
, // re-process state
847 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
848 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
849 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
850 [NAV_FSM_EVENT_SWITCH_TO_RTH
] = NAV_STATE_RTH_INITIALIZE
,
851 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
852 [NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD
] = NAV_STATE_COURSE_HOLD_INITIALIZE
,
853 [NAV_FSM_EVENT_SWITCH_TO_CRUISE
] = NAV_STATE_CRUISE_INITIALIZE
,
857 /** EMERGENCY LANDING ************************************************/
858 [NAV_STATE_EMERGENCY_LANDING_INITIALIZE
] = {
859 .persistentId
= NAV_PERSISTENT_ID_EMERGENCY_LANDING_INITIALIZE
,
860 .onEntry
= navOnEnteringState_NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
862 .stateFlags
= NAV_CTL_EMERG
| NAV_REQUIRE_ANGLE
,
863 .mapToFlightModes
= 0,
864 .mwState
= MW_NAV_STATE_EMERGENCY_LANDING
,
865 .mwError
= MW_NAV_ERROR_LANDING
,
867 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_EMERGENCY_LANDING_IN_PROGRESS
,
868 [NAV_FSM_EVENT_ERROR
] = NAV_STATE_IDLE
,
869 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
870 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
871 [NAV_FSM_EVENT_SWITCH_TO_RTH
] = NAV_STATE_RTH_INITIALIZE
,
872 [NAV_FSM_EVENT_SWITCH_TO_WAYPOINT
] = NAV_STATE_WAYPOINT_INITIALIZE
,
876 [NAV_STATE_EMERGENCY_LANDING_IN_PROGRESS
] = {
877 .persistentId
= NAV_PERSISTENT_ID_EMERGENCY_LANDING_IN_PROGRESS
,
878 .onEntry
= navOnEnteringState_NAV_STATE_EMERGENCY_LANDING_IN_PROGRESS
,
880 .stateFlags
= NAV_CTL_EMERG
| NAV_REQUIRE_ANGLE
,
881 .mapToFlightModes
= 0,
882 .mwState
= MW_NAV_STATE_EMERGENCY_LANDING
,
883 .mwError
= MW_NAV_ERROR_LANDING
,
885 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_EMERGENCY_LANDING_IN_PROGRESS
, // re-process the state
886 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_EMERGENCY_LANDING_FINISHED
,
887 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
888 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
889 [NAV_FSM_EVENT_SWITCH_TO_RTH
] = NAV_STATE_RTH_INITIALIZE
,
890 [NAV_FSM_EVENT_SWITCH_TO_WAYPOINT
] = NAV_STATE_WAYPOINT_INITIALIZE
,
894 [NAV_STATE_EMERGENCY_LANDING_FINISHED
] = {
895 .persistentId
= NAV_PERSISTENT_ID_EMERGENCY_LANDING_FINISHED
,
896 .onEntry
= navOnEnteringState_NAV_STATE_EMERGENCY_LANDING_FINISHED
,
898 .stateFlags
= NAV_CTL_EMERG
| NAV_REQUIRE_ANGLE
,
899 .mapToFlightModes
= 0,
900 .mwState
= MW_NAV_STATE_LANDED
,
901 .mwError
= MW_NAV_ERROR_LANDING
,
903 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_EMERGENCY_LANDING_FINISHED
,
904 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
908 [NAV_STATE_LAUNCH_INITIALIZE
] = {
909 .persistentId
= NAV_PERSISTENT_ID_LAUNCH_INITIALIZE
,
910 .onEntry
= navOnEnteringState_NAV_STATE_LAUNCH_INITIALIZE
,
912 .stateFlags
= NAV_REQUIRE_ANGLE
,
913 .mapToFlightModes
= NAV_LAUNCH_MODE
,
914 .mwState
= MW_NAV_STATE_NONE
,
915 .mwError
= MW_NAV_ERROR_NONE
,
917 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_LAUNCH_WAIT
,
918 [NAV_FSM_EVENT_ERROR
] = NAV_STATE_IDLE
,
919 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
923 [NAV_STATE_LAUNCH_WAIT
] = {
924 .persistentId
= NAV_PERSISTENT_ID_LAUNCH_WAIT
,
925 .onEntry
= navOnEnteringState_NAV_STATE_LAUNCH_WAIT
,
927 .stateFlags
= NAV_CTL_LAUNCH
| NAV_REQUIRE_ANGLE
,
928 .mapToFlightModes
= NAV_LAUNCH_MODE
,
929 .mwState
= MW_NAV_STATE_NONE
,
930 .mwError
= MW_NAV_ERROR_NONE
,
932 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_LAUNCH_WAIT
, // re-process the state
933 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_LAUNCH_IN_PROGRESS
,
934 [NAV_FSM_EVENT_ERROR
] = NAV_STATE_IDLE
,
935 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
939 [NAV_STATE_LAUNCH_IN_PROGRESS
] = {
940 .persistentId
= NAV_PERSISTENT_ID_LAUNCH_IN_PROGRESS
,
941 .onEntry
= navOnEnteringState_NAV_STATE_LAUNCH_IN_PROGRESS
,
943 .stateFlags
= NAV_CTL_LAUNCH
| NAV_REQUIRE_ANGLE
,
944 .mapToFlightModes
= NAV_LAUNCH_MODE
,
945 .mwState
= MW_NAV_STATE_NONE
,
946 .mwError
= MW_NAV_ERROR_NONE
,
948 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_LAUNCH_IN_PROGRESS
, // re-process the state
949 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_IDLE
,
950 [NAV_FSM_EVENT_ERROR
] = NAV_STATE_IDLE
,
951 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
955 /** MIXER AUTOMATED TRANSITION mode, alternated althod ***************************************************/
956 [NAV_STATE_MIXERAT_INITIALIZE
] = {
957 .persistentId
= NAV_PERSISTENT_ID_MIXERAT_INITIALIZE
,
958 .onEntry
= navOnEnteringState_NAV_STATE_MIXERAT_INITIALIZE
,
960 .stateFlags
= NAV_CTL_ALT
| NAV_REQUIRE_ANGLE
| NAV_REQUIRE_THRTILT
| NAV_MIXERAT
,
961 .mapToFlightModes
= NAV_ALTHOLD_MODE
,
962 .mwState
= MW_NAV_STATE_NONE
,
963 .mwError
= MW_NAV_ERROR_NONE
,
965 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_MIXERAT_IN_PROGRESS
,
966 [NAV_FSM_EVENT_ERROR
] = NAV_STATE_IDLE
,
967 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
971 [NAV_STATE_MIXERAT_IN_PROGRESS
] = {
972 .persistentId
= NAV_PERSISTENT_ID_MIXERAT_IN_PROGRESS
,
973 .onEntry
= navOnEnteringState_NAV_STATE_MIXERAT_IN_PROGRESS
,
975 .stateFlags
= NAV_CTL_ALT
| NAV_REQUIRE_ANGLE
| NAV_REQUIRE_THRTILT
| NAV_MIXERAT
,
976 .mapToFlightModes
= NAV_ALTHOLD_MODE
,
977 .mwState
= MW_NAV_STATE_NONE
,
978 .mwError
= MW_NAV_ERROR_NONE
,
980 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_MIXERAT_IN_PROGRESS
, // re-process the state
981 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_MIXERAT_ABORT
,
982 [NAV_FSM_EVENT_SWITCH_TO_RTH_HEAD_HOME
] = NAV_STATE_RTH_HEAD_HOME
, //switch to its pending state
983 [NAV_FSM_EVENT_SWITCH_TO_RTH_LANDING
] = NAV_STATE_RTH_LANDING
, //switch to its pending state
986 [NAV_STATE_MIXERAT_ABORT
] = {
987 .persistentId
= NAV_PERSISTENT_ID_MIXERAT_ABORT
,
988 .onEntry
= navOnEnteringState_NAV_STATE_MIXERAT_ABORT
, //will not switch to its pending state
990 .stateFlags
= NAV_CTL_ALT
| NAV_REQUIRE_ANGLE
| NAV_REQUIRE_THRTILT
,
991 .mapToFlightModes
= NAV_ALTHOLD_MODE
,
992 .mwState
= MW_NAV_STATE_NONE
,
993 .mwError
= MW_NAV_ERROR_NONE
,
995 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_IDLE
,
996 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
1002 static navigationFSMStateFlags_t
navGetStateFlags(navigationFSMState_t state
)
1004 return navFSM
[state
].stateFlags
;
1007 flightModeFlags_e
navGetMappedFlightModes(navigationFSMState_t state
)
1009 return navFSM
[state
].mapToFlightModes
;
1012 navigationFSMStateFlags_t
navGetCurrentStateFlags(void)
1014 return navGetStateFlags(posControl
.navState
);
1017 static bool navTerrainFollowingRequested(void)
1019 // Terrain following not supported on FIXED WING aircraft yet
1020 return !STATE(FIXED_WING_LEGACY
) && IS_RC_MODE_ACTIVE(BOXSURFACE
);
1023 /*************************************************************************************************/
1024 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_IDLE(navigationFSMState_t previousState
)
1026 UNUSED(previousState
);
1028 resetAltitudeController(false);
1029 resetHeadingController();
1030 resetPositionController();
1032 return NAV_FSM_EVENT_NONE
;
1035 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_ALTHOLD_INITIALIZE(navigationFSMState_t previousState
)
1037 const navigationFSMStateFlags_t prevFlags
= navGetStateFlags(previousState
);
1038 const bool terrainFollowingToggled
= (posControl
.flags
.isTerrainFollowEnabled
!= navTerrainFollowingRequested());
1042 // Prepare altitude controller if idle, RTH or WP modes active or surface mode status changed
1043 if (!(prevFlags
& NAV_CTL_ALT
) || (prevFlags
& NAV_AUTO_RTH
) || (prevFlags
& NAV_AUTO_WP
) || terrainFollowingToggled
) {
1044 resetAltitudeController(navTerrainFollowingRequested());
1045 setupAltitudeController();
1046 setDesiredPosition(&navGetCurrentActualPositionAndVelocity()->pos
, posControl
.actualState
.yaw
, NAV_POS_UPDATE_Z
); // This will reset surface offset
1049 return NAV_FSM_EVENT_SUCCESS
;
1052 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_ALTHOLD_IN_PROGRESS(navigationFSMState_t previousState
)
1054 UNUSED(previousState
);
1056 // If GCS was disabled - reset altitude setpoint
1057 if (posControl
.flags
.isGCSAssistedNavigationReset
) {
1058 setDesiredPosition(&navGetCurrentActualPositionAndVelocity()->pos
, posControl
.actualState
.yaw
, NAV_POS_UPDATE_Z
);
1062 return NAV_FSM_EVENT_NONE
;
1065 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_POSHOLD_3D_INITIALIZE(navigationFSMState_t previousState
)
1067 const navigationFSMStateFlags_t prevFlags
= navGetStateFlags(previousState
);
1068 const bool terrainFollowingToggled
= (posControl
.flags
.isTerrainFollowEnabled
!= navTerrainFollowingRequested());
1072 // Prepare altitude controller if idle, RTH or WP modes active or surface mode status changed
1073 if (!(prevFlags
& NAV_CTL_ALT
) || (prevFlags
& NAV_AUTO_RTH
) || (prevFlags
& NAV_AUTO_WP
) || terrainFollowingToggled
) {
1074 resetAltitudeController(navTerrainFollowingRequested());
1075 setupAltitudeController();
1076 setDesiredPosition(&navGetCurrentActualPositionAndVelocity()->pos
, posControl
.actualState
.yaw
, NAV_POS_UPDATE_Z
); // This will reset surface offset
1079 // Prepare position controller if idle or current Mode NOT active in position hold state
1080 if (previousState
!= NAV_STATE_RTH_HOVER_PRIOR_TO_LANDING
&& previousState
!= NAV_STATE_RTH_HOVER_ABOVE_HOME
&&
1081 previousState
!= NAV_STATE_RTH_LANDING
&& previousState
!= NAV_STATE_WAYPOINT_RTH_LAND
&&
1082 previousState
!= NAV_STATE_WAYPOINT_FINISHED
&& previousState
!= NAV_STATE_WAYPOINT_HOLD_TIME
)
1084 resetPositionController();
1086 fpVector3_t targetHoldPos
;
1087 calculateInitialHoldPosition(&targetHoldPos
);
1088 setDesiredPosition(&targetHoldPos
, posControl
.actualState
.yaw
, NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_HEADING
);
1091 return NAV_FSM_EVENT_SUCCESS
;
1094 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_POSHOLD_3D_IN_PROGRESS(navigationFSMState_t previousState
)
1096 UNUSED(previousState
);
1098 // If GCS was disabled - reset 2D pos setpoint
1099 if (posControl
.flags
.isGCSAssistedNavigationReset
) {
1100 fpVector3_t targetHoldPos
;
1101 calculateInitialHoldPosition(&targetHoldPos
);
1102 setDesiredPosition(&navGetCurrentActualPositionAndVelocity()->pos
, posControl
.actualState
.yaw
, NAV_POS_UPDATE_Z
);
1103 setDesiredPosition(&targetHoldPos
, posControl
.actualState
.yaw
, NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_HEADING
);
1107 return NAV_FSM_EVENT_NONE
;
1110 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_COURSE_HOLD_INITIALIZE(navigationFSMState_t previousState
)
1112 UNUSED(previousState
);
1114 if (STATE(MULTIROTOR
) && !navConfig()->general
.cruise_yaw_rate
) { // course hold not possible on MR without yaw control
1115 return NAV_FSM_EVENT_ERROR
;
1118 DEBUG_SET(DEBUG_CRUISE
, 0, 1);
1119 // Switch to IDLE if we do not have an healty position. Try the next iteration.
1120 if (checkForPositionSensorTimeout()) {
1121 return NAV_FSM_EVENT_SWITCH_TO_IDLE
;
1124 resetPositionController();
1126 if (STATE(AIRPLANE
)) {
1127 posControl
.cruise
.course
= posControl
.actualState
.cog
; // Store the course to follow
1128 } else { // Multicopter
1129 posControl
.cruise
.course
= posControl
.actualState
.yaw
;
1130 posControl
.cruise
.multicopterSpeed
= constrainf(posControl
.actualState
.velXY
, 10.0f
, navConfig()->general
.max_manual_speed
);
1131 posControl
.desiredState
.pos
= posControl
.actualState
.abs
.pos
;
1133 posControl
.cruise
.previousCourse
= posControl
.cruise
.course
;
1134 posControl
.cruise
.lastCourseAdjustmentTime
= 0;
1136 return NAV_FSM_EVENT_SUCCESS
; // Go to NAV_STATE_COURSE_HOLD_IN_PROGRESS state
1139 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_COURSE_HOLD_IN_PROGRESS(navigationFSMState_t previousState
)
1141 UNUSED(previousState
);
1143 const timeMs_t currentTimeMs
= millis();
1145 // Switch to IDLE if we do not have an healty position. Do the CRUISE init the next iteration.
1146 if (checkForPositionSensorTimeout()) {
1147 return NAV_FSM_EVENT_SWITCH_TO_IDLE
;
1150 DEBUG_SET(DEBUG_CRUISE
, 0, 2);
1151 DEBUG_SET(DEBUG_CRUISE
, 2, 0);
1153 if (STATE(AIRPLANE
) && posControl
.flags
.isAdjustingPosition
) { // inhibit for MR, pitch/roll only adjusts speed using pitch
1154 return NAV_FSM_EVENT_SWITCH_TO_COURSE_ADJ
;
1157 const bool mcRollStickHeadingAdjustmentActive
= STATE(MULTIROTOR
) && ABS(rcCommand
[ROLL
]) > rcControlsConfig()->pos_hold_deadband
;
1159 // User demanding yaw -> yaw stick on FW, yaw or roll sticks on MR
1160 // We record the desired course and change the desired target in the meanwhile
1161 if (posControl
.flags
.isAdjustingHeading
|| mcRollStickHeadingAdjustmentActive
) {
1162 int16_t cruiseYawRate
= DEGREES_TO_CENTIDEGREES(navConfig()->general
.cruise_yaw_rate
);
1163 int16_t headingAdjustCommand
= rcCommand
[YAW
];
1164 if (mcRollStickHeadingAdjustmentActive
&& ABS(rcCommand
[ROLL
]) > ABS(headingAdjustCommand
)) {
1165 headingAdjustCommand
= -rcCommand
[ROLL
];
1168 timeMs_t timeDifference
= currentTimeMs
- posControl
.cruise
.lastCourseAdjustmentTime
;
1169 if (timeDifference
> 100) timeDifference
= 0; // if adjustment was called long time ago, reset the time difference.
1170 float rateTarget
= scaleRangef((float)headingAdjustCommand
, -500.0f
, 500.0f
, -cruiseYawRate
, cruiseYawRate
);
1171 float centidegsPerIteration
= rateTarget
* MS2S(timeDifference
);
1172 posControl
.cruise
.course
= wrap_36000(posControl
.cruise
.course
- centidegsPerIteration
);
1173 DEBUG_SET(DEBUG_CRUISE
, 1, CENTIDEGREES_TO_DEGREES(posControl
.cruise
.course
));
1174 posControl
.cruise
.lastCourseAdjustmentTime
= currentTimeMs
;
1175 } else if (currentTimeMs
- posControl
.cruise
.lastCourseAdjustmentTime
> 4000) {
1176 posControl
.cruise
.previousCourse
= posControl
.cruise
.course
;
1179 setDesiredPosition(NULL
, posControl
.cruise
.course
, NAV_POS_UPDATE_HEADING
);
1181 return NAV_FSM_EVENT_NONE
;
1184 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_COURSE_HOLD_ADJUSTING(navigationFSMState_t previousState
)
1186 UNUSED(previousState
);
1187 DEBUG_SET(DEBUG_CRUISE
, 0, 3);
1189 // User is rolling, changing manually direction. Wait until it is done and then restore CRUISE
1190 if (posControl
.flags
.isAdjustingPosition
) {
1191 posControl
.cruise
.course
= posControl
.actualState
.cog
; //store current course
1192 posControl
.cruise
.lastCourseAdjustmentTime
= millis();
1193 return NAV_FSM_EVENT_NONE
; // reprocess the state
1196 resetPositionController();
1198 return NAV_FSM_EVENT_SUCCESS
; // go back to NAV_STATE_COURSE_HOLD_IN_PROGRESS state
1201 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_CRUISE_INITIALIZE(navigationFSMState_t previousState
)
1203 if (STATE(MULTIROTOR
) && !navConfig()->general
.cruise_yaw_rate
) { // course hold not possible on MR without yaw control
1204 return NAV_FSM_EVENT_ERROR
;
1207 navOnEnteringState_NAV_STATE_ALTHOLD_INITIALIZE(previousState
);
1209 return navOnEnteringState_NAV_STATE_COURSE_HOLD_INITIALIZE(previousState
);
1212 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_CRUISE_IN_PROGRESS(navigationFSMState_t previousState
)
1214 navOnEnteringState_NAV_STATE_ALTHOLD_IN_PROGRESS(previousState
);
1216 return navOnEnteringState_NAV_STATE_COURSE_HOLD_IN_PROGRESS(previousState
);
1219 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_CRUISE_ADJUSTING(navigationFSMState_t previousState
)
1221 navOnEnteringState_NAV_STATE_ALTHOLD_IN_PROGRESS(previousState
);
1223 return navOnEnteringState_NAV_STATE_COURSE_HOLD_ADJUSTING(previousState
);
1226 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_INITIALIZE(navigationFSMState_t previousState
)
1228 UNUSED(previousState
);
1230 if ((posControl
.flags
.estHeadingStatus
== EST_NONE
) || (posControl
.flags
.estAltStatus
== EST_NONE
) || !STATE(GPS_FIX_HOME
)) {
1231 // Heading sensor, altitude sensor and HOME fix are mandatory for RTH. If not satisfied - switch to emergency landing
1232 // Relevant to failsafe forced RTH only. Switched RTH blocked in selectNavEventFromBoxModeInput if sensors unavailable.
1233 // If we are in dead-reckoning mode - also fail, since coordinates may be unreliable
1234 return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
;
1237 if (STATE(FIXED_WING_LEGACY
) && (posControl
.homeDistance
< navConfig()->general
.min_rth_distance
) && !posControl
.flags
.forcedRTHActivated
) {
1238 // Prevent RTH from activating on airplanes if too close to home unless it's a failsafe RTH
1239 return NAV_FSM_EVENT_SWITCH_TO_IDLE
;
1242 // If we have valid position sensor or configured to ignore it's loss at initial stage - continue
1243 if ((posControl
.flags
.estPosStatus
>= EST_USABLE
) || navConfig()->general
.flags
.rth_climb_ignore_emerg
) {
1244 // Prepare controllers
1245 resetPositionController();
1246 resetAltitudeController(false); // Make sure surface tracking is not enabled - RTH uses global altitude, not AGL
1247 setupAltitudeController();
1249 // If close to home - reset home position and land
1250 if (posControl
.homeDistance
< navConfig()->general
.min_rth_distance
) {
1251 setHomePosition(&navGetCurrentActualPositionAndVelocity()->pos
, posControl
.actualState
.yaw
, NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_HEADING
, NAV_HOME_VALID_ALL
);
1252 setDesiredPosition(&navGetCurrentActualPositionAndVelocity()->pos
, posControl
.actualState
.yaw
, NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_Z
| NAV_POS_UPDATE_HEADING
);
1254 return NAV_FSM_EVENT_SWITCH_TO_RTH_LANDING
; // NAV_STATE_RTH_HOVER_PRIOR_TO_LANDING
1257 // Switch to RTH trackback
1258 bool trackbackActive
= navConfig()->general
.flags
.rth_trackback_mode
== RTH_TRACKBACK_ON
||
1259 (navConfig()->general
.flags
.rth_trackback_mode
== RTH_TRACKBACK_FS
&& posControl
.flags
.forcedRTHActivated
);
1261 if (trackbackActive
&& posControl
.activeRthTBPointIndex
>= 0 && !isWaypointMissionRTHActive()) {
1262 updateRthTrackback(true); // save final trackpoint for altitude and max trackback distance reference
1263 posControl
.flags
.rthTrackbackActive
= true;
1264 calculateAndSetActiveWaypointToLocalPosition(rthGetTrackbackPos());
1265 return NAV_FSM_EVENT_SWITCH_TO_NAV_STATE_RTH_TRACKBACK
;
1268 fpVector3_t targetHoldPos
;
1270 if (STATE(FIXED_WING_LEGACY
)) {
1271 // Airplane - climbout before heading home
1272 if (navConfig()->general
.flags
.rth_climb_first
== RTH_CLIMB_ON_FW_SPIRAL
) {
1273 // Spiral climb centered at xy of RTH activation
1274 calculateInitialHoldPosition(&targetHoldPos
);
1276 calculateFarAwayTarget(&targetHoldPos
, posControl
.actualState
.cog
, 100000.0f
); // 1km away Linear climb
1279 // Multicopter, hover and climb
1280 calculateInitialHoldPosition(&targetHoldPos
);
1282 // Initialize RTH sanity check to prevent fly-aways on RTH
1283 // For airplanes this is delayed until climb-out is finished
1284 initializeRTHSanityChecker();
1287 setDesiredPosition(&targetHoldPos
, posControl
.actualState
.yaw
, NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_HEADING
);
1289 return NAV_FSM_EVENT_SUCCESS
; // NAV_STATE_RTH_CLIMB_TO_SAFE_ALT
1292 /* Position sensor failure timeout - land. Land immediately if failsafe RTH and timeout disabled (set to 0) */
1293 else if (checkForPositionSensorTimeout() || (!navConfig()->general
.pos_failure_timeout
&& posControl
.flags
.forcedRTHActivated
)) {
1294 return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
;
1296 /* No valid POS sensor but still within valid timeout - wait */
1297 return NAV_FSM_EVENT_NONE
;
1300 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_CLIMB_TO_SAFE_ALT(navigationFSMState_t previousState
)
1302 UNUSED(previousState
);
1304 if (!STATE(ALTITUDE_CONTROL
)) {
1305 //If altitude control is not a thing, switch to RTH in progress instead
1306 return NAV_FSM_EVENT_SUCCESS
; //Will cause NAV_STATE_RTH_HEAD_HOME
1309 rthAltControlStickOverrideCheck(PITCH
);
1311 /* Position sensor failure timeout and not configured to ignore GPS loss - land */
1312 if ((posControl
.flags
.estHeadingStatus
== EST_NONE
) ||
1313 (checkForPositionSensorTimeout() && !navConfig()->general
.flags
.rth_climb_ignore_emerg
)) {
1314 return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
;
1317 const uint8_t rthClimbMarginPercent
= STATE(FIXED_WING_LEGACY
) ? FW_RTH_CLIMB_MARGIN_PERCENT
: MR_RTH_CLIMB_MARGIN_PERCENT
;
1318 const float rthAltitudeMargin
= MAX(FW_RTH_CLIMB_MARGIN_MIN_CM
, (rthClimbMarginPercent
/100.0f
) * fabsf(posControl
.rthState
.rthInitialAltitude
- posControl
.rthState
.homePosition
.pos
.z
));
1320 // If we reached desired initial RTH altitude or we don't want to climb first
1321 if (((navGetCurrentActualPositionAndVelocity()->pos
.z
- posControl
.rthState
.rthInitialAltitude
) > -rthAltitudeMargin
) || (navConfig()->general
.flags
.rth_climb_first
== RTH_CLIMB_OFF
) || rthAltControlStickOverrideCheck(ROLL
) || rthClimbStageActiveAndComplete()) {
1323 // Delayed initialization for RTH sanity check on airplanes - allow to finish climb first as it can take some distance
1324 if (STATE(FIXED_WING_LEGACY
)) {
1325 initializeRTHSanityChecker();
1328 // Save initial home distance for future use
1329 posControl
.rthState
.rthInitialDistance
= posControl
.homeDistance
;
1330 fpVector3_t
* tmpHomePos
= rthGetHomeTargetPosition(RTH_HOME_ENROUTE_INITIAL
);
1332 if (navConfig()->general
.flags
.rth_tail_first
&& !STATE(FIXED_WING_LEGACY
)) {
1333 setDesiredPosition(tmpHomePos
, 0, NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_Z
| NAV_POS_UPDATE_BEARING_TAIL_FIRST
);
1336 setDesiredPosition(tmpHomePos
, 0, NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_Z
| NAV_POS_UPDATE_BEARING
);
1339 return NAV_FSM_EVENT_SUCCESS
; // NAV_STATE_RTH_HEAD_HOME
1343 fpVector3_t
* tmpHomePos
= rthGetHomeTargetPosition(RTH_HOME_ENROUTE_INITIAL
);
1345 /* For multi-rotors execute sanity check during initial ascent as well */
1346 if (!STATE(FIXED_WING_LEGACY
) && !validateRTHSanityChecker()) {
1347 return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
;
1350 // Climb to safe altitude and turn to correct direction
1351 // Until the initial climb phase is complete target slightly *above* the cruise altitude to ensure we actually reach
1352 // it in a reasonable time. Immediately after we finish this phase - target the original altitude.
1353 if (STATE(FIXED_WING_LEGACY
)) {
1354 tmpHomePos
->z
+= FW_RTH_CLIMB_OVERSHOOT_CM
;
1355 setDesiredPosition(tmpHomePos
, 0, NAV_POS_UPDATE_Z
);
1357 tmpHomePos
->z
+= MR_RTH_CLIMB_OVERSHOOT_CM
;
1359 if (navConfig()->general
.flags
.rth_tail_first
) {
1360 setDesiredPosition(tmpHomePos
, 0, NAV_POS_UPDATE_Z
| NAV_POS_UPDATE_BEARING_TAIL_FIRST
);
1362 setDesiredPosition(tmpHomePos
, 0, NAV_POS_UPDATE_Z
| NAV_POS_UPDATE_BEARING
);
1366 return NAV_FSM_EVENT_NONE
;
1370 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_TRACKBACK(navigationFSMState_t previousState
)
1372 UNUSED(previousState
);
1374 /* If position sensors unavailable - land immediately */
1375 if ((posControl
.flags
.estHeadingStatus
== EST_NONE
) || checkForPositionSensorTimeout()) {
1376 return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
;
1379 if (posControl
.flags
.estPosStatus
>= EST_USABLE
) {
1380 const int32_t distFromStartTrackback
= calculateDistanceToDestination(&posControl
.rthTBPointsList
[posControl
.rthTBLastSavedIndex
]) / 100;
1381 #ifdef USE_MULTI_FUNCTIONS
1382 const bool overrideTrackback
= rthAltControlStickOverrideCheck(ROLL
) || MULTI_FUNC_FLAG(MF_SUSPEND_TRACKBACK
);
1384 const bool overrideTrackback
= rthAltControlStickOverrideCheck(ROLL
);
1386 const bool cancelTrackback
= distFromStartTrackback
> navConfig()->general
.rth_trackback_distance
||
1387 (overrideTrackback
&& !posControl
.flags
.forcedRTHActivated
);
1389 if (posControl
.activeRthTBPointIndex
< 0 || cancelTrackback
) {
1390 posControl
.rthTBWrapAroundCounter
= posControl
.activeRthTBPointIndex
= -1;
1391 posControl
.flags
.rthTrackbackActive
= false;
1392 return NAV_FSM_EVENT_SWITCH_TO_NAV_STATE_RTH_INITIALIZE
; // procede to home after final trackback point
1395 if (isWaypointReached(&posControl
.activeWaypoint
.pos
, &posControl
.activeWaypoint
.bearing
)) {
1396 posControl
.activeRthTBPointIndex
--;
1398 if (posControl
.rthTBWrapAroundCounter
> -1 && posControl
.activeRthTBPointIndex
< 0) {
1399 posControl
.activeRthTBPointIndex
= NAV_RTH_TRACKBACK_POINTS
- 1;
1401 calculateAndSetActiveWaypointToLocalPosition(rthGetTrackbackPos());
1403 if (posControl
.activeRthTBPointIndex
- posControl
.rthTBWrapAroundCounter
== 0) {
1404 posControl
.rthTBWrapAroundCounter
= posControl
.activeRthTBPointIndex
= -1;
1407 setDesiredPosition(rthGetTrackbackPos(), 0, NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_Z
| NAV_POS_UPDATE_BEARING
);
1411 return NAV_FSM_EVENT_NONE
;
1414 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_HEAD_HOME(navigationFSMState_t previousState
)
1416 UNUSED(previousState
);
1418 rthAltControlStickOverrideCheck(PITCH
);
1420 /* If position sensors unavailable - land immediately */
1421 if ((posControl
.flags
.estHeadingStatus
== EST_NONE
) || !validateRTHSanityChecker()) {
1422 return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
;
1425 if (checkMixerATRequired(MIXERAT_REQUEST_RTH
) && (calculateDistanceToDestination(&posControl
.rthState
.homePosition
.pos
) > (navConfig()->fw
.loiter_radius
* 3))){
1426 return NAV_FSM_EVENT_SWITCH_TO_MIXERAT
;
1429 if (navConfig()->general
.flags
.rth_use_linear_descent
&& navConfig()->general
.rth_home_altitude
> 0) {
1430 // Check linear descent status
1431 uint32_t homeDistance
= calculateDistanceToDestination(&posControl
.rthState
.homePosition
.pos
);
1433 if (homeDistance
<= METERS_TO_CENTIMETERS(navConfig()->general
.rth_linear_descent_start_distance
)) {
1434 posControl
.rthState
.rthFinalAltitude
= posControl
.rthState
.homePosition
.pos
.z
+ navConfig()->general
.rth_home_altitude
;
1438 // If we have position sensor - continue home
1439 if ((posControl
.flags
.estPosStatus
>= EST_USABLE
)) {
1440 fpVector3_t
* tmpHomePos
= rthGetHomeTargetPosition(RTH_HOME_ENROUTE_PROPORTIONAL
);
1442 if (isWaypointReached(tmpHomePos
, 0)) {
1443 // Successfully reached position target - update XYZ-position
1444 setDesiredPosition(tmpHomePos
, posControl
.rthState
.homePosition
.heading
, NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_Z
| NAV_POS_UPDATE_HEADING
);
1445 return NAV_FSM_EVENT_SUCCESS
; // NAV_STATE_RTH_HOVER_PRIOR_TO_LANDING
1447 setDesiredPosition(tmpHomePos
, 0, NAV_POS_UPDATE_Z
| NAV_POS_UPDATE_XY
);
1448 return NAV_FSM_EVENT_NONE
;
1451 /* Position sensor failure timeout - land */
1452 else if (checkForPositionSensorTimeout()) {
1453 return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
;
1455 /* No valid POS sensor but still within valid timeout - wait */
1456 return NAV_FSM_EVENT_NONE
;
1459 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_HOVER_PRIOR_TO_LANDING(navigationFSMState_t previousState
)
1461 UNUSED(previousState
);
1463 //On ROVER and BOAT we immediately switch to the next event
1464 if (!STATE(ALTITUDE_CONTROL
)) {
1465 return NAV_FSM_EVENT_SUCCESS
;
1468 /* If position sensors unavailable - land immediately (wait for timeout on GPS) */
1469 if ((posControl
.flags
.estHeadingStatus
== EST_NONE
) || checkForPositionSensorTimeout() || !validateRTHSanityChecker()) {
1470 return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
;
1473 // If position ok OR within valid timeout - continue
1474 // Wait until target heading is reached for MR (with 15 deg margin for error), or continue for Fixed Wing
1475 if ((ABS(wrap_18000(posControl
.rthState
.homePosition
.heading
- posControl
.actualState
.yaw
)) < DEGREES_TO_CENTIDEGREES(15)) || STATE(FIXED_WING_LEGACY
)) {
1476 resetLandingDetector(); // force reset landing detector just in case
1477 updateClimbRateToAltitudeController(0, 0, ROC_TO_ALT_RESET
);
1478 return navigationRTHAllowsLanding() ? NAV_FSM_EVENT_SUCCESS
: NAV_FSM_EVENT_SWITCH_TO_RTH_HOVER_ABOVE_HOME
; // success = land
1480 fpVector3_t
* tmpHomePos
= rthGetHomeTargetPosition(RTH_HOME_ENROUTE_FINAL
);
1481 setDesiredPosition(tmpHomePos
, posControl
.rthState
.homePosition
.heading
, NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_Z
| NAV_POS_UPDATE_HEADING
);
1482 return NAV_FSM_EVENT_NONE
;
1486 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_HOVER_ABOVE_HOME(navigationFSMState_t previousState
)
1488 UNUSED(previousState
);
1490 /* If position sensors unavailable - land immediately (wait for timeout on GPS) */
1491 if (posControl
.flags
.estHeadingStatus
== EST_NONE
|| checkForPositionSensorTimeout() || !validateRTHSanityChecker()) {
1492 return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
;
1495 fpVector3_t
* tmpHomePos
= rthGetHomeTargetPosition(RTH_HOME_FINAL_HOVER
);
1496 setDesiredPosition(tmpHomePos
, 0, NAV_POS_UPDATE_Z
);
1498 return NAV_FSM_EVENT_NONE
;
1501 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_LANDING(navigationFSMState_t previousState
)
1503 UNUSED(previousState
);
1505 //On ROVER and BOAT we immediately switch to the next event
1506 if (!STATE(ALTITUDE_CONTROL
)) {
1507 return NAV_FSM_EVENT_SUCCESS
;
1510 if (!ARMING_FLAG(ARMED
) || STATE(LANDING_DETECTED
)) {
1511 return NAV_FSM_EVENT_SUCCESS
;
1514 /* If position sensors unavailable - land immediately (wait for timeout on GPS)
1515 * Continue to check for RTH sanity during landing */
1516 if (posControl
.flags
.estHeadingStatus
== EST_NONE
|| checkForPositionSensorTimeout() || !validateRTHSanityChecker()) {
1517 return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
;
1520 if (checkMixerATRequired(MIXERAT_REQUEST_LAND
)){
1521 return NAV_FSM_EVENT_SWITCH_TO_MIXERAT
;
1524 float descentVelLimited
= 0;
1526 fpVector3_t tmpHomePos
= posControl
.rthState
.homeTmpWaypoint
;
1527 uint32_t remaning_distance
= calculateDistanceToDestination(&tmpHomePos
);
1529 int32_t landingElevation
= posControl
.rthState
.homeTmpWaypoint
.z
;
1530 if(STATE(MULTIROTOR
) && (remaning_distance
>MR_RTH_LAND_MARGIN_CM
)){
1531 descentVelLimited
= navConfig()->general
.land_minalt_vspd
;
1533 // A safeguard - if surface altitude sensors is available and it is reading < 50cm altitude - drop to low descend speed
1534 else if ((posControl
.flags
.estAglStatus
== EST_TRUSTED
) && posControl
.actualState
.agl
.pos
.z
< 50.0f
) {
1535 // land_descent_rate == 200 : descend speed = 30 cm/s, gentle touchdown
1536 // Do not allow descent velocity slower than -30cm/s so the landing detector works.
1537 descentVelLimited
= navConfig()->general
.land_minalt_vspd
;
1539 // Ramp down descent velocity from 100% at maxAlt altitude to 25% from minAlt to 0cm.
1540 float descentVelScaled
= scaleRangef(navGetCurrentActualPositionAndVelocity()->pos
.z
,
1541 navConfig()->general
.land_slowdown_minalt
+ landingElevation
,
1542 navConfig()->general
.land_slowdown_maxalt
+ landingElevation
,
1543 navConfig()->general
.land_minalt_vspd
, navConfig()->general
.land_maxalt_vspd
);
1545 descentVelLimited
= constrainf(descentVelScaled
, navConfig()->general
.land_minalt_vspd
, navConfig()->general
.land_maxalt_vspd
);
1548 updateClimbRateToAltitudeController(-descentVelLimited
, 0, ROC_TO_ALT_CONSTANT
);
1550 return NAV_FSM_EVENT_NONE
;
1553 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_FINISHING(navigationFSMState_t previousState
)
1555 UNUSED(previousState
);
1557 //On ROVER and BOAT disarm immediately
1558 if (!STATE(ALTITUDE_CONTROL
)) {
1559 disarm(DISARM_NAVIGATION
);
1562 return NAV_FSM_EVENT_SUCCESS
;
1565 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_FINISHED(navigationFSMState_t previousState
)
1567 // Stay in this state
1568 UNUSED(previousState
);
1570 if (STATE(ALTITUDE_CONTROL
)) {
1571 updateClimbRateToAltitudeController(-1.1f
* navConfig()->general
.land_minalt_vspd
, 0, ROC_TO_ALT_CONSTANT
); // FIXME
1574 // Prevent I-terms growing when already landed
1575 pidResetErrorAccumulators();
1576 return NAV_FSM_EVENT_NONE
;
1579 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_WAYPOINT_INITIALIZE(navigationFSMState_t previousState
)
1581 UNUSED(previousState
);
1583 if (!posControl
.waypointCount
|| !posControl
.waypointListValid
) {
1584 return NAV_FSM_EVENT_ERROR
;
1587 // Prepare controllers
1588 resetPositionController();
1589 resetAltitudeController(false); // Make sure surface tracking is not enabled - WP uses global altitude, not AGL
1591 if (posControl
.activeWaypointIndex
== posControl
.startWpIndex
|| posControl
.wpMissionRestart
) {
1592 /* Use p3 as the volatile jump counter, allowing embedded, rearmed jumps
1593 Using p3 minimises the risk of saving an invalid counter if a mission is aborted */
1594 setupJumpCounters();
1595 posControl
.activeWaypointIndex
= posControl
.startWpIndex
;
1596 wpHeadingControl
.mode
= NAV_WP_HEAD_MODE_NONE
;
1599 if (navConfig()->general
.flags
.waypoint_mission_restart
== WP_MISSION_SWITCH
) {
1600 posControl
.wpMissionRestart
= posControl
.activeWaypointIndex
> posControl
.startWpIndex
? !posControl
.wpMissionRestart
: false;
1602 posControl
.wpMissionRestart
= navConfig()->general
.flags
.waypoint_mission_restart
== WP_MISSION_START
;
1605 return NAV_FSM_EVENT_SUCCESS
; // will switch to NAV_STATE_WAYPOINT_PRE_ACTION
1608 static navigationFSMEvent_t
nextForNonGeoStates(void)
1610 /* simple helper for non-geographical states that just set other data */
1611 if (isLastMissionWaypoint()) { // non-geo state is the last waypoint, switch to finish.
1612 return NAV_FSM_EVENT_SWITCH_TO_WAYPOINT_FINISHED
;
1613 } else { // Finished non-geo, move to next WP
1614 posControl
.activeWaypointIndex
++;
1615 return NAV_FSM_EVENT_NONE
; // re-process the state passing to the next WP
1619 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_WAYPOINT_PRE_ACTION(navigationFSMState_t previousState
)
1621 /* A helper function to do waypoint-specific action */
1622 UNUSED(previousState
);
1624 switch ((navWaypointActions_e
)posControl
.waypointList
[posControl
.activeWaypointIndex
].action
) {
1625 case NAV_WP_ACTION_HOLD_TIME
:
1626 case NAV_WP_ACTION_WAYPOINT
:
1627 case NAV_WP_ACTION_LAND
:
1628 calculateAndSetActiveWaypoint(&posControl
.waypointList
[posControl
.activeWaypointIndex
]);
1629 posControl
.wpInitialDistance
= calculateDistanceToDestination(&posControl
.activeWaypoint
.pos
);
1630 posControl
.wpInitialAltitude
= posControl
.actualState
.abs
.pos
.z
;
1631 posControl
.wpAltitudeReached
= false;
1632 return NAV_FSM_EVENT_SUCCESS
; // will switch to NAV_STATE_WAYPOINT_IN_PROGRESS
1634 case NAV_WP_ACTION_JUMP
:
1635 // We use p3 as the volatile jump counter (p2 is the static value)
1636 if (posControl
.waypointList
[posControl
.activeWaypointIndex
].p3
!= -1) {
1637 if (posControl
.waypointList
[posControl
.activeWaypointIndex
].p3
== 0) {
1639 return nextForNonGeoStates();
1643 posControl
.waypointList
[posControl
.activeWaypointIndex
].p3
--;
1646 posControl
.activeWaypointIndex
= posControl
.waypointList
[posControl
.activeWaypointIndex
].p1
+ posControl
.startWpIndex
;
1647 return NAV_FSM_EVENT_NONE
; // re-process the state passing to the next WP
1649 case NAV_WP_ACTION_SET_POI
:
1650 if (STATE(MULTIROTOR
)) {
1651 wpHeadingControl
.mode
= NAV_WP_HEAD_MODE_POI
;
1652 mapWaypointToLocalPosition(&wpHeadingControl
.poi_pos
,
1653 &posControl
.waypointList
[posControl
.activeWaypointIndex
], GEO_ALT_RELATIVE
);
1655 return nextForNonGeoStates();
1657 case NAV_WP_ACTION_SET_HEAD
:
1658 if (STATE(MULTIROTOR
)) {
1659 if (posControl
.waypointList
[posControl
.activeWaypointIndex
].p1
< 0 ||
1660 posControl
.waypointList
[posControl
.activeWaypointIndex
].p1
> 359) {
1661 wpHeadingControl
.mode
= NAV_WP_HEAD_MODE_NONE
;
1663 wpHeadingControl
.mode
= NAV_WP_HEAD_MODE_FIXED
;
1664 wpHeadingControl
.heading
= DEGREES_TO_CENTIDEGREES(posControl
.waypointList
[posControl
.activeWaypointIndex
].p1
);
1667 return nextForNonGeoStates();
1669 case NAV_WP_ACTION_RTH
:
1670 posControl
.wpMissionRestart
= true;
1671 return NAV_FSM_EVENT_SWITCH_TO_RTH
;
1677 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_WAYPOINT_IN_PROGRESS(navigationFSMState_t previousState
)
1679 UNUSED(previousState
);
1681 // If no position sensor available - land immediately
1682 if ((posControl
.flags
.estPosStatus
>= EST_USABLE
) && (posControl
.flags
.estHeadingStatus
>= EST_USABLE
)) {
1683 switch ((navWaypointActions_e
)posControl
.waypointList
[posControl
.activeWaypointIndex
].action
) {
1684 case NAV_WP_ACTION_HOLD_TIME
:
1685 case NAV_WP_ACTION_WAYPOINT
:
1686 case NAV_WP_ACTION_LAND
:
1687 if (isWaypointReached(&posControl
.activeWaypoint
.pos
, &posControl
.activeWaypoint
.bearing
)) {
1688 return NAV_FSM_EVENT_SUCCESS
; // will switch to NAV_STATE_WAYPOINT_REACHED
1691 fpVector3_t tmpWaypoint
;
1692 tmpWaypoint
.x
= posControl
.activeWaypoint
.pos
.x
;
1693 tmpWaypoint
.y
= posControl
.activeWaypoint
.pos
.y
;
1694 tmpWaypoint
.z
= scaleRangef(constrainf(posControl
.wpDistance
, posControl
.wpInitialDistance
/ 10.0f
, posControl
.wpInitialDistance
),
1695 posControl
.wpInitialDistance
, posControl
.wpInitialDistance
/ 10.0f
,
1696 posControl
.wpInitialAltitude
, posControl
.activeWaypoint
.pos
.z
);
1697 setDesiredPosition(&tmpWaypoint
, 0, NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_Z
| NAV_POS_UPDATE_BEARING
);
1698 if(STATE(MULTIROTOR
)) {
1699 switch (wpHeadingControl
.mode
) {
1700 case NAV_WP_HEAD_MODE_NONE
:
1702 case NAV_WP_HEAD_MODE_FIXED
:
1703 setDesiredPosition(NULL
, wpHeadingControl
.heading
, NAV_POS_UPDATE_HEADING
);
1705 case NAV_WP_HEAD_MODE_POI
:
1706 setDesiredPosition(&wpHeadingControl
.poi_pos
, 0, NAV_POS_UPDATE_BEARING
);
1710 return NAV_FSM_EVENT_NONE
; // will re-process state in >10ms
1714 case NAV_WP_ACTION_JUMP
:
1715 case NAV_WP_ACTION_SET_HEAD
:
1716 case NAV_WP_ACTION_SET_POI
:
1717 case NAV_WP_ACTION_RTH
:
1721 /* If position sensors unavailable - land immediately (wait for timeout on GPS) */
1722 else if (checkForPositionSensorTimeout() || (posControl
.flags
.estHeadingStatus
== EST_NONE
)) {
1723 return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
;
1726 return NAV_FSM_EVENT_NONE
; // will re-process state in >10ms
1729 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_WAYPOINT_REACHED(navigationFSMState_t previousState
)
1731 UNUSED(previousState
);
1733 if (navConfig()->general
.waypoint_enforce_altitude
) {
1734 posControl
.wpAltitudeReached
= isWaypointAltitudeReached();
1737 switch ((navWaypointActions_e
)posControl
.waypointList
[posControl
.activeWaypointIndex
].action
) {
1738 case NAV_WP_ACTION_WAYPOINT
:
1739 if (navConfig()->general
.waypoint_enforce_altitude
&& !posControl
.wpAltitudeReached
) {
1740 return NAV_FSM_EVENT_SWITCH_TO_WAYPOINT_HOLD_TIME
;
1742 return NAV_FSM_EVENT_SUCCESS
; // NAV_STATE_WAYPOINT_NEXT
1745 case NAV_WP_ACTION_JUMP
:
1746 case NAV_WP_ACTION_SET_HEAD
:
1747 case NAV_WP_ACTION_SET_POI
:
1748 case NAV_WP_ACTION_RTH
:
1751 case NAV_WP_ACTION_LAND
:
1752 return NAV_FSM_EVENT_SWITCH_TO_WAYPOINT_RTH_LAND
;
1754 case NAV_WP_ACTION_HOLD_TIME
:
1755 // Save the current time for the time the waypoint was reached
1756 posControl
.wpReachedTime
= millis();
1757 return NAV_FSM_EVENT_SWITCH_TO_WAYPOINT_HOLD_TIME
;
1763 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_WAYPOINT_HOLD_TIME(navigationFSMState_t previousState
)
1765 UNUSED(previousState
);
1767 /* If position sensors unavailable - land immediately (wait for timeout on GPS) */
1768 if (posControl
.flags
.estHeadingStatus
== EST_NONE
|| checkForPositionSensorTimeout()) {
1769 return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
;
1772 if (navConfig()->general
.waypoint_enforce_altitude
&& !posControl
.wpAltitudeReached
) {
1773 // Adjust altitude to waypoint setting
1774 setDesiredPosition(&posControl
.activeWaypoint
.pos
, 0, NAV_POS_UPDATE_Z
);
1776 posControl
.wpAltitudeReached
= isWaypointAltitudeReached();
1778 if (posControl
.wpAltitudeReached
) {
1779 posControl
.wpReachedTime
= millis();
1781 return NAV_FSM_EVENT_NONE
;
1785 timeMs_t currentTime
= millis();
1787 if (posControl
.waypointList
[posControl
.activeWaypointIndex
].p1
<= 0 ||
1788 posControl
.waypointList
[posControl
.activeWaypointIndex
].action
== NAV_WP_ACTION_WAYPOINT
||
1789 (posControl
.wpReachedTime
!= 0 && currentTime
- posControl
.wpReachedTime
>= (timeMs_t
)posControl
.waypointList
[posControl
.activeWaypointIndex
].p1
*1000L)) {
1790 return NAV_FSM_EVENT_SUCCESS
;
1793 return NAV_FSM_EVENT_NONE
; // will re-process state in >10ms
1796 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_WAYPOINT_RTH_LAND(navigationFSMState_t previousState
)
1798 UNUSED(previousState
);
1800 const navigationFSMEvent_t landEvent
= navOnEnteringState_NAV_STATE_RTH_LANDING(previousState
);
1801 if (landEvent
== NAV_FSM_EVENT_SUCCESS
) {
1802 // Landing controller returned success - invoke RTH finishing state and finish the waypoint
1803 navOnEnteringState_NAV_STATE_RTH_FINISHING(previousState
);
1804 return NAV_FSM_EVENT_SUCCESS
;
1807 return NAV_FSM_EVENT_NONE
;
1811 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_WAYPOINT_NEXT(navigationFSMState_t previousState
)
1813 UNUSED(previousState
);
1815 if (isLastMissionWaypoint()) { // Last waypoint reached
1816 return NAV_FSM_EVENT_SWITCH_TO_WAYPOINT_FINISHED
;
1819 // Waypoint reached, do something and move on to next waypoint
1820 posControl
.activeWaypointIndex
++;
1821 return NAV_FSM_EVENT_SUCCESS
; // will switch to NAV_STATE_WAYPOINT_PRE_ACTION
1825 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_WAYPOINT_FINISHED(navigationFSMState_t previousState
)
1827 UNUSED(previousState
);
1829 clearJumpCounters();
1830 posControl
.wpMissionRestart
= true;
1832 /* If position sensors unavailable - land immediately (wait for timeout on GPS) */
1833 if (posControl
.flags
.estHeadingStatus
== EST_NONE
|| checkForPositionSensorTimeout()) {
1834 return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
;
1837 return NAV_FSM_EVENT_NONE
; // will re-process state in >10ms
1840 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_EMERGENCY_LANDING_INITIALIZE(navigationFSMState_t previousState
)
1842 UNUSED(previousState
);
1844 if ((posControl
.flags
.estPosStatus
>= EST_USABLE
)) {
1845 resetPositionController();
1846 setDesiredPosition(&navGetCurrentActualPositionAndVelocity()->pos
, 0, NAV_POS_UPDATE_XY
);
1849 // Emergency landing MAY use common altitude controller if vertical position is valid - initialize it
1850 // Make sure terrain following is not enabled
1851 resetAltitudeController(false);
1853 return NAV_FSM_EVENT_SUCCESS
;
1856 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_EMERGENCY_LANDING_IN_PROGRESS(navigationFSMState_t previousState
)
1858 UNUSED(previousState
);
1860 // Reset target position if too far away for some reason, e.g. GPS recovered since start landing.
1861 if (posControl
.flags
.estPosStatus
>= EST_USABLE
) {
1862 float targetPosLimit
= STATE(MULTIROTOR
) ? 2000.0f
: navConfig()->fw
.loiter_radius
* 2.0f
;
1863 if (calculateDistanceToDestination(&posControl
.desiredState
.pos
) > targetPosLimit
) {
1864 setDesiredPosition(&navGetCurrentActualPositionAndVelocity()->pos
, 0, NAV_POS_UPDATE_XY
);
1868 if (STATE(LANDING_DETECTED
)) {
1869 return NAV_FSM_EVENT_SUCCESS
;
1872 return NAV_FSM_EVENT_NONE
;
1875 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_EMERGENCY_LANDING_FINISHED(navigationFSMState_t previousState
)
1877 UNUSED(previousState
);
1879 return NAV_FSM_EVENT_NONE
;
1882 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_LAUNCH_INITIALIZE(navigationFSMState_t previousState
)
1884 const timeUs_t currentTimeUs
= micros();
1885 UNUSED(previousState
);
1887 resetFixedWingLaunchController(currentTimeUs
);
1889 return NAV_FSM_EVENT_SUCCESS
; // NAV_STATE_LAUNCH_WAIT
1892 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_LAUNCH_WAIT(navigationFSMState_t previousState
)
1894 const timeUs_t currentTimeUs
= micros();
1895 UNUSED(previousState
);
1897 // Continue immediately to launch in progress if manual launch throttle used
1898 if (navConfig()->fw
.launch_manual_throttle
) {
1899 return NAV_FSM_EVENT_SUCCESS
;
1902 if (fixedWingLaunchStatus() == FW_LAUNCH_DETECTED
) {
1903 enableFixedWingLaunchController(currentTimeUs
);
1904 return NAV_FSM_EVENT_SUCCESS
; // NAV_STATE_LAUNCH_IN_PROGRESS
1907 // abort NAV_LAUNCH_MODE by moving sticks with low throttle or throttle stick < launch idle throttle
1908 if (abortLaunchAllowed() && isRollPitchStickDeflected(navConfig()->fw
.launch_abort_deadband
)) {
1909 abortFixedWingLaunch();
1910 return NAV_FSM_EVENT_SWITCH_TO_IDLE
;
1913 return NAV_FSM_EVENT_NONE
;
1916 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_LAUNCH_IN_PROGRESS(navigationFSMState_t previousState
)
1918 UNUSED(previousState
);
1920 if (fixedWingLaunchStatus() >= FW_LAUNCH_ABORTED
) {
1921 return NAV_FSM_EVENT_SUCCESS
;
1924 return NAV_FSM_EVENT_NONE
;
1927 navigationFSMState_t navMixerATPendingState
= NAV_STATE_IDLE
;
1928 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_MIXERAT_INITIALIZE(navigationFSMState_t previousState
)
1930 const navigationFSMStateFlags_t prevFlags
= navGetStateFlags(previousState
);
1932 // Prepare altitude controller if idle, RTH or WP modes active or surface mode status changed
1933 if (!(prevFlags
& NAV_CTL_ALT
) || (prevFlags
& NAV_AUTO_RTH
) || (prevFlags
& NAV_AUTO_WP
)) {
1934 resetAltitudeController(false);
1935 setupAltitudeController();
1937 setDesiredPosition(&navGetCurrentActualPositionAndVelocity()->pos
, posControl
.actualState
.yaw
, NAV_POS_UPDATE_Z
);
1938 navMixerATPendingState
= previousState
;
1939 return NAV_FSM_EVENT_SUCCESS
;
1942 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_MIXERAT_IN_PROGRESS(navigationFSMState_t previousState
)
1944 UNUSED(previousState
);
1945 mixerProfileATRequest_e required_action
;
1946 switch (navMixerATPendingState
)
1948 case NAV_STATE_RTH_HEAD_HOME
:
1949 required_action
= MIXERAT_REQUEST_RTH
;
1951 case NAV_STATE_RTH_LANDING
:
1952 required_action
= MIXERAT_REQUEST_LAND
;
1955 required_action
= MIXERAT_REQUEST_NONE
;
1958 if (mixerATUpdateState(required_action
)){
1959 // MixerAT is done, switch to next state
1960 resetPositionController();
1961 resetAltitudeController(false); // Make sure surface tracking is not enabled uses global altitude, not AGL
1962 mixerATUpdateState(MIXERAT_REQUEST_ABORT
);
1963 switch (navMixerATPendingState
)
1965 case NAV_STATE_RTH_HEAD_HOME
:
1966 setupAltitudeController();
1967 return NAV_FSM_EVENT_SWITCH_TO_RTH_HEAD_HOME
;
1969 case NAV_STATE_RTH_LANDING
:
1970 setupAltitudeController();
1971 return NAV_FSM_EVENT_SWITCH_TO_RTH_LANDING
;
1974 return NAV_FSM_EVENT_SWITCH_TO_IDLE
;
1979 setDesiredPosition(&navGetCurrentActualPositionAndVelocity()->pos
, posControl
.actualState
.yaw
, NAV_POS_UPDATE_Z
);
1981 return NAV_FSM_EVENT_NONE
;
1984 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_MIXERAT_ABORT(navigationFSMState_t previousState
)
1986 UNUSED(previousState
);
1987 mixerATUpdateState(MIXERAT_REQUEST_ABORT
);
1988 return NAV_FSM_EVENT_SUCCESS
;
1991 static navigationFSMState_t
navSetNewFSMState(navigationFSMState_t newState
)
1993 navigationFSMState_t previousState
;
1995 previousState
= posControl
.navState
;
1996 if (posControl
.navState
!= newState
) {
1997 posControl
.navState
= newState
;
1998 posControl
.navPersistentId
= navFSM
[newState
].persistentId
;
2000 return previousState
;
2003 static void navProcessFSMEvents(navigationFSMEvent_t injectedEvent
)
2005 const timeMs_t currentMillis
= millis();
2006 navigationFSMState_t previousState
= NAV_STATE_UNDEFINED
;
2007 static timeMs_t lastStateProcessTime
= 0;
2009 /* Process new injected event if event defined,
2010 * otherwise process timeout event if defined */
2011 if (injectedEvent
!= NAV_FSM_EVENT_NONE
&& navFSM
[posControl
.navState
].onEvent
[injectedEvent
] != NAV_STATE_UNDEFINED
) {
2013 previousState
= navSetNewFSMState(navFSM
[posControl
.navState
].onEvent
[injectedEvent
]);
2014 } else if ((navFSM
[posControl
.navState
].timeoutMs
> 0) && (navFSM
[posControl
.navState
].onEvent
[NAV_FSM_EVENT_TIMEOUT
] != NAV_STATE_UNDEFINED
) &&
2015 ((currentMillis
- lastStateProcessTime
) >= navFSM
[posControl
.navState
].timeoutMs
)) {
2017 previousState
= navSetNewFSMState(navFSM
[posControl
.navState
].onEvent
[NAV_FSM_EVENT_TIMEOUT
]);
2020 if (previousState
) { /* If state updated call new state's entry function */
2021 while (navFSM
[posControl
.navState
].onEntry
) {
2022 navigationFSMEvent_t newEvent
= navFSM
[posControl
.navState
].onEntry(previousState
);
2024 if ((newEvent
!= NAV_FSM_EVENT_NONE
) && (navFSM
[posControl
.navState
].onEvent
[newEvent
] != NAV_STATE_UNDEFINED
)) {
2025 previousState
= navSetNewFSMState(navFSM
[posControl
.navState
].onEvent
[newEvent
]);
2032 lastStateProcessTime
= currentMillis
;
2035 /* Update public system state information */
2036 NAV_Status
.mode
= MW_GPS_MODE_NONE
;
2038 if (ARMING_FLAG(ARMED
)) {
2039 navigationFSMStateFlags_t navStateFlags
= navGetStateFlags(posControl
.navState
);
2041 if (navStateFlags
& NAV_AUTO_RTH
) {
2042 NAV_Status
.mode
= MW_GPS_MODE_RTH
;
2044 else if (navStateFlags
& NAV_AUTO_WP
) {
2045 NAV_Status
.mode
= MW_GPS_MODE_NAV
;
2047 else if (navStateFlags
& NAV_CTL_EMERG
) {
2048 NAV_Status
.mode
= MW_GPS_MODE_EMERG
;
2050 else if (navStateFlags
& NAV_CTL_POS
) {
2051 NAV_Status
.mode
= MW_GPS_MODE_HOLD
;
2055 NAV_Status
.state
= navFSM
[posControl
.navState
].mwState
;
2056 NAV_Status
.error
= navFSM
[posControl
.navState
].mwError
;
2058 NAV_Status
.flags
= 0;
2059 if (posControl
.flags
.isAdjustingPosition
) NAV_Status
.flags
|= MW_NAV_FLAG_ADJUSTING_POSITION
;
2060 if (posControl
.flags
.isAdjustingAltitude
) NAV_Status
.flags
|= MW_NAV_FLAG_ADJUSTING_ALTITUDE
;
2062 NAV_Status
.activeWpIndex
= posControl
.activeWaypointIndex
- posControl
.startWpIndex
;
2063 NAV_Status
.activeWpNumber
= NAV_Status
.activeWpIndex
+ 1;
2065 NAV_Status
.activeWpAction
= 0;
2066 if ((posControl
.activeWaypointIndex
>= 0) && (posControl
.activeWaypointIndex
< NAV_MAX_WAYPOINTS
)) {
2067 NAV_Status
.activeWpAction
= posControl
.waypointList
[posControl
.activeWaypointIndex
].action
;
2071 static fpVector3_t
* rthGetHomeTargetPosition(rthTargetMode_e mode
)
2073 posControl
.rthState
.homeTmpWaypoint
= posControl
.rthState
.homePosition
.pos
;
2076 case RTH_HOME_ENROUTE_INITIAL
:
2077 posControl
.rthState
.homeTmpWaypoint
.z
= posControl
.rthState
.rthInitialAltitude
;
2080 case RTH_HOME_ENROUTE_PROPORTIONAL
:
2082 float rthTotalDistanceToTravel
= posControl
.rthState
.rthInitialDistance
- (STATE(FIXED_WING_LEGACY
) ? navConfig()->fw
.loiter_radius
: 0);
2083 if (rthTotalDistanceToTravel
>= 100) {
2084 float ratioNotTravelled
= constrainf(posControl
.homeDistance
/ rthTotalDistanceToTravel
, 0.0f
, 1.0f
);
2085 posControl
.rthState
.homeTmpWaypoint
.z
= (posControl
.rthState
.rthInitialAltitude
* ratioNotTravelled
) + (posControl
.rthState
.rthFinalAltitude
* (1.0f
- ratioNotTravelled
));
2088 posControl
.rthState
.homeTmpWaypoint
.z
= posControl
.rthState
.rthFinalAltitude
;
2093 case RTH_HOME_ENROUTE_FINAL
:
2094 posControl
.rthState
.homeTmpWaypoint
.z
= posControl
.rthState
.rthFinalAltitude
;
2097 case RTH_HOME_FINAL_HOVER
:
2098 if (navConfig()->general
.rth_home_altitude
) {
2099 posControl
.rthState
.homeTmpWaypoint
.z
= posControl
.rthState
.homePosition
.pos
.z
+ navConfig()->general
.rth_home_altitude
;
2102 // If home altitude not defined - fall back to final ENROUTE altitude
2103 posControl
.rthState
.homeTmpWaypoint
.z
= posControl
.rthState
.rthFinalAltitude
;
2107 case RTH_HOME_FINAL_LAND
:
2108 // if WP mission p2 > 0 use p2 value as landing elevation (in meters !) (otherwise default to takeoff home elevation)
2109 if (FLIGHT_MODE(NAV_WP_MODE
) && posControl
.waypointList
[posControl
.activeWaypointIndex
].action
== NAV_WP_ACTION_LAND
&& posControl
.waypointList
[posControl
.activeWaypointIndex
].p2
!= 0) {
2110 posControl
.rthState
.homeTmpWaypoint
.z
= posControl
.waypointList
[posControl
.activeWaypointIndex
].p2
* 100; // 100 -> m to cm
2111 if (waypointMissionAltConvMode(posControl
.waypointList
[posControl
.activeWaypointIndex
].p3
) == GEO_ALT_ABSOLUTE
) {
2112 posControl
.rthState
.homeTmpWaypoint
.z
-= posControl
.gpsOrigin
.alt
; // correct to relative if absolute SL altitude datum used
2118 return &posControl
.rthState
.homeTmpWaypoint
;
2121 /*-----------------------------------------------------------
2122 * Detects if thrust vector is facing downwards
2123 *-----------------------------------------------------------*/
2124 bool isThrustFacingDownwards(void)
2126 // Tilt angle <= 80 deg; cos(80) = 0.17364817766693034885171662676931
2127 return (calculateCosTiltAngle() >= 0.173648178f
);
2130 /*-----------------------------------------------------------
2131 * Checks if position sensor (GPS) is failing for a specified timeout (if enabled)
2132 *-----------------------------------------------------------*/
2133 bool checkForPositionSensorTimeout(void)
2135 if (navConfig()->general
.pos_failure_timeout
) {
2136 if ((posControl
.flags
.estPosStatus
== EST_NONE
) && ((millis() - posControl
.lastValidPositionTimeMs
) > (1000 * navConfig()->general
.pos_failure_timeout
))) {
2144 // Timeout not defined, never fail
2149 /*-----------------------------------------------------------
2150 * Processes an update to XY-position and velocity
2151 *-----------------------------------------------------------*/
2152 void updateActualHorizontalPositionAndVelocity(bool estPosValid
, bool estVelValid
, float newX
, float newY
, float newVelX
, float newVelY
)
2154 posControl
.actualState
.abs
.pos
.x
= newX
;
2155 posControl
.actualState
.abs
.pos
.y
= newY
;
2156 posControl
.actualState
.abs
.vel
.x
= newVelX
;
2157 posControl
.actualState
.abs
.vel
.y
= newVelY
;
2159 posControl
.actualState
.agl
.pos
.x
= newX
;
2160 posControl
.actualState
.agl
.pos
.y
= newY
;
2161 posControl
.actualState
.agl
.vel
.x
= newVelX
;
2162 posControl
.actualState
.agl
.vel
.y
= newVelY
;
2164 posControl
.actualState
.velXY
= calc_length_pythagorean_2D(newVelX
, newVelY
);
2166 // CASE 1: POS & VEL valid
2167 if (estPosValid
&& estVelValid
) {
2168 posControl
.flags
.estPosStatus
= EST_TRUSTED
;
2169 posControl
.flags
.estVelStatus
= EST_TRUSTED
;
2170 posControl
.flags
.horizontalPositionDataNew
= true;
2171 posControl
.lastValidPositionTimeMs
= millis();
2173 // CASE 1: POS invalid, VEL valid
2174 else if (!estPosValid
&& estVelValid
) {
2175 posControl
.flags
.estPosStatus
= EST_USABLE
; // Pos usable, but not trusted
2176 posControl
.flags
.estVelStatus
= EST_TRUSTED
;
2177 posControl
.flags
.horizontalPositionDataNew
= true;
2178 posControl
.lastValidPositionTimeMs
= millis();
2180 // CASE 3: can't use pos/vel data
2182 posControl
.flags
.estPosStatus
= EST_NONE
;
2183 posControl
.flags
.estVelStatus
= EST_NONE
;
2184 posControl
.flags
.horizontalPositionDataNew
= false;
2187 //Update blackbox data
2188 navLatestActualPosition
[X
] = newX
;
2189 navLatestActualPosition
[Y
] = newY
;
2190 navActualVelocity
[X
] = constrain(newVelX
, -32678, 32767);
2191 navActualVelocity
[Y
] = constrain(newVelY
, -32678, 32767);
2194 /*-----------------------------------------------------------
2195 * Processes an update to Z-position and velocity
2196 *-----------------------------------------------------------*/
2197 void updateActualAltitudeAndClimbRate(bool estimateValid
, float newAltitude
, float newVelocity
, float surfaceDistance
, float surfaceVelocity
, navigationEstimateStatus_e surfaceStatus
, float gpsCfEstimatedAltitudeError
)
2199 posControl
.actualState
.abs
.pos
.z
= newAltitude
;
2200 posControl
.actualState
.abs
.vel
.z
= newVelocity
;
2202 posControl
.actualState
.agl
.pos
.z
= surfaceDistance
;
2203 posControl
.actualState
.agl
.vel
.z
= surfaceVelocity
;
2205 // Update altitude that would be used when executing RTH
2206 if (estimateValid
) {
2207 updateDesiredRTHAltitude();
2209 // If we acquired new surface reference - changing from NONE/USABLE -> TRUSTED
2210 if ((surfaceStatus
== EST_TRUSTED
) && (posControl
.flags
.estAglStatus
!= EST_TRUSTED
)) {
2211 // If we are in terrain-following modes - signal that we should update the surface tracking setpoint
2212 // NONE/USABLE means that we were flying blind, now we should lock to surface
2213 //updateSurfaceTrackingSetpoint();
2216 posControl
.flags
.estAglStatus
= surfaceStatus
; // Could be TRUSTED or USABLE
2217 posControl
.flags
.estAltStatus
= EST_TRUSTED
;
2218 posControl
.flags
.verticalPositionDataNew
= true;
2219 posControl
.lastValidAltitudeTimeMs
= millis();
2220 /* flag set if mismatch between relative GPS and estimated altitude exceeds 20m */
2221 posControl
.flags
.gpsCfEstimatedAltitudeMismatch
= fabsf(gpsCfEstimatedAltitudeError
) > 2000;
2224 posControl
.flags
.estAltStatus
= EST_NONE
;
2225 posControl
.flags
.estAglStatus
= EST_NONE
;
2226 posControl
.flags
.verticalPositionDataNew
= false;
2227 posControl
.flags
.gpsCfEstimatedAltitudeMismatch
= false;
2230 if (ARMING_FLAG(ARMED
)) {
2231 if ((posControl
.flags
.estAglStatus
== EST_TRUSTED
) && surfaceDistance
> 0) {
2232 if (posControl
.actualState
.surfaceMin
> 0) {
2233 posControl
.actualState
.surfaceMin
= MIN(posControl
.actualState
.surfaceMin
, surfaceDistance
);
2236 posControl
.actualState
.surfaceMin
= surfaceDistance
;
2241 posControl
.actualState
.surfaceMin
= -1;
2244 //Update blackbox data
2245 navLatestActualPosition
[Z
] = navGetCurrentActualPositionAndVelocity()->pos
.z
;
2246 navActualVelocity
[Z
] = constrain(navGetCurrentActualPositionAndVelocity()->vel
.z
, -32678, 32767);
2249 /*-----------------------------------------------------------
2250 * Processes an update to estimated heading
2251 *-----------------------------------------------------------*/
2252 void updateActualHeading(bool headingValid
, int32_t newHeading
, int32_t newGroundCourse
)
2254 /* Update heading. Check if we're acquiring a valid heading for the
2255 * first time and update home heading accordingly.
2258 navigationEstimateStatus_e newEstHeading
= headingValid
? EST_TRUSTED
: EST_NONE
;
2260 #ifdef USE_DEV_TOOLS
2261 if (systemConfig()->groundTestMode
&& STATE(AIRPLANE
)) {
2262 newEstHeading
= EST_TRUSTED
;
2265 if (newEstHeading
>= EST_USABLE
&& posControl
.flags
.estHeadingStatus
< EST_USABLE
&&
2266 (posControl
.rthState
.homeFlags
& (NAV_HOME_VALID_XY
| NAV_HOME_VALID_Z
)) &&
2267 (posControl
.rthState
.homeFlags
& NAV_HOME_VALID_HEADING
) == 0) {
2269 // Home was stored using the fake heading (assuming boot as 0deg). Calculate
2270 // the offset from the fake to the actual yaw and apply the same rotation
2271 // to the home point.
2272 int32_t fakeToRealYawOffset
= newHeading
- posControl
.actualState
.yaw
;
2273 posControl
.rthState
.homePosition
.heading
+= fakeToRealYawOffset
;
2274 posControl
.rthState
.homePosition
.heading
= wrap_36000(posControl
.rthState
.homePosition
.heading
);
2276 posControl
.rthState
.homeFlags
|= NAV_HOME_VALID_HEADING
;
2279 posControl
.actualState
.yaw
= newHeading
;
2280 posControl
.actualState
.cog
= newGroundCourse
;
2281 posControl
.flags
.estHeadingStatus
= newEstHeading
;
2283 /* Precompute sin/cos of yaw angle */
2284 posControl
.actualState
.sinYaw
= sin_approx(CENTIDEGREES_TO_RADIANS(newHeading
));
2285 posControl
.actualState
.cosYaw
= cos_approx(CENTIDEGREES_TO_RADIANS(newHeading
));
2288 /*-----------------------------------------------------------
2289 * Returns pointer to currently used position (ABS or AGL) depending on surface tracking status
2290 *-----------------------------------------------------------*/
2291 const navEstimatedPosVel_t
* navGetCurrentActualPositionAndVelocity(void)
2293 return posControl
.flags
.isTerrainFollowEnabled
? &posControl
.actualState
.agl
: &posControl
.actualState
.abs
;
2296 /*-----------------------------------------------------------
2297 * Calculates distance and bearing to destination point
2298 *-----------------------------------------------------------*/
2299 static uint32_t calculateDistanceFromDelta(float deltaX
, float deltaY
)
2301 return calc_length_pythagorean_2D(deltaX
, deltaY
);
2304 static int32_t calculateBearingFromDelta(float deltaX
, float deltaY
)
2306 return wrap_36000(RADIANS_TO_CENTIDEGREES(atan2_approx(deltaY
, deltaX
)));
2309 uint32_t calculateDistanceToDestination(const fpVector3_t
* destinationPos
)
2311 const navEstimatedPosVel_t
*posvel
= navGetCurrentActualPositionAndVelocity();
2312 const float deltaX
= destinationPos
->x
- posvel
->pos
.x
;
2313 const float deltaY
= destinationPos
->y
- posvel
->pos
.y
;
2315 return calculateDistanceFromDelta(deltaX
, deltaY
);
2318 int32_t calculateBearingToDestination(const fpVector3_t
* destinationPos
)
2320 const navEstimatedPosVel_t
*posvel
= navGetCurrentActualPositionAndVelocity();
2321 const float deltaX
= destinationPos
->x
- posvel
->pos
.x
;
2322 const float deltaY
= destinationPos
->y
- posvel
->pos
.y
;
2324 return calculateBearingFromDelta(deltaX
, deltaY
);
2327 int32_t calculateBearingBetweenLocalPositions(const fpVector3_t
* startPos
, const fpVector3_t
* endPos
)
2329 const float deltaX
= endPos
->x
- startPos
->x
;
2330 const float deltaY
= endPos
->y
- startPos
->y
;
2332 return calculateBearingFromDelta(deltaX
, deltaY
);
2335 bool navCalculatePathToDestination(navDestinationPath_t
*result
, const fpVector3_t
* destinationPos
) // NOT USED ANYWHERE
2337 if (posControl
.flags
.estPosStatus
== EST_NONE
||
2338 posControl
.flags
.estHeadingStatus
== EST_NONE
) {
2343 const navEstimatedPosVel_t
*posvel
= navGetCurrentActualPositionAndVelocity();
2344 const float deltaX
= destinationPos
->x
- posvel
->pos
.x
;
2345 const float deltaY
= destinationPos
->y
- posvel
->pos
.y
;
2347 result
->distance
= calculateDistanceFromDelta(deltaX
, deltaY
);
2348 result
->bearing
= calculateBearingFromDelta(deltaX
, deltaY
);
2352 static bool getLocalPosNextWaypoint(fpVector3_t
* nextWpPos
)
2354 // Only for WP Mode not Trackback. Ignore non geo waypoints except RTH and JUMP.
2355 if (navGetStateFlags(posControl
.navState
) & NAV_AUTO_WP
&& !isLastMissionWaypoint()) {
2356 navWaypointActions_e nextWpAction
= posControl
.waypointList
[posControl
.activeWaypointIndex
+ 1].action
;
2358 if (!(nextWpAction
== NAV_WP_ACTION_SET_POI
|| nextWpAction
== NAV_WP_ACTION_SET_HEAD
)) {
2359 uint8_t nextWpIndex
= posControl
.activeWaypointIndex
+ 1;
2360 if (nextWpAction
== NAV_WP_ACTION_JUMP
) {
2361 if (posControl
.waypointList
[posControl
.activeWaypointIndex
+ 1].p3
!= 0 ||
2362 posControl
.waypointList
[posControl
.activeWaypointIndex
+ 1].p2
== -1) {
2363 nextWpIndex
= posControl
.waypointList
[posControl
.activeWaypointIndex
+ 1].p1
+ posControl
.startWpIndex
;
2364 } else if (posControl
.activeWaypointIndex
+ 2 <= posControl
.startWpIndex
+ posControl
.waypointCount
- 1) {
2365 if (posControl
.waypointList
[posControl
.activeWaypointIndex
+ 2].action
!= NAV_WP_ACTION_JUMP
) {
2368 return false; // give up - too complicated
2372 mapWaypointToLocalPosition(nextWpPos
, &posControl
.waypointList
[nextWpIndex
], 0);
2377 return false; // no position available
2380 /*-----------------------------------------------------------
2381 * Check if waypoint is/was reached.
2382 * waypointBearing stores initial bearing to waypoint
2383 *-----------------------------------------------------------*/
2384 static bool isWaypointReached(const fpVector3_t
* waypointPos
, const int32_t * waypointBearing
)
2386 posControl
.wpDistance
= calculateDistanceToDestination(waypointPos
);
2388 // Airplane will do a circular loiter at hold waypoints and might never approach them closer than waypoint_radius
2389 // Check within 10% margin of circular loiter radius
2390 if (STATE(AIRPLANE
) && isNavHoldPositionActive() && posControl
.wpDistance
<= (navConfig()->fw
.loiter_radius
* 1.10f
)) {
2394 if (navGetStateFlags(posControl
.navState
) & NAV_AUTO_WP
|| posControl
.flags
.rthTrackbackActive
) {
2395 // If WP turn smoothing CUT option used WP is reached when start of turn is initiated
2396 if (navConfig()->fw
.wp_turn_smoothing
== WP_TURN_SMOOTHING_CUT
&& posControl
.flags
.wpTurnSmoothingActive
) {
2397 posControl
.flags
.wpTurnSmoothingActive
= false;
2400 // Check if waypoint was missed based on bearing to WP exceeding 100 degrees relative to waypoint Yaw
2401 // Same method for turn smoothing option but relative bearing set at 60 degrees
2402 uint16_t relativeBearing
= posControl
.flags
.wpTurnSmoothingActive
? 6000 : 10000;
2403 if (ABS(wrap_18000(calculateBearingToDestination(waypointPos
) - *waypointBearing
)) > relativeBearing
) {
2408 return posControl
.wpDistance
<= (navConfig()->general
.waypoint_radius
);
2411 bool isWaypointAltitudeReached(void)
2413 return ABS(navGetCurrentActualPositionAndVelocity()->pos
.z
- posControl
.activeWaypoint
.pos
.z
) < navConfig()->general
.waypoint_enforce_altitude
;
2416 static void updateHomePositionCompatibility(void)
2418 geoConvertLocalToGeodetic(&GPS_home
, &posControl
.gpsOrigin
, &posControl
.rthState
.homePosition
.pos
);
2419 GPS_distanceToHome
= posControl
.homeDistance
* 0.01f
;
2420 GPS_directionToHome
= posControl
.homeDirection
* 0.01f
;
2423 // Backdoor for RTH estimator
2424 float getFinalRTHAltitude(void)
2426 return posControl
.rthState
.rthFinalAltitude
;
2429 /*-----------------------------------------------------------
2430 * Update the RTH Altitudes
2431 *-----------------------------------------------------------*/
2432 static void updateDesiredRTHAltitude(void)
2434 if (ARMING_FLAG(ARMED
)) {
2435 if (!((navGetStateFlags(posControl
.navState
) & NAV_AUTO_RTH
)
2436 || ((navGetStateFlags(posControl
.navState
) & NAV_AUTO_WP
) && posControl
.waypointList
[posControl
.activeWaypointIndex
].action
== NAV_WP_ACTION_RTH
))) {
2437 switch (navConfig()->general
.flags
.rth_climb_first_stage_mode
) {
2438 case NAV_RTH_CLIMB_STAGE_AT_LEAST
:
2439 posControl
.rthState
.rthClimbStageAltitude
= posControl
.rthState
.homePosition
.pos
.z
+ navConfig()->general
.rth_climb_first_stage_altitude
;
2441 case NAV_RTH_CLIMB_STAGE_EXTRA
:
2442 posControl
.rthState
.rthClimbStageAltitude
= posControl
.actualState
.abs
.pos
.z
+ navConfig()->general
.rth_climb_first_stage_altitude
;
2446 switch (navConfig()->general
.flags
.rth_alt_control_mode
) {
2447 case NAV_RTH_NO_ALT
:
2448 posControl
.rthState
.rthInitialAltitude
= posControl
.actualState
.abs
.pos
.z
;
2449 posControl
.rthState
.rthFinalAltitude
= posControl
.rthState
.rthInitialAltitude
;
2452 case NAV_RTH_EXTRA_ALT
: // Maintain current altitude + predefined safety margin
2453 posControl
.rthState
.rthInitialAltitude
= posControl
.actualState
.abs
.pos
.z
+ navConfig()->general
.rth_altitude
;
2454 posControl
.rthState
.rthFinalAltitude
= posControl
.rthState
.rthInitialAltitude
;
2457 case NAV_RTH_MAX_ALT
:
2458 posControl
.rthState
.rthInitialAltitude
= MAX(posControl
.rthState
.rthInitialAltitude
, posControl
.actualState
.abs
.pos
.z
);
2459 if (navConfig()->general
.rth_altitude
> 0) {
2460 posControl
.rthState
.rthInitialAltitude
= MAX(posControl
.rthState
.rthInitialAltitude
, posControl
.rthState
.homePosition
.pos
.z
+ navConfig()->general
.rth_altitude
);
2462 posControl
.rthState
.rthFinalAltitude
= posControl
.rthState
.rthInitialAltitude
;
2465 case NAV_RTH_AT_LEAST_ALT
: // Climb to at least some predefined altitude above home
2466 posControl
.rthState
.rthInitialAltitude
= MAX(posControl
.rthState
.homePosition
.pos
.z
+ navConfig()->general
.rth_altitude
, posControl
.actualState
.abs
.pos
.z
);
2467 posControl
.rthState
.rthFinalAltitude
= posControl
.rthState
.rthInitialAltitude
;
2470 case NAV_RTH_CONST_ALT
: // Climb/descend to predefined altitude above home
2472 posControl
.rthState
.rthInitialAltitude
= posControl
.rthState
.homePosition
.pos
.z
+ navConfig()->general
.rth_altitude
;
2473 posControl
.rthState
.rthFinalAltitude
= posControl
.rthState
.rthInitialAltitude
;
2476 if ((navConfig()->general
.flags
.rth_use_linear_descent
) && (navConfig()->general
.rth_home_altitude
> 0) && (navConfig()->general
.rth_linear_descent_start_distance
== 0) ) {
2477 posControl
.rthState
.rthFinalAltitude
= posControl
.rthState
.homePosition
.pos
.z
+ navConfig()->general
.rth_home_altitude
;
2481 posControl
.rthState
.rthClimbStageAltitude
= posControl
.actualState
.abs
.pos
.z
;
2482 posControl
.rthState
.rthInitialAltitude
= posControl
.actualState
.abs
.pos
.z
;
2483 posControl
.rthState
.rthFinalAltitude
= posControl
.actualState
.abs
.pos
.z
;
2487 /*-----------------------------------------------------------
2488 * RTH sanity test logic
2489 *-----------------------------------------------------------*/
2490 void initializeRTHSanityChecker(void)
2492 const timeMs_t currentTimeMs
= millis();
2494 posControl
.rthSanityChecker
.lastCheckTime
= currentTimeMs
;
2495 posControl
.rthSanityChecker
.rthSanityOK
= true;
2496 posControl
.rthSanityChecker
.minimalDistanceToHome
= calculateDistanceToDestination(&posControl
.rthState
.homePosition
.pos
);
2499 bool validateRTHSanityChecker(void)
2501 const timeMs_t currentTimeMs
= millis();
2503 // Ability to disable sanity checker
2504 if (navConfig()->general
.rth_abort_threshold
== 0) {
2508 // Check at 10Hz rate
2509 if ((currentTimeMs
- posControl
.rthSanityChecker
.lastCheckTime
) > 100) {
2510 const float currentDistanceToHome
= calculateDistanceToDestination(&posControl
.rthState
.homePosition
.pos
);
2511 posControl
.rthSanityChecker
.lastCheckTime
= currentTimeMs
;
2513 if (currentDistanceToHome
< posControl
.rthSanityChecker
.minimalDistanceToHome
) {
2514 posControl
.rthSanityChecker
.minimalDistanceToHome
= currentDistanceToHome
;
2516 // If while doing RTH we got even farther away from home - RTH is doing something crazy
2517 posControl
.rthSanityChecker
.rthSanityOK
= (currentDistanceToHome
- posControl
.rthSanityChecker
.minimalDistanceToHome
) < navConfig()->general
.rth_abort_threshold
;
2521 return posControl
.rthSanityChecker
.rthSanityOK
;
2524 /*-----------------------------------------------------------
2525 * Reset home position to current position
2526 *-----------------------------------------------------------*/
2527 void setHomePosition(const fpVector3_t
* pos
, int32_t heading
, navSetWaypointFlags_t useMask
, navigationHomeFlags_t homeFlags
)
2530 if ((useMask
& NAV_POS_UPDATE_XY
) != 0) {
2531 posControl
.rthState
.homePosition
.pos
.x
= pos
->x
;
2532 posControl
.rthState
.homePosition
.pos
.y
= pos
->y
;
2533 if (homeFlags
& NAV_HOME_VALID_XY
) {
2534 posControl
.rthState
.homeFlags
|= NAV_HOME_VALID_XY
;
2536 posControl
.rthState
.homeFlags
&= ~NAV_HOME_VALID_XY
;
2541 if ((useMask
& NAV_POS_UPDATE_Z
) != 0) {
2542 posControl
.rthState
.homePosition
.pos
.z
= pos
->z
;
2543 if (homeFlags
& NAV_HOME_VALID_Z
) {
2544 posControl
.rthState
.homeFlags
|= NAV_HOME_VALID_Z
;
2546 posControl
.rthState
.homeFlags
&= ~NAV_HOME_VALID_Z
;
2551 if ((useMask
& NAV_POS_UPDATE_HEADING
) != 0) {
2553 posControl
.rthState
.homePosition
.heading
= heading
;
2554 if (homeFlags
& NAV_HOME_VALID_HEADING
) {
2555 posControl
.rthState
.homeFlags
|= NAV_HOME_VALID_HEADING
;
2557 posControl
.rthState
.homeFlags
&= ~NAV_HOME_VALID_HEADING
;
2561 posControl
.homeDistance
= 0;
2562 posControl
.homeDirection
= 0;
2564 // Update target RTH altitude as a waypoint above home
2565 updateDesiredRTHAltitude();
2567 // Reset RTH sanity checker for new home position if RTH active
2568 if (FLIGHT_MODE(NAV_RTH_MODE
)) {
2569 initializeRTHSanityChecker();
2572 updateHomePositionCompatibility();
2573 ENABLE_STATE(GPS_FIX_HOME
);
2576 static navigationHomeFlags_t
navigationActualStateHomeValidity(void)
2578 navigationHomeFlags_t flags
= 0;
2580 if (posControl
.flags
.estPosStatus
>= EST_USABLE
) {
2581 flags
|= NAV_HOME_VALID_XY
| NAV_HOME_VALID_Z
;
2584 if (posControl
.flags
.estHeadingStatus
>= EST_USABLE
) {
2585 flags
|= NAV_HOME_VALID_HEADING
;
2591 #if defined(USE_SAFE_HOME)
2592 void checkSafeHomeState(bool shouldBeEnabled
)
2594 bool safehomeNotApplicable
= navConfig()->general
.flags
.safehome_usage_mode
== SAFEHOME_USAGE_OFF
|| posControl
.flags
.rthTrackbackActive
||
2595 (!posControl
.safehomeState
.isApplied
&& posControl
.homeDistance
< navConfig()->general
.min_rth_distance
);
2596 #ifdef USE_MULTI_FUNCTIONS
2597 safehomeNotApplicable
= safehomeNotApplicable
|| (MULTI_FUNC_FLAG(MF_SUSPEND_SAFEHOMES
) && !posControl
.flags
.forcedRTHActivated
);
2600 if (safehomeNotApplicable
) {
2601 shouldBeEnabled
= false;
2602 } else if (navConfig()->general
.flags
.safehome_usage_mode
== SAFEHOME_USAGE_RTH_FS
&& shouldBeEnabled
) {
2603 // if safehomes are only used with failsafe and we're trying to enable safehome
2604 // then enable the safehome only with failsafe
2605 shouldBeEnabled
= posControl
.flags
.forcedRTHActivated
;
2607 // no safe homes found when arming or safehome feature in the correct state, then we don't need to do anything
2608 if (posControl
.safehomeState
.distance
== 0 || posControl
.safehomeState
.isApplied
== shouldBeEnabled
) {
2611 if (shouldBeEnabled
) {
2612 // set home to safehome
2613 setHomePosition(&posControl
.safehomeState
.nearestSafeHome
, 0, NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_Z
| NAV_POS_UPDATE_HEADING
, navigationActualStateHomeValidity());
2614 posControl
.safehomeState
.isApplied
= true;
2616 // set home to original arming point
2617 setHomePosition(&posControl
.rthState
.originalHomePosition
, 0, NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_Z
| NAV_POS_UPDATE_HEADING
, navigationActualStateHomeValidity());
2618 posControl
.safehomeState
.isApplied
= false;
2620 // if we've changed the home position, update the distance and direction
2621 updateHomePosition();
2624 /***********************************************************
2625 * See if there are any safehomes near where we are arming.
2626 * If so, save the nearest one in case we need it later for RTH.
2627 **********************************************************/
2628 bool findNearestSafeHome(void)
2630 posControl
.safehomeState
.index
= -1;
2631 uint32_t nearest_safehome_distance
= navConfig()->general
.safehome_max_distance
+ 1;
2632 uint32_t distance_to_current
;
2633 fpVector3_t currentSafeHome
;
2634 gpsLocation_t shLLH
;
2636 for (uint8_t i
= 0; i
< MAX_SAFE_HOMES
; i
++) {
2637 if (!safeHomeConfig(i
)->enabled
)
2640 shLLH
.lat
= safeHomeConfig(i
)->lat
;
2641 shLLH
.lon
= safeHomeConfig(i
)->lon
;
2642 geoConvertGeodeticToLocal(¤tSafeHome
, &posControl
.gpsOrigin
, &shLLH
, GEO_ALT_RELATIVE
);
2643 distance_to_current
= calculateDistanceToDestination(¤tSafeHome
);
2644 if (distance_to_current
< nearest_safehome_distance
) {
2645 // this safehome is the nearest so far - keep track of it.
2646 posControl
.safehomeState
.index
= i
;
2647 nearest_safehome_distance
= distance_to_current
;
2648 posControl
.safehomeState
.nearestSafeHome
= currentSafeHome
;
2651 if (posControl
.safehomeState
.index
>= 0) {
2652 posControl
.safehomeState
.distance
= nearest_safehome_distance
;
2654 posControl
.safehomeState
.distance
= 0;
2656 return posControl
.safehomeState
.distance
> 0;
2660 /*-----------------------------------------------------------
2661 * Update home position, calculate distance and bearing to home
2662 *-----------------------------------------------------------*/
2663 void updateHomePosition(void)
2665 // Disarmed and have a valid position, constantly update home before first arm (depending on setting)
2666 // Update immediately after arming thereafter if reset on each arm (required to avoid home reset after emerg in flight rearm)
2667 static bool setHome
= false;
2668 navSetWaypointFlags_t homeUpdateFlags
= NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_Z
| NAV_POS_UPDATE_HEADING
;
2670 if (!ARMING_FLAG(ARMED
)) {
2671 if (posControl
.flags
.estPosStatus
>= EST_USABLE
) {
2672 const navigationHomeFlags_t validHomeFlags
= NAV_HOME_VALID_XY
| NAV_HOME_VALID_Z
;
2673 setHome
= (posControl
.rthState
.homeFlags
& validHomeFlags
) != validHomeFlags
;
2674 switch ((nav_reset_type_e
)positionEstimationConfig()->reset_home_type
) {
2675 case NAV_RESET_NEVER
:
2677 case NAV_RESET_ON_FIRST_ARM
:
2678 setHome
|= !ARMING_FLAG(WAS_EVER_ARMED
);
2680 case NAV_RESET_ON_EACH_ARM
:
2687 static bool isHomeResetAllowed
= false;
2688 // If pilot so desires he may reset home position to current position
2689 if (IS_RC_MODE_ACTIVE(BOXHOMERESET
)) {
2690 if (isHomeResetAllowed
&& !FLIGHT_MODE(FAILSAFE_MODE
) && !FLIGHT_MODE(NAV_RTH_MODE
) && !FLIGHT_MODE(NAV_WP_MODE
) && (posControl
.flags
.estPosStatus
>= EST_USABLE
)) {
2691 homeUpdateFlags
= 0;
2692 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
);
2694 isHomeResetAllowed
= false;
2698 isHomeResetAllowed
= true;
2701 // Update distance and direction to home if armed (home is not updated when armed)
2702 if (STATE(GPS_FIX_HOME
)) {
2703 fpVector3_t
* tmpHomePos
= rthGetHomeTargetPosition(RTH_HOME_FINAL_LAND
);
2704 posControl
.homeDistance
= calculateDistanceToDestination(tmpHomePos
);
2705 posControl
.homeDirection
= calculateBearingToDestination(tmpHomePos
);
2706 updateHomePositionCompatibility();
2709 setHome
&= !STATE(IN_FLIGHT_EMERG_REARM
); // prevent reset following emerg in flight rearm
2712 if (setHome
&& (!ARMING_FLAG(WAS_EVER_ARMED
) || ARMING_FLAG(ARMED
))) {
2713 #if defined(USE_SAFE_HOME)
2714 findNearestSafeHome();
2716 setHomePosition(&posControl
.actualState
.abs
.pos
, posControl
.actualState
.yaw
, homeUpdateFlags
, navigationActualStateHomeValidity());
2718 if (ARMING_FLAG(ARMED
) && positionEstimationConfig()->reset_altitude_type
== NAV_RESET_ON_EACH_ARM
) {
2719 posControl
.rthState
.homePosition
.pos
.z
= 0; // force to 0 if reference altitude also reset every arm
2721 // save the current location in case it is replaced by a safehome or HOME_RESET
2722 posControl
.rthState
.originalHomePosition
= posControl
.rthState
.homePosition
.pos
;
2727 /* -----------------------------------------------------------
2728 * Override RTH preset altitude and Climb First option
2729 * using Pitch/Roll stick held for > 1 seconds
2730 * Climb First override limited to Fixed Wing only
2731 * Roll also cancels RTH trackback on Fixed Wing and Multirotor
2732 *-----------------------------------------------------------*/
2733 static bool rthAltControlStickOverrideCheck(unsigned axis
)
2735 if (!navConfig()->general
.flags
.rth_alt_control_override
|| posControl
.flags
.forcedRTHActivated
||
2736 (axis
== ROLL
&& STATE(MULTIROTOR
) && !posControl
.flags
.rthTrackbackActive
)) {
2739 static timeMs_t rthOverrideStickHoldStartTime
[2];
2741 if (rxGetChannelValue(axis
) > rxConfig()->maxcheck
) {
2742 timeDelta_t holdTime
= millis() - rthOverrideStickHoldStartTime
[axis
];
2744 if (!rthOverrideStickHoldStartTime
[axis
]) {
2745 rthOverrideStickHoldStartTime
[axis
] = millis();
2746 } else if (ABS(1500 - holdTime
) < 500) { // 1s delay to activate, activation duration limited to 1 sec
2747 if (axis
== PITCH
) { // PITCH down to override preset altitude reset to current altitude
2748 posControl
.rthState
.rthInitialAltitude
= posControl
.actualState
.abs
.pos
.z
;
2749 posControl
.rthState
.rthFinalAltitude
= posControl
.rthState
.rthInitialAltitude
;
2751 } else if (axis
== ROLL
) { // ROLL right to override climb first
2756 rthOverrideStickHoldStartTime
[axis
] = 0;
2762 /* ---------------------------------------------------
2763 * If climb stage is being used, see if it is time to
2764 * transiton in to turn.
2765 * Limited to fixed wing only.
2766 * --------------------------------------------------- */
2767 bool rthClimbStageActiveAndComplete(void) {
2768 if ((STATE(FIXED_WING_LEGACY
) || STATE(AIRPLANE
)) && (navConfig()->general
.rth_climb_first_stage_altitude
> 0)) {
2769 if (posControl
.actualState
.abs
.pos
.z
>= posControl
.rthState
.rthClimbStageAltitude
) {
2777 /* --------------------------------------------------------------------------------
2778 * == RTH Trackback ==
2779 * Saves track during flight which is used during RTH to back track
2780 * along arrival route rather than immediately heading directly toward home.
2781 * Max desired trackback distance set by user or limited by number of available points.
2782 * Reverts to normal RTH heading direct to home when end of track reached.
2783 * Trackpoints logged with precedence for course/altitude changes. Distance based changes
2784 * only logged if no course/altitude changes logged over an extended distance.
2785 * Tracking suspended during fixed wing loiter (PosHold and WP Mode timed hold).
2786 * --------------------------------------------------------------------------------- */
2787 static void updateRthTrackback(bool forceSaveTrackPoint
)
2789 static bool suspendTracking
= false;
2790 bool fwLoiterIsActive
= STATE(AIRPLANE
) && (NAV_Status
.state
== MW_NAV_STATE_HOLD_TIMED
|| FLIGHT_MODE(NAV_POSHOLD_MODE
));
2791 if (!fwLoiterIsActive
&& suspendTracking
) {
2792 suspendTracking
= false;
2795 if (navConfig()->general
.flags
.rth_trackback_mode
== RTH_TRACKBACK_OFF
|| FLIGHT_MODE(NAV_RTH_MODE
) || !ARMING_FLAG(ARMED
) || suspendTracking
) {
2799 // Record trackback points based on significant change in course/altitude until
2800 // points limit reached. Overwrite older points from then on.
2801 if (posControl
.flags
.estPosStatus
>= EST_USABLE
&& posControl
.flags
.estAltStatus
>= EST_USABLE
) {
2802 static int32_t previousTBTripDist
; // cm
2803 static int16_t previousTBCourse
; // degrees
2804 static int16_t previousTBAltitude
; // meters
2805 static uint8_t distanceCounter
= 0;
2806 bool saveTrackpoint
= forceSaveTrackPoint
;
2807 bool GPSCourseIsValid
= isGPSHeadingValid();
2809 // start recording when some distance from home, 50m seems reasonable.
2810 if (posControl
.activeRthTBPointIndex
< 0) {
2811 saveTrackpoint
= posControl
.homeDistance
> METERS_TO_CENTIMETERS(50);
2813 previousTBCourse
= CENTIDEGREES_TO_DEGREES(posControl
.actualState
.cog
);
2814 previousTBTripDist
= posControl
.totalTripDistance
;
2816 // Minimum distance increment between course change track points when GPS course valid - set to 10m
2817 const bool distanceIncrement
= posControl
.totalTripDistance
- previousTBTripDist
> METERS_TO_CENTIMETERS(10);
2820 if (ABS(previousTBAltitude
- CENTIMETERS_TO_METERS(posControl
.actualState
.abs
.pos
.z
)) > 10) { // meters
2821 saveTrackpoint
= true;
2822 } else if (distanceIncrement
&& GPSCourseIsValid
) {
2823 // Course change - set to 45 degrees
2824 if (ABS(wrap_18000(DEGREES_TO_CENTIDEGREES(DECIDEGREES_TO_DEGREES(gpsSol
.groundCourse
) - previousTBCourse
))) > DEGREES_TO_CENTIDEGREES(45)) {
2825 saveTrackpoint
= true;
2826 } else if (distanceCounter
>= 9) {
2827 // Distance based trackpoint logged if at least 10 distance increments occur without altitude or course change
2828 // and deviation from projected course path > 20m
2829 float distToPrevPoint
= calculateDistanceToDestination(&posControl
.rthTBPointsList
[posControl
.activeRthTBPointIndex
]);
2831 fpVector3_t virtualCoursePoint
;
2832 virtualCoursePoint
.x
= posControl
.rthTBPointsList
[posControl
.activeRthTBPointIndex
].x
+ distToPrevPoint
* cos_approx(DEGREES_TO_RADIANS(previousTBCourse
));
2833 virtualCoursePoint
.y
= posControl
.rthTBPointsList
[posControl
.activeRthTBPointIndex
].y
+ distToPrevPoint
* sin_approx(DEGREES_TO_RADIANS(previousTBCourse
));
2835 saveTrackpoint
= calculateDistanceToDestination(&virtualCoursePoint
) > METERS_TO_CENTIMETERS(20);
2838 previousTBTripDist
= posControl
.totalTripDistance
;
2839 } else if (!GPSCourseIsValid
) {
2840 // if no reliable course revert to basic distance logging based on direct distance from last point - set to 20m
2841 saveTrackpoint
= calculateDistanceToDestination(&posControl
.rthTBPointsList
[posControl
.activeRthTBPointIndex
]) > METERS_TO_CENTIMETERS(20);
2842 previousTBTripDist
= posControl
.totalTripDistance
;
2845 // Suspend tracking during loiter on fixed wing. Save trackpoint at start of loiter.
2846 if (distanceCounter
&& fwLoiterIsActive
) {
2847 saveTrackpoint
= suspendTracking
= true;
2851 // when trackpoint store full, overwrite from start of store using 'rthTBWrapAroundCounter' to track overwrite position
2852 if (saveTrackpoint
) {
2853 if (posControl
.activeRthTBPointIndex
== (NAV_RTH_TRACKBACK_POINTS
- 1)) { // wraparound to start
2854 posControl
.rthTBWrapAroundCounter
= posControl
.activeRthTBPointIndex
= 0;
2856 posControl
.activeRthTBPointIndex
++;
2857 if (posControl
.rthTBWrapAroundCounter
> -1) { // track wraparound overwrite position after store first filled
2858 posControl
.rthTBWrapAroundCounter
= posControl
.activeRthTBPointIndex
;
2861 posControl
.rthTBPointsList
[posControl
.activeRthTBPointIndex
] = posControl
.actualState
.abs
.pos
;
2863 posControl
.rthTBLastSavedIndex
= posControl
.activeRthTBPointIndex
;
2864 previousTBAltitude
= CENTIMETERS_TO_METERS(posControl
.actualState
.abs
.pos
.z
);
2865 previousTBCourse
= GPSCourseIsValid
? DECIDEGREES_TO_DEGREES(gpsSol
.groundCourse
) : previousTBCourse
;
2866 distanceCounter
= 0;
2871 static fpVector3_t
* rthGetTrackbackPos(void)
2873 // ensure trackback altitude never lower than altitude of start point
2874 if (posControl
.rthTBPointsList
[posControl
.activeRthTBPointIndex
].z
< posControl
.rthTBPointsList
[posControl
.rthTBLastSavedIndex
].z
) {
2875 posControl
.rthTBPointsList
[posControl
.activeRthTBPointIndex
].z
= posControl
.rthTBPointsList
[posControl
.rthTBLastSavedIndex
].z
;
2878 return &posControl
.rthTBPointsList
[posControl
.activeRthTBPointIndex
];
2881 /*-----------------------------------------------------------
2882 * Update flight statistics
2883 *-----------------------------------------------------------*/
2884 static void updateNavigationFlightStatistics(void)
2886 static timeMs_t previousTimeMs
= 0;
2887 const timeMs_t currentTimeMs
= millis();
2888 const timeDelta_t timeDeltaMs
= currentTimeMs
- previousTimeMs
;
2889 previousTimeMs
= currentTimeMs
;
2891 if (ARMING_FLAG(ARMED
)) {
2892 posControl
.totalTripDistance
+= posControl
.actualState
.velXY
* MS2S(timeDeltaMs
);
2897 * Total travel distance in cm
2899 uint32_t getTotalTravelDistance(void)
2901 return lrintf(posControl
.totalTripDistance
);
2904 /*-----------------------------------------------------------
2905 * Calculate platform-specific hold position (account for deceleration)
2906 *-----------------------------------------------------------*/
2907 void calculateInitialHoldPosition(fpVector3_t
* pos
)
2909 if (STATE(FIXED_WING_LEGACY
)) { // FIXED_WING_LEGACY
2910 calculateFixedWingInitialHoldPosition(pos
);
2913 calculateMulticopterInitialHoldPosition(pos
);
2917 /*-----------------------------------------------------------
2918 * Set active XYZ-target and desired heading
2919 *-----------------------------------------------------------*/
2920 void setDesiredPosition(const fpVector3_t
* pos
, int32_t yaw
, navSetWaypointFlags_t useMask
)
2922 // XY-position update is allowed only when not braking in NAV_CRUISE_BRAKING
2923 if ((useMask
& NAV_POS_UPDATE_XY
) != 0 && !STATE(NAV_CRUISE_BRAKING
)) {
2924 posControl
.desiredState
.pos
.x
= pos
->x
;
2925 posControl
.desiredState
.pos
.y
= pos
->y
;
2929 if ((useMask
& NAV_POS_UPDATE_Z
) != 0) {
2930 updateClimbRateToAltitudeController(0, 0, ROC_TO_ALT_RESET
); // Reset RoC/RoD -> altitude controller
2931 posControl
.desiredState
.pos
.z
= pos
->z
;
2935 if ((useMask
& NAV_POS_UPDATE_HEADING
) != 0) {
2937 posControl
.desiredState
.yaw
= yaw
;
2939 else if ((useMask
& NAV_POS_UPDATE_BEARING
) != 0) {
2940 posControl
.desiredState
.yaw
= calculateBearingToDestination(pos
);
2942 else if ((useMask
& NAV_POS_UPDATE_BEARING_TAIL_FIRST
) != 0) {
2943 posControl
.desiredState
.yaw
= wrap_36000(calculateBearingToDestination(pos
) - 18000);
2947 void calculateFarAwayTarget(fpVector3_t
* farAwayPos
, int32_t bearing
, int32_t distance
)
2949 farAwayPos
->x
= navGetCurrentActualPositionAndVelocity()->pos
.x
+ distance
* cos_approx(CENTIDEGREES_TO_RADIANS(bearing
));
2950 farAwayPos
->y
= navGetCurrentActualPositionAndVelocity()->pos
.y
+ distance
* sin_approx(CENTIDEGREES_TO_RADIANS(bearing
));
2951 farAwayPos
->z
= navGetCurrentActualPositionAndVelocity()->pos
.z
;
2954 /*-----------------------------------------------------------
2956 *-----------------------------------------------------------*/
2957 void updateLandingStatus(timeMs_t currentTimeMs
)
2959 if (STATE(AIRPLANE
) && !navConfig()->general
.flags
.disarm_on_landing
) {
2960 return; // no point using this with a fixed wing if not set to disarm
2963 static timeMs_t lastUpdateTimeMs
= 0;
2964 if ((currentTimeMs
- lastUpdateTimeMs
) <= HZ2MS(100)) { // limit update to 100Hz
2967 lastUpdateTimeMs
= currentTimeMs
;
2969 DEBUG_SET(DEBUG_LANDING
, 0, landingDetectorIsActive
);
2970 DEBUG_SET(DEBUG_LANDING
, 1, STATE(LANDING_DETECTED
));
2972 if (!ARMING_FLAG(ARMED
)) {
2973 if (STATE(LANDING_DETECTED
)) {
2974 landingDetectorIsActive
= false;
2976 resetLandingDetector();
2978 if (!IS_RC_MODE_ACTIVE(BOXARM
)) {
2979 DISABLE_ARMING_FLAG(ARMING_DISABLED_LANDING_DETECTED
);
2984 if (!landingDetectorIsActive
) {
2985 if (isFlightDetected()) {
2986 landingDetectorIsActive
= true;
2987 resetLandingDetector();
2989 } else if (STATE(LANDING_DETECTED
)) {
2990 pidResetErrorAccumulators();
2991 if (navConfig()->general
.flags
.disarm_on_landing
&& !FLIGHT_MODE(FAILSAFE_MODE
)) {
2992 ENABLE_ARMING_FLAG(ARMING_DISABLED_LANDING_DETECTED
);
2993 disarm(DISARM_LANDING
);
2994 } else if (!navigationInAutomaticThrottleMode()) {
2995 // for multirotor only - reactivate landing detector without disarm when throttle raised toward hover throttle
2996 landingDetectorIsActive
= rxGetChannelValue(THROTTLE
) < (0.5 * (currentBatteryProfile
->nav
.mc
.hover_throttle
+ getThrottleIdleValue()));
2998 } else if (isLandingDetected()) {
2999 ENABLE_STATE(LANDING_DETECTED
);
3003 bool isLandingDetected(void)
3005 return STATE(AIRPLANE
) ? isFixedWingLandingDetected() : isMulticopterLandingDetected();
3008 void resetLandingDetector(void)
3010 DISABLE_STATE(LANDING_DETECTED
);
3011 posControl
.flags
.resetLandingDetector
= true;
3014 void resetLandingDetectorActiveState(void)
3016 landingDetectorIsActive
= false;
3019 bool isFlightDetected(void)
3021 return STATE(AIRPLANE
) ? isFixedWingFlying() : isMulticopterFlying();
3024 bool isProbablyStillFlying(void)
3026 bool inFlightSanityCheck
;
3027 if (STATE(MULTIROTOR
)) {
3028 inFlightSanityCheck
= posControl
.actualState
.velXY
> MC_LAND_CHECK_VEL_XY_MOVING
|| averageAbsGyroRates() > 4.0f
;
3030 inFlightSanityCheck
= isGPSHeadingValid();
3033 return landingDetectorIsActive
&& inFlightSanityCheck
;
3036 /*-----------------------------------------------------------
3037 * Z-position controller
3038 *-----------------------------------------------------------*/
3039 void updateClimbRateToAltitudeController(float desiredClimbRate
, float targetAltitude
, climbRateToAltitudeControllerMode_e mode
)
3041 #define MIN_TARGET_CLIMB_RATE 100.0f // cm/s
3043 static timeUs_t lastUpdateTimeUs
;
3044 timeUs_t currentTimeUs
= micros();
3046 // Terrain following uses different altitude measurement
3047 const float altitudeToUse
= navGetCurrentActualPositionAndVelocity()->pos
.z
;
3049 if (mode
!= ROC_TO_ALT_RESET
&& desiredClimbRate
) {
3050 /* ROC_TO_ALT_CONSTANT - constant climb rate
3051 * ROC_TO_ALT_TARGET - constant climb rate until close to target altitude reducing to min rate when altitude reached
3052 * Rate reduction starts at distance from target altitude of 5 x climb rate for FW, 1 x climb rate for MC */
3054 if (mode
== ROC_TO_ALT_TARGET
&& fabsf(desiredClimbRate
) > MIN_TARGET_CLIMB_RATE
) {
3055 const int8_t direction
= desiredClimbRate
> 0 ? 1 : -1;
3056 const float absClimbRate
= fabsf(desiredClimbRate
);
3057 const uint16_t maxRateCutoffAlt
= STATE(AIRPLANE
) ? absClimbRate
* 5 : absClimbRate
;
3058 const float verticalVelScaled
= scaleRangef(navGetCurrentActualPositionAndVelocity()->pos
.z
- targetAltitude
,
3059 0.0f
, -maxRateCutoffAlt
* direction
, MIN_TARGET_CLIMB_RATE
, absClimbRate
);
3061 desiredClimbRate
= direction
* constrainf(verticalVelScaled
, MIN_TARGET_CLIMB_RATE
, absClimbRate
);
3065 * If max altitude is set, reset climb rate if altitude is reached and climb rate is > 0
3066 * In other words, when altitude is reached, allow it only to shrink
3068 if (navConfig()->general
.max_altitude
> 0 && altitudeToUse
>= navConfig()->general
.max_altitude
&& desiredClimbRate
> 0) {
3069 desiredClimbRate
= 0;
3072 if (STATE(FIXED_WING_LEGACY
)) {
3073 // Fixed wing climb rate controller is open-loop. We simply move the known altitude target
3074 float timeDelta
= US2S(currentTimeUs
- lastUpdateTimeUs
);
3075 static bool targetHoldActive
= false;
3077 if (timeDelta
<= HZ2S(MIN_POSITION_UPDATE_RATE_HZ
) && desiredClimbRate
) {
3078 // Update target altitude only if actual altitude moving in same direction and lagging by < 5 m, otherwise hold target
3079 if (navGetCurrentActualPositionAndVelocity()->vel
.z
* desiredClimbRate
>= 0 && fabsf(posControl
.desiredState
.pos
.z
- altitudeToUse
) < 500) {
3080 posControl
.desiredState
.pos
.z
+= desiredClimbRate
* timeDelta
;
3081 targetHoldActive
= false;
3082 } else if (!targetHoldActive
) { // Reset and hold target to actual + climb rate boost until actual catches up
3083 posControl
.desiredState
.pos
.z
= altitudeToUse
+ desiredClimbRate
;
3084 targetHoldActive
= true;
3087 targetHoldActive
= false;
3091 // Multicopter climb-rate control is closed-loop, it's possible to directly calculate desired altitude setpoint to yield the required RoC/RoD
3092 posControl
.desiredState
.pos
.z
= altitudeToUse
+ (desiredClimbRate
/ posControl
.pids
.pos
[Z
].param
.kP
);
3094 } else { // ROC_TO_ALT_RESET or zero desired climbrate
3095 posControl
.desiredState
.pos
.z
= altitudeToUse
;
3098 lastUpdateTimeUs
= currentTimeUs
;
3101 static void resetAltitudeController(bool useTerrainFollowing
)
3103 // Set terrain following flag
3104 posControl
.flags
.isTerrainFollowEnabled
= useTerrainFollowing
;
3106 if (STATE(FIXED_WING_LEGACY
)) {
3107 resetFixedWingAltitudeController();
3110 resetMulticopterAltitudeController();
3114 static void setupAltitudeController(void)
3116 if (STATE(FIXED_WING_LEGACY
)) {
3117 setupFixedWingAltitudeController();
3120 setupMulticopterAltitudeController();
3124 static bool adjustAltitudeFromRCInput(void)
3126 if (STATE(FIXED_WING_LEGACY
)) {
3127 return adjustFixedWingAltitudeFromRCInput();
3130 return adjustMulticopterAltitudeFromRCInput();
3134 /*-----------------------------------------------------------
3135 * Jump Counter support functions
3136 *-----------------------------------------------------------*/
3137 static void setupJumpCounters(void)
3139 for (uint8_t wp
= posControl
.startWpIndex
; wp
< posControl
.waypointCount
+ posControl
.startWpIndex
; wp
++) {
3140 if (posControl
.waypointList
[wp
].action
== NAV_WP_ACTION_JUMP
){
3141 posControl
.waypointList
[wp
].p3
= posControl
.waypointList
[wp
].p2
;
3146 static void resetJumpCounter(void)
3148 // reset the volatile counter from the set / static value
3149 posControl
.waypointList
[posControl
.activeWaypointIndex
].p3
= posControl
.waypointList
[posControl
.activeWaypointIndex
].p2
;
3152 static void clearJumpCounters(void)
3154 for (uint8_t wp
= posControl
.startWpIndex
; wp
< posControl
.waypointCount
+ posControl
.startWpIndex
; wp
++) {
3155 if (posControl
.waypointList
[wp
].action
== NAV_WP_ACTION_JUMP
) {
3156 posControl
.waypointList
[wp
].p3
= 0;
3163 /*-----------------------------------------------------------
3164 * Heading controller (pass-through to MAG mode)
3165 *-----------------------------------------------------------*/
3166 static void resetHeadingController(void)
3168 if (STATE(FIXED_WING_LEGACY
)) {
3169 resetFixedWingHeadingController();
3172 resetMulticopterHeadingController();
3176 static bool adjustHeadingFromRCInput(void)
3178 if (STATE(FIXED_WING_LEGACY
)) {
3179 return adjustFixedWingHeadingFromRCInput();
3182 return adjustMulticopterHeadingFromRCInput();
3186 /*-----------------------------------------------------------
3187 * XY Position controller
3188 *-----------------------------------------------------------*/
3189 static void resetPositionController(void)
3191 if (STATE(FIXED_WING_LEGACY
)) {
3192 resetFixedWingPositionController();
3195 resetMulticopterPositionController();
3196 resetMulticopterBrakingMode();
3200 static bool adjustPositionFromRCInput(void)
3204 if (STATE(FIXED_WING_LEGACY
)) {
3205 retValue
= adjustFixedWingPositionFromRCInput();
3209 const int16_t rcPitchAdjustment
= applyDeadbandRescaled(rcCommand
[PITCH
], rcControlsConfig()->pos_hold_deadband
, -500, 500);
3210 const int16_t rcRollAdjustment
= applyDeadbandRescaled(rcCommand
[ROLL
], rcControlsConfig()->pos_hold_deadband
, -500, 500);
3212 retValue
= adjustMulticopterPositionFromRCInput(rcPitchAdjustment
, rcRollAdjustment
);
3218 /*-----------------------------------------------------------
3220 *-----------------------------------------------------------*/
3221 void resetGCSFlags(void)
3223 posControl
.flags
.isGCSAssistedNavigationReset
= false;
3224 posControl
.flags
.isGCSAssistedNavigationEnabled
= false;
3227 void getWaypoint(uint8_t wpNumber
, navWaypoint_t
* wpData
)
3229 /* Default waypoint to send */
3230 wpData
->action
= NAV_WP_ACTION_RTH
;
3237 wpData
->flag
= NAV_WP_FLAG_LAST
;
3239 // WP #0 - special waypoint - HOME
3240 if (wpNumber
== 0) {
3241 if (STATE(GPS_FIX_HOME
)) {
3242 wpData
->lat
= GPS_home
.lat
;
3243 wpData
->lon
= GPS_home
.lon
;
3244 wpData
->alt
= GPS_home
.alt
;
3247 // WP #255 - special waypoint - directly get actualPosition
3248 else if (wpNumber
== 255) {
3249 gpsLocation_t wpLLH
;
3251 geoConvertLocalToGeodetic(&wpLLH
, &posControl
.gpsOrigin
, &navGetCurrentActualPositionAndVelocity()->pos
);
3253 wpData
->lat
= wpLLH
.lat
;
3254 wpData
->lon
= wpLLH
.lon
;
3255 wpData
->alt
= wpLLH
.alt
;
3257 // WP #254 - special waypoint - get desiredPosition that was set by ground control station if in 3D-guided mode
3258 else if (wpNumber
== 254) {
3259 navigationFSMStateFlags_t navStateFlags
= navGetStateFlags(posControl
.navState
);
3261 if ((posControl
.gpsOrigin
.valid
) && (navStateFlags
& NAV_CTL_ALT
) && (navStateFlags
& NAV_CTL_POS
)) {
3262 gpsLocation_t wpLLH
;
3264 geoConvertLocalToGeodetic(&wpLLH
, &posControl
.gpsOrigin
, &posControl
.desiredState
.pos
);
3266 wpData
->lat
= wpLLH
.lat
;
3267 wpData
->lon
= wpLLH
.lon
;
3268 wpData
->alt
= wpLLH
.alt
;
3271 // WP #1 - #60 - common waypoints - pre-programmed mission
3272 else if ((wpNumber
>= 1) && (wpNumber
<= NAV_MAX_WAYPOINTS
)) {
3273 if (wpNumber
<= getWaypointCount()) {
3274 *wpData
= posControl
.waypointList
[wpNumber
- 1 + (ARMING_FLAG(ARMED
) ? posControl
.startWpIndex
: 0)];
3275 if(wpData
->action
== NAV_WP_ACTION_JUMP
) {
3276 wpData
->p1
+= 1; // make WP # (vice index)
3282 void setWaypoint(uint8_t wpNumber
, const navWaypoint_t
* wpData
)
3284 gpsLocation_t wpLLH
;
3285 navWaypointPosition_t wpPos
;
3287 // Pre-fill structure to convert to local coordinates
3288 wpLLH
.lat
= wpData
->lat
;
3289 wpLLH
.lon
= wpData
->lon
;
3290 wpLLH
.alt
= wpData
->alt
;
3292 // WP #0 - special waypoint - HOME
3293 if ((wpNumber
== 0) && ARMING_FLAG(ARMED
) && (posControl
.flags
.estPosStatus
>= EST_USABLE
) && posControl
.gpsOrigin
.valid
&& posControl
.flags
.isGCSAssistedNavigationEnabled
) {
3294 // Forcibly set home position. Note that this is only valid if already armed, otherwise home will be reset instantly
3295 geoConvertGeodeticToLocal(&wpPos
.pos
, &posControl
.gpsOrigin
, &wpLLH
, GEO_ALT_RELATIVE
);
3296 setHomePosition(&wpPos
.pos
, 0, NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_Z
| NAV_POS_UPDATE_HEADING
, NAV_HOME_VALID_ALL
);
3298 // WP #255 - special waypoint - directly set desiredPosition
3299 // Only valid when armed and in poshold mode
3300 else if ((wpNumber
== 255) && (wpData
->action
== NAV_WP_ACTION_WAYPOINT
) &&
3301 ARMING_FLAG(ARMED
) && (posControl
.flags
.estPosStatus
== EST_TRUSTED
) && posControl
.gpsOrigin
.valid
&& posControl
.flags
.isGCSAssistedNavigationEnabled
&&
3302 (posControl
.navState
== NAV_STATE_POSHOLD_3D_IN_PROGRESS
)) {
3303 // Convert to local coordinates
3304 geoConvertGeodeticToLocal(&wpPos
.pos
, &posControl
.gpsOrigin
, &wpLLH
, GEO_ALT_RELATIVE
);
3306 navSetWaypointFlags_t waypointUpdateFlags
= NAV_POS_UPDATE_XY
;
3308 // If we received global altitude == 0, use current altitude
3309 if (wpData
->alt
!= 0) {
3310 waypointUpdateFlags
|= NAV_POS_UPDATE_Z
;
3313 if (wpData
->p1
> 0 && wpData
->p1
< 360) {
3314 waypointUpdateFlags
|= NAV_POS_UPDATE_HEADING
;
3317 setDesiredPosition(&wpPos
.pos
, DEGREES_TO_CENTIDEGREES(wpData
->p1
), waypointUpdateFlags
);
3319 // WP #1 - #NAV_MAX_WAYPOINTS - common waypoints - pre-programmed mission
3320 else if ((wpNumber
>= 1) && (wpNumber
<= NAV_MAX_WAYPOINTS
) && !ARMING_FLAG(ARMED
)) {
3321 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
) {
3322 // Only allow upload next waypoint (continue upload mission) or first waypoint (new mission)
3323 static int8_t nonGeoWaypointCount
= 0;
3325 if (wpNumber
== (posControl
.waypointCount
+ 1) || wpNumber
== 1) {
3326 if (wpNumber
== 1) {
3327 resetWaypointList();
3329 posControl
.waypointList
[wpNumber
- 1] = *wpData
;
3330 if(wpData
->action
== NAV_WP_ACTION_SET_POI
|| wpData
->action
== NAV_WP_ACTION_SET_HEAD
|| wpData
->action
== NAV_WP_ACTION_JUMP
) {
3331 nonGeoWaypointCount
+= 1;
3332 if(wpData
->action
== NAV_WP_ACTION_JUMP
) {
3333 posControl
.waypointList
[wpNumber
- 1].p1
-= 1; // make index (vice WP #)
3337 posControl
.waypointCount
= wpNumber
;
3338 posControl
.waypointListValid
= (wpData
->flag
== NAV_WP_FLAG_LAST
);
3339 posControl
.geoWaypointCount
= posControl
.waypointCount
- nonGeoWaypointCount
;
3340 if (posControl
.waypointListValid
) {
3341 nonGeoWaypointCount
= 0;
3348 void resetWaypointList(void)
3350 posControl
.waypointCount
= 0;
3351 posControl
.waypointListValid
= false;
3352 posControl
.geoWaypointCount
= 0;
3353 posControl
.startWpIndex
= 0;
3354 #ifdef USE_MULTI_MISSION
3355 posControl
.totalMultiMissionWpCount
= 0;
3356 posControl
.loadedMultiMissionIndex
= 0;
3357 posControl
.multiMissionCount
= 0;
3361 bool isWaypointListValid(void)
3363 return posControl
.waypointListValid
;
3366 int getWaypointCount(void)
3368 uint8_t waypointCount
= posControl
.waypointCount
;
3369 #ifdef USE_MULTI_MISSION
3370 if (!ARMING_FLAG(ARMED
) && posControl
.totalMultiMissionWpCount
) {
3371 waypointCount
= posControl
.totalMultiMissionWpCount
;
3374 return waypointCount
;
3377 #ifdef USE_MULTI_MISSION
3378 void selectMultiMissionIndex(int8_t increment
)
3380 if (posControl
.multiMissionCount
> 1) { // stick selection only active when multi mission loaded
3381 navConfigMutable()->general
.waypoint_multi_mission_index
= constrain(navConfigMutable()->general
.waypoint_multi_mission_index
+ increment
, 1, posControl
.multiMissionCount
);
3385 void loadSelectedMultiMission(uint8_t missionIndex
)
3387 uint8_t missionCount
= 1;
3388 posControl
.waypointCount
= 0;
3389 posControl
.geoWaypointCount
= 0;
3391 for (int i
= 0; i
< NAV_MAX_WAYPOINTS
; i
++) {
3392 if (missionCount
== missionIndex
) {
3393 /* store details of selected mission: start wp index, mission wp count, geo wp count */
3394 if (!(posControl
.waypointList
[i
].action
== NAV_WP_ACTION_SET_POI
||
3395 posControl
.waypointList
[i
].action
== NAV_WP_ACTION_SET_HEAD
||
3396 posControl
.waypointList
[i
].action
== NAV_WP_ACTION_JUMP
)) {
3397 posControl
.geoWaypointCount
++;
3400 if (posControl
.waypointCount
== 0) {
3401 posControl
.waypointCount
= 1; // start marker only, value unimportant (but not 0)
3402 posControl
.startWpIndex
= i
;
3405 if (posControl
.waypointList
[i
].flag
== NAV_WP_FLAG_LAST
) {
3406 posControl
.waypointCount
= i
- posControl
.startWpIndex
+ 1;
3409 } else if (posControl
.waypointList
[i
].flag
== NAV_WP_FLAG_LAST
) {
3414 posControl
.loadedMultiMissionIndex
= posControl
.multiMissionCount
? missionIndex
: 0;
3415 posControl
.activeWaypointIndex
= posControl
.startWpIndex
;
3418 bool updateWpMissionChange(void)
3420 /* Function only called when ARMED */
3422 if (posControl
.multiMissionCount
< 2 || posControl
.wpPlannerActiveWPIndex
|| FLIGHT_MODE(NAV_WP_MODE
)) {
3426 uint8_t setMissionIndex
= navConfig()->general
.waypoint_multi_mission_index
;
3427 if (!(IS_RC_MODE_ACTIVE(BOXCHANGEMISSION
) || isAdjustmentFunctionSelected(ADJUSTMENT_NAV_WP_MULTI_MISSION_INDEX
))) {
3428 /* reload mission if mission index changed */
3429 if (posControl
.loadedMultiMissionIndex
!= setMissionIndex
) {
3430 loadSelectedMultiMission(setMissionIndex
);
3435 static bool toggleFlag
= false;
3436 if (IS_RC_MODE_ACTIVE(BOXNAVWP
) && toggleFlag
) {
3437 if (setMissionIndex
== posControl
.multiMissionCount
) {
3438 navConfigMutable()->general
.waypoint_multi_mission_index
= 1;
3440 selectMultiMissionIndex(1);
3443 } else if (!IS_RC_MODE_ACTIVE(BOXNAVWP
)) {
3446 return false; // block WP mode while changing mission when armed
3449 bool checkMissionCount(int8_t waypoint
)
3451 if (nonVolatileWaypointList(waypoint
)->flag
== NAV_WP_FLAG_LAST
) {
3452 posControl
.multiMissionCount
+= 1; // count up no missions in multi mission WP file
3453 if (waypoint
!= NAV_MAX_WAYPOINTS
- 1) {
3454 return (nonVolatileWaypointList(waypoint
+ 1)->flag
== NAV_WP_FLAG_LAST
&&
3455 nonVolatileWaypointList(waypoint
+ 1)->action
==NAV_WP_ACTION_RTH
);
3456 // end of multi mission file if successive NAV_WP_FLAG_LAST and default action (RTH)
3461 #endif // multi mission
3462 #ifdef NAV_NON_VOLATILE_WAYPOINT_STORAGE
3463 bool loadNonVolatileWaypointList(bool clearIfLoaded
)
3465 /* Don't load if armed or mission planner active */
3466 if (ARMING_FLAG(ARMED
) || posControl
.wpPlannerActiveWPIndex
) {
3470 // if forced and waypoints are already loaded, just unload them.
3471 if (clearIfLoaded
&& posControl
.waypointCount
> 0) {
3472 resetWaypointList();
3475 #ifdef USE_MULTI_MISSION
3476 /* Reset multi mission index to 1 if exceeds number of available missions */
3477 if (navConfig()->general
.waypoint_multi_mission_index
> posControl
.multiMissionCount
) {
3478 navConfigMutable()->general
.waypoint_multi_mission_index
= 1;
3481 for (int i
= 0; i
< NAV_MAX_WAYPOINTS
; i
++) {
3482 setWaypoint(i
+ 1, nonVolatileWaypointList(i
));
3483 #ifdef USE_MULTI_MISSION
3484 /* count up number of missions and exit after last multi mission */
3485 if (checkMissionCount(i
)) {
3489 posControl
.totalMultiMissionWpCount
= posControl
.waypointCount
;
3490 loadSelectedMultiMission(navConfig()->general
.waypoint_multi_mission_index
);
3492 /* Mission sanity check failed - reset the list
3493 * Also reset if no selected mission loaded (shouldn't happen) */
3494 if (!posControl
.waypointListValid
|| !posControl
.waypointCount
) {
3496 // check this is the last waypoint
3497 if (nonVolatileWaypointList(i
)->flag
== NAV_WP_FLAG_LAST
) {
3502 // Mission sanity check failed - reset the list
3503 if (!posControl
.waypointListValid
) {
3505 resetWaypointList();
3508 return posControl
.waypointListValid
;
3511 bool saveNonVolatileWaypointList(void)
3513 if (ARMING_FLAG(ARMED
) || !posControl
.waypointListValid
)
3516 for (int i
= 0; i
< NAV_MAX_WAYPOINTS
; i
++) {
3517 getWaypoint(i
+ 1, nonVolatileWaypointListMutable(i
));
3519 #ifdef USE_MULTI_MISSION
3520 navConfigMutable()->general
.waypoint_multi_mission_index
= 1; // reset selected mission to 1 when new entries saved
3522 saveConfigAndNotify();
3528 #if defined(USE_SAFE_HOME)
3530 void resetSafeHomes(void)
3532 memset(safeHomeConfigMutable(0), 0, sizeof(navSafeHome_t
) * MAX_SAFE_HOMES
);
3536 static void mapWaypointToLocalPosition(fpVector3_t
* localPos
, const navWaypoint_t
* waypoint
, geoAltitudeConversionMode_e altConv
)
3538 gpsLocation_t wpLLH
;
3540 /* Default to home position if lat & lon = 0 or HOME flag set
3541 * Applicable to WAYPOINT, HOLD_TIME & LANDING WP types */
3542 if ((waypoint
->lat
== 0 && waypoint
->lon
== 0) || waypoint
->flag
== NAV_WP_FLAG_HOME
) {
3543 wpLLH
.lat
= GPS_home
.lat
;
3544 wpLLH
.lon
= GPS_home
.lon
;
3546 wpLLH
.lat
= waypoint
->lat
;
3547 wpLLH
.lon
= waypoint
->lon
;
3549 wpLLH
.alt
= waypoint
->alt
;
3551 geoConvertGeodeticToLocal(localPos
, &posControl
.gpsOrigin
, &wpLLH
, altConv
);
3554 static void calculateAndSetActiveWaypointToLocalPosition(const fpVector3_t
* pos
)
3556 // Calculate bearing towards waypoint and store it in waypoint bearing parameter (this will further be used to detect missed waypoints)
3557 if (isWaypointNavTrackingActive() && !(posControl
.activeWaypoint
.pos
.x
== pos
->x
&& posControl
.activeWaypoint
.pos
.y
== pos
->y
)) {
3558 posControl
.activeWaypoint
.bearing
= calculateBearingBetweenLocalPositions(&posControl
.activeWaypoint
.pos
, pos
);
3560 posControl
.activeWaypoint
.bearing
= calculateBearingToDestination(pos
);
3562 posControl
.activeWaypoint
.nextTurnAngle
= -1; // no turn angle set (-1), will be set by WP mode as required
3564 posControl
.activeWaypoint
.pos
= *pos
;
3566 // Set desired position to next waypoint (XYZ-controller)
3567 setDesiredPosition(&posControl
.activeWaypoint
.pos
, posControl
.activeWaypoint
.bearing
, NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_Z
| NAV_POS_UPDATE_HEADING
);
3570 geoAltitudeConversionMode_e
waypointMissionAltConvMode(geoAltitudeDatumFlag_e datumFlag
)
3572 return ((datumFlag
& NAV_WP_MSL_DATUM
) == NAV_WP_MSL_DATUM
) ? GEO_ALT_ABSOLUTE
: GEO_ALT_RELATIVE
;
3575 static void calculateAndSetActiveWaypoint(const navWaypoint_t
* waypoint
)
3577 fpVector3_t localPos
;
3578 mapWaypointToLocalPosition(&localPos
, waypoint
, waypointMissionAltConvMode(waypoint
->p3
));
3579 calculateAndSetActiveWaypointToLocalPosition(&localPos
);
3581 if (navConfig()->fw
.wp_turn_smoothing
) {
3582 fpVector3_t posNextWp
;
3583 if (getLocalPosNextWaypoint(&posNextWp
)) {
3584 int32_t bearingToNextWp
= calculateBearingBetweenLocalPositions(&posControl
.activeWaypoint
.pos
, &posNextWp
);
3585 posControl
.activeWaypoint
.nextTurnAngle
= wrap_18000(bearingToNextWp
- posControl
.activeWaypoint
.bearing
);
3590 /* Checks if active waypoint is last in mission */
3591 bool isLastMissionWaypoint(void)
3593 return FLIGHT_MODE(NAV_WP_MODE
) && (posControl
.activeWaypointIndex
>= (posControl
.startWpIndex
+ posControl
.waypointCount
- 1) ||
3594 (posControl
.waypointList
[posControl
.activeWaypointIndex
].flag
== NAV_WP_FLAG_LAST
));
3597 /* Checks if Nav hold position is active */
3598 bool isNavHoldPositionActive(void)
3600 // WP mode last WP hold and Timed hold positions
3601 if (FLIGHT_MODE(NAV_WP_MODE
)) {
3602 return isLastMissionWaypoint() || NAV_Status
.state
== MW_NAV_STATE_HOLD_TIMED
;
3604 // RTH mode (spiral climb and Home positions but excluding RTH Trackback point positions) and POSHOLD mode
3605 // Also hold position during emergency landing if position valid
3606 return (FLIGHT_MODE(NAV_RTH_MODE
) && !posControl
.flags
.rthTrackbackActive
) ||
3607 FLIGHT_MODE(NAV_POSHOLD_MODE
) ||
3608 navigationIsExecutingAnEmergencyLanding();
3611 float getActiveSpeed(void)
3613 /* Currently only applicable for multicopter */
3615 // Speed limit for modes where speed manually controlled
3616 if (posControl
.flags
.isAdjustingPosition
|| FLIGHT_MODE(NAV_COURSE_HOLD_MODE
)) {
3617 return navConfig()->general
.max_manual_speed
;
3620 uint16_t waypointSpeed
= navConfig()->general
.auto_speed
;
3622 if (navGetStateFlags(posControl
.navState
) & NAV_AUTO_WP
) {
3623 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
)) {
3624 float wpSpecificSpeed
= 0.0f
;
3625 if(posControl
.waypointList
[posControl
.activeWaypointIndex
].action
== NAV_WP_ACTION_HOLD_TIME
)
3626 wpSpecificSpeed
= posControl
.waypointList
[posControl
.activeWaypointIndex
].p2
; // P1 is hold time
3628 wpSpecificSpeed
= posControl
.waypointList
[posControl
.activeWaypointIndex
].p1
; // default case
3630 if (wpSpecificSpeed
>= 50.0f
&& wpSpecificSpeed
<= navConfig()->general
.max_auto_speed
) {
3631 waypointSpeed
= wpSpecificSpeed
;
3632 } else if (wpSpecificSpeed
> navConfig()->general
.max_auto_speed
) {
3633 waypointSpeed
= navConfig()->general
.max_auto_speed
;
3638 return waypointSpeed
;
3641 bool isWaypointNavTrackingActive(void)
3643 // NAV_WP_MODE flag used rather than state flag NAV_AUTO_WP to ensure heading to initial waypoint
3644 // is set from current position not previous WP. Works for WP Restart intermediate WP as well as first mission WP.
3645 // (NAV_WP_MODE flag isn't set until WP initialisation is finished, i.e. after calculateAndSetActiveWaypoint called)
3647 return FLIGHT_MODE(NAV_WP_MODE
) || (posControl
.flags
.rthTrackbackActive
&& posControl
.activeRthTBPointIndex
!= posControl
.rthTBLastSavedIndex
);
3650 /*-----------------------------------------------------------
3651 * Process adjustments to alt, pos and yaw controllers
3652 *-----------------------------------------------------------*/
3653 static void processNavigationRCAdjustments(void)
3655 /* Process pilot's RC input. Disable all pilot's input when in FAILSAFE_MODE */
3656 navigationFSMStateFlags_t navStateFlags
= navGetStateFlags(posControl
.navState
);
3658 if (FLIGHT_MODE(FAILSAFE_MODE
)) {
3659 if (STATE(MULTIROTOR
) && navStateFlags
& NAV_RC_POS
) {
3660 resetMulticopterBrakingMode();
3662 posControl
.flags
.isAdjustingAltitude
= false;
3663 posControl
.flags
.isAdjustingPosition
= false;
3664 posControl
.flags
.isAdjustingHeading
= false;
3669 posControl
.flags
.isAdjustingAltitude
= (navStateFlags
& NAV_RC_ALT
) && adjustAltitudeFromRCInput();
3670 posControl
.flags
.isAdjustingPosition
= (navStateFlags
& NAV_RC_POS
) && adjustPositionFromRCInput();
3671 posControl
.flags
.isAdjustingHeading
= (navStateFlags
& NAV_RC_YAW
) && adjustHeadingFromRCInput();
3674 /*-----------------------------------------------------------
3675 * A main function to call position controllers at loop rate
3676 *-----------------------------------------------------------*/
3677 void applyWaypointNavigationAndAltitudeHold(void)
3679 const timeUs_t currentTimeUs
= micros();
3681 //Updata blackbox data
3683 if (posControl
.flags
.estAltStatus
== EST_TRUSTED
) navFlags
|= (1 << 0);
3684 if (posControl
.flags
.estAglStatus
== EST_TRUSTED
) navFlags
|= (1 << 1);
3685 if (posControl
.flags
.estPosStatus
== EST_TRUSTED
) navFlags
|= (1 << 2);
3686 if (posControl
.flags
.isTerrainFollowEnabled
) navFlags
|= (1 << 3);
3687 #if defined(NAV_GPS_GLITCH_DETECTION)
3688 if (isGPSGlitchDetected()) navFlags
|= (1 << 4);
3690 if (posControl
.flags
.estHeadingStatus
== EST_TRUSTED
) navFlags
|= (1 << 5);
3692 // Reset all navigation requests - NAV controllers will set them if necessary
3693 DISABLE_STATE(NAV_MOTOR_STOP_OR_IDLE
);
3695 // No navigation when disarmed
3696 if (!ARMING_FLAG(ARMED
)) {
3697 // If we are disarmed, abort forced RTH or Emergency Landing
3698 posControl
.flags
.forcedRTHActivated
= false;
3699 posControl
.flags
.forcedEmergLandingActivated
= false;
3700 posControl
.flags
.manualEmergLandActive
= false;
3701 // ensure WP missions always restart from first waypoint after disarm
3702 posControl
.activeWaypointIndex
= posControl
.startWpIndex
;
3703 // Reset RTH trackback
3704 posControl
.activeRthTBPointIndex
= -1;
3705 posControl
.flags
.rthTrackbackActive
= false;
3706 posControl
.rthTBWrapAroundCounter
= -1;
3712 posControl
.flags
.horizontalPositionDataConsumed
= false;
3713 posControl
.flags
.verticalPositionDataConsumed
= false;
3715 /* Process controllers */
3716 navigationFSMStateFlags_t navStateFlags
= navGetStateFlags(posControl
.navState
);
3717 if (STATE(ROVER
) || STATE(BOAT
)) {
3718 applyRoverBoatNavigationController(navStateFlags
, currentTimeUs
);
3719 } else if (STATE(FIXED_WING_LEGACY
)) {
3720 applyFixedWingNavigationController(navStateFlags
, currentTimeUs
);
3723 applyMulticopterNavigationController(navStateFlags
, currentTimeUs
);
3726 /* Consume position data */
3727 if (posControl
.flags
.horizontalPositionDataConsumed
)
3728 posControl
.flags
.horizontalPositionDataNew
= false;
3730 if (posControl
.flags
.verticalPositionDataConsumed
)
3731 posControl
.flags
.verticalPositionDataNew
= false;
3733 //Update blackbox data
3734 if (posControl
.flags
.isAdjustingPosition
) navFlags
|= (1 << 6);
3735 if (posControl
.flags
.isAdjustingAltitude
) navFlags
|= (1 << 7);
3736 if (posControl
.flags
.isAdjustingHeading
) navFlags
|= (1 << 8);
3738 navTargetPosition
[X
] = lrintf(posControl
.desiredState
.pos
.x
);
3739 navTargetPosition
[Y
] = lrintf(posControl
.desiredState
.pos
.y
);
3740 navTargetPosition
[Z
] = lrintf(posControl
.desiredState
.pos
.z
);
3742 navDesiredHeading
= wrap_36000(posControl
.desiredState
.yaw
);
3745 /*-----------------------------------------------------------
3746 * Set CF's FLIGHT_MODE from current NAV_MODE
3747 *-----------------------------------------------------------*/
3748 void switchNavigationFlightModes(void)
3750 const flightModeFlags_e enabledNavFlightModes
= navGetMappedFlightModes(posControl
.navState
);
3751 const flightModeFlags_e disabledFlightModes
= (NAV_ALTHOLD_MODE
| NAV_RTH_MODE
| NAV_POSHOLD_MODE
| NAV_WP_MODE
| NAV_LAUNCH_MODE
| NAV_COURSE_HOLD_MODE
) & (~enabledNavFlightModes
);
3752 DISABLE_FLIGHT_MODE(disabledFlightModes
);
3753 ENABLE_FLIGHT_MODE(enabledNavFlightModes
);
3756 /*-----------------------------------------------------------
3757 * desired NAV_MODE from combination of FLIGHT_MODE flags
3758 *-----------------------------------------------------------*/
3759 static bool canActivateAltHoldMode(void)
3761 return (posControl
.flags
.estAltStatus
>= EST_USABLE
);
3764 static bool canActivatePosHoldMode(void)
3766 return (posControl
.flags
.estPosStatus
>= EST_USABLE
) && (posControl
.flags
.estVelStatus
== EST_TRUSTED
) && (posControl
.flags
.estHeadingStatus
>= EST_USABLE
);
3769 static bool canActivateNavigationModes(void)
3771 return (posControl
.flags
.estPosStatus
== EST_TRUSTED
) && (posControl
.flags
.estVelStatus
== EST_TRUSTED
) && (posControl
.flags
.estHeadingStatus
>= EST_USABLE
);
3774 static bool isWaypointMissionValid(void)
3776 return posControl
.waypointListValid
&& (posControl
.waypointCount
> 0);
3779 void checkManualEmergencyLandingControl(bool forcedActivation
)
3781 static timeMs_t timeout
= 0;
3782 static int8_t counter
= 0;
3784 timeMs_t currentTimeMs
= millis();
3786 if (timeout
&& currentTimeMs
> timeout
) {
3788 counter
-= counter
? 1 : 0;
3793 if (IS_RC_MODE_ACTIVE(BOXNAVPOSHOLD
)) {
3794 if (!timeout
&& toggle
) {
3795 timeout
= currentTimeMs
+ 4000;
3803 // Emergency landing toggled ON or OFF after 5 cycles of Poshold mode @ 1Hz minimum rate
3804 if (counter
>= 5 || forcedActivation
) {
3806 posControl
.flags
.manualEmergLandActive
= !posControl
.flags
.manualEmergLandActive
;
3808 if (!posControl
.flags
.manualEmergLandActive
) {
3809 navProcessFSMEvents(NAV_FSM_EVENT_SWITCH_TO_IDLE
);
3814 static navigationFSMEvent_t
selectNavEventFromBoxModeInput(void)
3816 static bool canActivateWaypoint
= false;
3817 static bool canActivateLaunchMode
= false;
3819 //We can switch modes only when ARMED
3820 if (ARMING_FLAG(ARMED
)) {
3821 // Ask failsafe system if we can use navigation system
3822 if (failsafeBypassNavigation()) {
3823 return NAV_FSM_EVENT_SWITCH_TO_IDLE
;
3826 // Flags if we can activate certain nav modes (check if we have required sensors and they provide valid data)
3827 const bool canActivateAltHold
= canActivateAltHoldMode();
3828 const bool canActivatePosHold
= canActivatePosHoldMode();
3829 const bool canActivateNavigation
= canActivateNavigationModes();
3830 const bool isExecutingRTH
= navGetStateFlags(posControl
.navState
) & NAV_AUTO_RTH
;
3831 #ifdef USE_SAFE_HOME
3832 checkSafeHomeState(isExecutingRTH
|| posControl
.flags
.forcedRTHActivated
);
3834 // deactivate rth trackback if RTH not active
3835 if (posControl
.flags
.rthTrackbackActive
) {
3836 posControl
.flags
.rthTrackbackActive
= isExecutingRTH
;
3839 /* Emergency landing controlled manually by rapid switching of Poshold mode.
3840 * Landing toggled ON or OFF for each Poshold activation sequence */
3841 checkManualEmergencyLandingControl(false);
3843 /* Emergency landing triggered by failsafe Landing or manually initiated */
3844 if (posControl
.flags
.forcedEmergLandingActivated
|| posControl
.flags
.manualEmergLandActive
) {
3845 return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
;
3848 /* Keep Emergency landing mode active once triggered.
3849 * If caused by sensor failure - landing auto cancelled if sensors working again or when WP and RTH deselected or if Althold selected.
3850 * If caused by RTH Sanity Checking - landing cancelled if RTH deselected.
3851 * Remains active if failsafe active regardless of mode selections */
3852 if (navigationIsExecutingAnEmergencyLanding()) {
3853 bool autonomousNavIsPossible
= canActivateNavigation
&& canActivateAltHold
&& STATE(GPS_FIX_HOME
);
3854 bool emergLandingCancel
= (!autonomousNavIsPossible
&&
3855 ((IS_RC_MODE_ACTIVE(BOXNAVALTHOLD
) && canActivateAltHold
) || !(IS_RC_MODE_ACTIVE(BOXNAVWP
) || IS_RC_MODE_ACTIVE(BOXNAVRTH
)))) ||
3856 (autonomousNavIsPossible
&& !IS_RC_MODE_ACTIVE(BOXNAVRTH
));
3858 if ((!posControl
.rthSanityChecker
.rthSanityOK
|| !autonomousNavIsPossible
) && (!emergLandingCancel
|| FLIGHT_MODE(FAILSAFE_MODE
))) {
3859 return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
;
3862 posControl
.rthSanityChecker
.rthSanityOK
= true;
3864 // Keep canActivateWaypoint flag at FALSE if there is no mission loaded
3865 // Also block WP mission if we are executing RTH
3866 if (!isWaypointMissionValid() || isExecutingRTH
) {
3867 canActivateWaypoint
= false;
3870 /* Airplane specific modes */
3871 if (STATE(AIRPLANE
)) {
3872 // LAUNCH mode has priority over any other NAV mode
3873 if (isNavLaunchEnabled()) { // FIXME: Only available for fixed wing aircrafts now
3874 if (canActivateLaunchMode
) {
3875 canActivateLaunchMode
= false;
3876 return NAV_FSM_EVENT_SWITCH_TO_LAUNCH
;
3878 else if FLIGHT_MODE(NAV_LAUNCH_MODE
) {
3879 // Make sure we don't bail out to IDLE
3880 return NAV_FSM_EVENT_NONE
;
3884 // If we were in LAUNCH mode - force switch to IDLE only if the throttle is low or throttle stick < launch idle throttle
3885 if (FLIGHT_MODE(NAV_LAUNCH_MODE
)) {
3886 if (abortLaunchAllowed()) {
3887 return NAV_FSM_EVENT_SWITCH_TO_IDLE
;
3889 return NAV_FSM_EVENT_NONE
;
3894 /* Soaring mode, disables altitude control in Position hold and Course hold modes.
3895 * Pitch allowed to freefloat within defined Angle mode deadband */
3896 if (IS_RC_MODE_ACTIVE(BOXSOARING
) && (FLIGHT_MODE(NAV_POSHOLD_MODE
) || FLIGHT_MODE(NAV_COURSE_HOLD_MODE
))) {
3897 if (!FLIGHT_MODE(SOARING_MODE
)) {
3898 ENABLE_FLIGHT_MODE(SOARING_MODE
);
3901 DISABLE_FLIGHT_MODE(SOARING_MODE
);
3905 // Failsafe_RTH (can override MANUAL)
3906 if (posControl
.flags
.forcedRTHActivated
) {
3907 // If we request forced RTH - attempt to activate it no matter what
3908 // This might switch to emergency landing controller if GPS is unavailable
3909 return NAV_FSM_EVENT_SWITCH_TO_RTH
;
3912 /* Pilot-triggered RTH (can override MANUAL), also fall-back for WP if there is no mission loaded
3913 * Prevent MANUAL falling back to RTH if selected during active mission (canActivateWaypoint is set false on MANUAL selection)
3914 * Also prevent WP falling back to RTH if WP mission planner is active */
3915 const bool blockWPFallback
= IS_RC_MODE_ACTIVE(BOXMANUAL
) || posControl
.flags
.wpMissionPlannerActive
;
3916 if (IS_RC_MODE_ACTIVE(BOXNAVRTH
) || (IS_RC_MODE_ACTIVE(BOXNAVWP
) && !canActivateWaypoint
&& !blockWPFallback
)) {
3917 // Check for isExecutingRTH to prevent switching our from RTH in case of a brief GPS loss
3918 // If don't keep this, loss of any of the canActivateNavigation && canActivateAltHold
3919 // will kick us out of RTH state machine via NAV_FSM_EVENT_SWITCH_TO_IDLE and will prevent any of the fall-back
3920 // logic to kick in (waiting for GPS on airplanes, switch to emergency landing etc)
3921 if (isExecutingRTH
|| (canActivateNavigation
&& canActivateAltHold
&& STATE(GPS_FIX_HOME
))) {
3922 return NAV_FSM_EVENT_SWITCH_TO_RTH
;
3926 // MANUAL mode has priority over WP/PH/AH
3927 if (IS_RC_MODE_ACTIVE(BOXMANUAL
)) {
3928 canActivateWaypoint
= false; // Block WP mode if we are in PASSTHROUGH mode
3929 return NAV_FSM_EVENT_SWITCH_TO_IDLE
;
3932 // Pilot-activated waypoint mission. Fall-back to RTH in case of no mission loaded
3933 // Block activation if using WP Mission Planner
3934 // Also check multimission mission change status before activating WP mode
3935 #ifdef USE_MULTI_MISSION
3936 if (updateWpMissionChange() && IS_RC_MODE_ACTIVE(BOXNAVWP
) && !posControl
.flags
.wpMissionPlannerActive
) {
3938 if (IS_RC_MODE_ACTIVE(BOXNAVWP
) && !posControl
.flags
.wpMissionPlannerActive
) {
3940 if (FLIGHT_MODE(NAV_WP_MODE
) || (canActivateWaypoint
&& canActivateNavigation
&& canActivateAltHold
&& STATE(GPS_FIX_HOME
)))
3941 return NAV_FSM_EVENT_SWITCH_TO_WAYPOINT
;
3944 // Arm the state variable if the WP BOX mode is not enabled
3945 canActivateWaypoint
= true;
3948 if (IS_RC_MODE_ACTIVE(BOXNAVPOSHOLD
)) {
3949 if (FLIGHT_MODE(NAV_POSHOLD_MODE
) || (canActivatePosHold
&& canActivateAltHold
))
3950 return NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
;
3953 // CRUISE has priority over COURSE_HOLD and AH
3954 if (IS_RC_MODE_ACTIVE(BOXNAVCRUISE
)) {
3955 if ((FLIGHT_MODE(NAV_COURSE_HOLD_MODE
) && FLIGHT_MODE(NAV_ALTHOLD_MODE
)) || (canActivatePosHold
&& canActivateAltHold
))
3956 return NAV_FSM_EVENT_SWITCH_TO_CRUISE
;
3959 // PH has priority over COURSE_HOLD
3960 // CRUISE has priority on AH
3961 if (IS_RC_MODE_ACTIVE(BOXNAVCOURSEHOLD
)) {
3962 if (IS_RC_MODE_ACTIVE(BOXNAVALTHOLD
) && ((FLIGHT_MODE(NAV_COURSE_HOLD_MODE
) && FLIGHT_MODE(NAV_ALTHOLD_MODE
)) || (canActivatePosHold
&& canActivateAltHold
))) {
3963 return NAV_FSM_EVENT_SWITCH_TO_CRUISE
;
3966 if (FLIGHT_MODE(NAV_COURSE_HOLD_MODE
) || (canActivatePosHold
)) {
3967 return NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD
;
3971 if (IS_RC_MODE_ACTIVE(BOXNAVALTHOLD
)) {
3972 if ((FLIGHT_MODE(NAV_ALTHOLD_MODE
)) || (canActivateAltHold
))
3973 return NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
;
3976 canActivateWaypoint
= false;
3978 // 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)
3979 canActivateLaunchMode
= isNavLaunchEnabled() && (!sensors(SENSOR_GPS
) || (sensors(SENSOR_GPS
) && !isGPSHeadingValid()));
3982 return NAV_FSM_EVENT_SWITCH_TO_IDLE
;
3985 /*-----------------------------------------------------------
3986 * An indicator that throttle tilt compensation is forced
3987 *-----------------------------------------------------------*/
3988 bool navigationRequiresThrottleTiltCompensation(void)
3990 return !STATE(FIXED_WING_LEGACY
) && (navGetStateFlags(posControl
.navState
) & NAV_REQUIRE_THRTILT
);
3993 /*-----------------------------------------------------------
3994 * An indicator that ANGLE mode must be forced per NAV requirement
3995 *-----------------------------------------------------------*/
3996 bool navigationRequiresAngleMode(void)
3998 const navigationFSMStateFlags_t currentState
= navGetStateFlags(posControl
.navState
);
3999 return (currentState
& NAV_REQUIRE_ANGLE
) || ((currentState
& NAV_REQUIRE_ANGLE_FW
) && STATE(FIXED_WING_LEGACY
));
4002 /*-----------------------------------------------------------
4003 * An indicator that TURN ASSISTANCE is required for navigation
4004 *-----------------------------------------------------------*/
4005 bool navigationRequiresTurnAssistance(void)
4007 const navigationFSMStateFlags_t currentState
= navGetStateFlags(posControl
.navState
);
4008 if (STATE(FIXED_WING_LEGACY
)) {
4009 // For airplanes turn assistant is always required when controlling position
4010 return (currentState
& (NAV_CTL_POS
| NAV_CTL_ALT
));
4017 * An indicator that NAV is in charge of heading control (a signal to disable other heading controllers)
4019 int8_t navigationGetHeadingControlState(void)
4021 // For airplanes report as manual heading control
4022 if (STATE(FIXED_WING_LEGACY
)) {
4023 return NAV_HEADING_CONTROL_MANUAL
;
4026 // For multirotors it depends on navigation system mode
4027 // Course hold requires Auto Control to update heading hold target whilst RC adjustment active
4028 if (navGetStateFlags(posControl
.navState
) & NAV_REQUIRE_MAGHOLD
) {
4029 if (posControl
.flags
.isAdjustingHeading
&& !FLIGHT_MODE(NAV_COURSE_HOLD_MODE
)) {
4030 return NAV_HEADING_CONTROL_MANUAL
;
4033 return NAV_HEADING_CONTROL_AUTO
;
4036 return NAV_HEADING_CONTROL_NONE
;
4039 bool navigationTerrainFollowingEnabled(void)
4041 return posControl
.flags
.isTerrainFollowEnabled
;
4044 uint32_t distanceToFirstWP(void)
4046 fpVector3_t startingWaypointPos
;
4047 mapWaypointToLocalPosition(&startingWaypointPos
, &posControl
.waypointList
[posControl
.startWpIndex
], GEO_ALT_RELATIVE
);
4048 return calculateDistanceToDestination(&startingWaypointPos
);
4051 bool navigationPositionEstimateIsHealthy(void)
4053 return (posControl
.flags
.estPosStatus
>= EST_USABLE
) && STATE(GPS_FIX_HOME
);
4056 navArmingBlocker_e
navigationIsBlockingArming(bool *usedBypass
)
4058 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
)));
4061 *usedBypass
= false;
4064 // Apply extra arming safety only if pilot has any of GPS modes configured
4065 if ((isUsingNavigationModes() || failsafeMayRequireNavigationMode()) && !navigationPositionEstimateIsHealthy()) {
4066 if (navConfig()->general
.flags
.extra_arming_safety
== NAV_EXTRA_ARMING_SAFETY_ALLOW_BYPASS
&&
4067 (STATE(NAV_EXTRA_ARMING_SAFETY_BYPASSED
) || checkStickPosition(YAW_HI
))) {
4071 return NAV_ARMING_BLOCKER_NONE
;
4073 return NAV_ARMING_BLOCKER_MISSING_GPS_FIX
;
4076 // Don't allow arming if any of NAV modes is active
4077 if (!ARMING_FLAG(ARMED
) && navBoxModesEnabled
) {
4078 return NAV_ARMING_BLOCKER_NAV_IS_ALREADY_ACTIVE
;
4081 // Don't allow arming if first waypoint is farther than configured safe distance
4082 if ((posControl
.waypointCount
> 0) && (navConfig()->general
.waypoint_safe_distance
!= 0)) {
4083 if (distanceToFirstWP() > METERS_TO_CENTIMETERS(navConfig()->general
.waypoint_safe_distance
) && !checkStickPosition(YAW_HI
)) {
4084 return NAV_ARMING_BLOCKER_FIRST_WAYPOINT_TOO_FAR
;
4089 * Don't allow arming if any of JUMP waypoint has invalid settings
4090 * First WP can't be JUMP
4091 * Can't jump to immediately adjacent WPs (pointless)
4092 * Can't jump beyond WP list
4093 * Only jump to geo-referenced WP types
4095 if (posControl
.waypointCount
) {
4096 for (uint8_t wp
= posControl
.startWpIndex
; wp
< posControl
.waypointCount
+ posControl
.startWpIndex
; wp
++){
4097 if (posControl
.waypointList
[wp
].action
== NAV_WP_ACTION_JUMP
){
4098 if (wp
== posControl
.startWpIndex
|| posControl
.waypointList
[wp
].p1
>= posControl
.waypointCount
||
4099 (posControl
.waypointList
[wp
].p1
> (wp
- posControl
.startWpIndex
- 2) && posControl
.waypointList
[wp
].p1
< (wp
- posControl
.startWpIndex
+ 2)) || posControl
.waypointList
[wp
].p2
< -1) {
4100 return NAV_ARMING_BLOCKER_JUMP_WAYPOINT_ERROR
;
4103 /* check for target geo-ref sanity */
4104 uint16_t target
= posControl
.waypointList
[wp
].p1
+ posControl
.startWpIndex
;
4105 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
)) {
4106 return NAV_ARMING_BLOCKER_JUMP_WAYPOINT_ERROR
;
4112 return NAV_ARMING_BLOCKER_NONE
;
4116 * Indicate ready/not ready status
4118 static void updateReadyStatus(void)
4120 static bool posReadyBeepDone
= false;
4122 /* Beep out READY_BEEP once when position lock is firstly acquired and HOME set */
4123 if (navigationPositionEstimateIsHealthy() && !posReadyBeepDone
) {
4124 beeper(BEEPER_READY_BEEP
);
4125 posReadyBeepDone
= true;
4129 void updateFlightBehaviorModifiers(void)
4131 if (posControl
.flags
.isGCSAssistedNavigationEnabled
&& !IS_RC_MODE_ACTIVE(BOXGCSNAV
)) {
4132 posControl
.flags
.isGCSAssistedNavigationReset
= true;
4135 posControl
.flags
.isGCSAssistedNavigationEnabled
= IS_RC_MODE_ACTIVE(BOXGCSNAV
);
4138 /* On the fly WP mission planner mode allows WP missions to be setup during navigation.
4139 * Uses the WP mode switch to save WP at current location (WP mode disabled when active)
4140 * Mission can be flown after mission planner mode switched off and saved after disarm. */
4142 void updateWpMissionPlanner(void)
4144 static timeMs_t resetTimerStart
= 0;
4145 if (IS_RC_MODE_ACTIVE(BOXPLANWPMISSION
) && !(FLIGHT_MODE(NAV_WP_MODE
) || isWaypointMissionRTHActive())) {
4146 const bool positionTrusted
= posControl
.flags
.estAltStatus
== EST_TRUSTED
&& posControl
.flags
.estPosStatus
== EST_TRUSTED
&& STATE(GPS_FIX
);
4148 posControl
.flags
.wpMissionPlannerActive
= true;
4149 if (millis() - resetTimerStart
< 1000 && navConfig()->general
.flags
.mission_planner_reset
) {
4150 posControl
.waypointCount
= posControl
.wpPlannerActiveWPIndex
= 0;
4151 posControl
.waypointListValid
= false;
4152 posControl
.wpMissionPlannerStatus
= WP_PLAN_WAIT
;
4154 if (positionTrusted
&& posControl
.wpMissionPlannerStatus
!= WP_PLAN_FULL
) {
4155 missionPlannerSetWaypoint();
4157 posControl
.wpMissionPlannerStatus
= posControl
.wpMissionPlannerStatus
== WP_PLAN_FULL
? WP_PLAN_FULL
: WP_PLAN_WAIT
;
4159 } else if (posControl
.flags
.wpMissionPlannerActive
) {
4160 posControl
.flags
.wpMissionPlannerActive
= false;
4161 posControl
.activeWaypointIndex
= 0;
4162 resetTimerStart
= millis();
4166 void missionPlannerSetWaypoint(void)
4168 static bool boxWPModeIsReset
= true;
4170 boxWPModeIsReset
= !boxWPModeIsReset
? !IS_RC_MODE_ACTIVE(BOXNAVWP
) : boxWPModeIsReset
; // only able to save new WP when WP mode reset
4171 posControl
.wpMissionPlannerStatus
= boxWPModeIsReset
? boxWPModeIsReset
: posControl
.wpMissionPlannerStatus
; // hold save status until WP mode reset
4173 if (!boxWPModeIsReset
|| !IS_RC_MODE_ACTIVE(BOXNAVWP
)) {
4177 if (!posControl
.wpPlannerActiveWPIndex
) { // reset existing mission data before adding first WP
4178 resetWaypointList();
4181 gpsLocation_t wpLLH
;
4182 geoConvertLocalToGeodetic(&wpLLH
, &posControl
.gpsOrigin
, &navGetCurrentActualPositionAndVelocity()->pos
);
4184 posControl
.waypointList
[posControl
.wpPlannerActiveWPIndex
].action
= 1;
4185 posControl
.waypointList
[posControl
.wpPlannerActiveWPIndex
].lat
= wpLLH
.lat
;
4186 posControl
.waypointList
[posControl
.wpPlannerActiveWPIndex
].lon
= wpLLH
.lon
;
4187 posControl
.waypointList
[posControl
.wpPlannerActiveWPIndex
].alt
= wpLLH
.alt
;
4188 posControl
.waypointList
[posControl
.wpPlannerActiveWPIndex
].p1
= posControl
.waypointList
[posControl
.wpPlannerActiveWPIndex
].p2
= 0;
4189 posControl
.waypointList
[posControl
.wpPlannerActiveWPIndex
].p3
|= NAV_WP_ALTMODE
; // use absolute altitude datum
4190 posControl
.waypointList
[posControl
.wpPlannerActiveWPIndex
].flag
= NAV_WP_FLAG_LAST
;
4191 posControl
.waypointListValid
= true;
4193 if (posControl
.wpPlannerActiveWPIndex
) {
4194 posControl
.waypointList
[posControl
.wpPlannerActiveWPIndex
- 1].flag
= 0; // rollling reset of previous end of mission flag when new WP added
4197 posControl
.wpPlannerActiveWPIndex
+= 1;
4198 posControl
.waypointCount
= posControl
.geoWaypointCount
= posControl
.wpPlannerActiveWPIndex
;
4199 posControl
.wpMissionPlannerStatus
= posControl
.waypointCount
== NAV_MAX_WAYPOINTS
? WP_PLAN_FULL
: WP_PLAN_OK
;
4200 boxWPModeIsReset
= false;
4204 * Process NAV mode transition and WP/RTH state machine
4205 * Update rate: RX (data driven or 50Hz)
4207 void updateWaypointsAndNavigationMode(void)
4209 /* Initiate home position update */
4210 updateHomePosition();
4212 /* Update flight statistics */
4213 updateNavigationFlightStatistics();
4215 /* Update NAV ready status */
4216 updateReadyStatus();
4218 // Update flight behaviour modifiers
4219 updateFlightBehaviorModifiers();
4221 // Process switch to a different navigation mode (if needed)
4222 navProcessFSMEvents(selectNavEventFromBoxModeInput());
4224 // Process pilot's RC input to adjust behaviour
4225 processNavigationRCAdjustments();
4227 // Map navMode back to enabled flight modes
4228 switchNavigationFlightModes();
4230 // Update WP mission planner
4231 updateWpMissionPlanner();
4233 // Update RTH trackback
4234 updateRthTrackback(false);
4236 //Update Blackbox data
4237 navCurrentState
= (int16_t)posControl
.navPersistentId
;
4240 /*-----------------------------------------------------------
4241 * NAV main control functions
4242 *-----------------------------------------------------------*/
4243 void navigationUsePIDs(void)
4245 /** Multicopter PIDs */
4246 // Brake time parameter
4247 posControl
.posDecelerationTime
= (float)navConfig()->mc
.posDecelerationTime
/ 100.0f
;
4249 // Position controller expo (taret vel expo for MC)
4250 posControl
.posResponseExpo
= constrainf((float)navConfig()->mc
.posResponseExpo
/ 100.0f
, 0.0f
, 1.0f
);
4252 // Initialize position hold P-controller
4253 for (int axis
= 0; axis
< 2; axis
++) {
4255 &posControl
.pids
.pos
[axis
],
4256 (float)pidProfile()->bank_mc
.pid
[PID_POS_XY
].P
/ 100.0f
,
4264 navPidInit(&posControl
.pids
.vel
[axis
], (float)pidProfile()->bank_mc
.pid
[PID_VEL_XY
].P
/ 20.0f
,
4265 (float)pidProfile()->bank_mc
.pid
[PID_VEL_XY
].I
/ 100.0f
,
4266 (float)pidProfile()->bank_mc
.pid
[PID_VEL_XY
].D
/ 100.0f
,
4267 (float)pidProfile()->bank_mc
.pid
[PID_VEL_XY
].FF
/ 100.0f
,
4268 pidProfile()->navVelXyDTermLpfHz
,
4274 * Set coefficients used in MC VEL_XY
4276 multicopterPosXyCoefficients
.dTermAttenuation
= pidProfile()->navVelXyDtermAttenuation
/ 100.0f
;
4277 multicopterPosXyCoefficients
.dTermAttenuationStart
= pidProfile()->navVelXyDtermAttenuationStart
/ 100.0f
;
4278 multicopterPosXyCoefficients
.dTermAttenuationEnd
= pidProfile()->navVelXyDtermAttenuationEnd
/ 100.0f
;
4280 #ifdef USE_MR_BRAKING_MODE
4281 multicopterPosXyCoefficients
.breakingBoostFactor
= (float) navConfig()->mc
.braking_boost_factor
/ 100.0f
;
4284 // Initialize altitude hold PID-controllers (pos_z, vel_z, acc_z
4286 &posControl
.pids
.pos
[Z
],
4287 (float)pidProfile()->bank_mc
.pid
[PID_POS_Z
].P
/ 100.0f
,
4295 navPidInit(&posControl
.pids
.vel
[Z
], (float)pidProfile()->bank_mc
.pid
[PID_VEL_Z
].P
/ 66.7f
,
4296 (float)pidProfile()->bank_mc
.pid
[PID_VEL_Z
].I
/ 20.0f
,
4297 (float)pidProfile()->bank_mc
.pid
[PID_VEL_Z
].D
/ 100.0f
,
4299 NAV_VEL_Z_DERIVATIVE_CUT_HZ
,
4300 NAV_VEL_Z_ERROR_CUT_HZ
4303 // Initialize surface tracking PID
4304 navPidInit(&posControl
.pids
.surface
, 2.0f
,
4312 /** Airplane PIDs */
4313 // Initialize fixed wing PID controllers
4314 navPidInit(&posControl
.pids
.fw_nav
, (float)pidProfile()->bank_fw
.pid
[PID_POS_XY
].P
/ 100.0f
,
4315 (float)pidProfile()->bank_fw
.pid
[PID_POS_XY
].I
/ 100.0f
,
4316 (float)pidProfile()->bank_fw
.pid
[PID_POS_XY
].D
/ 100.0f
,
4322 navPidInit(&posControl
.pids
.fw_alt
, (float)pidProfile()->bank_fw
.pid
[PID_POS_Z
].P
/ 10.0f
,
4323 (float)pidProfile()->bank_fw
.pid
[PID_POS_Z
].I
/ 10.0f
,
4324 (float)pidProfile()->bank_fw
.pid
[PID_POS_Z
].D
/ 10.0f
,
4330 navPidInit(&posControl
.pids
.fw_heading
, (float)pidProfile()->bank_fw
.pid
[PID_POS_HEADING
].P
/ 10.0f
,
4331 (float)pidProfile()->bank_fw
.pid
[PID_POS_HEADING
].I
/ 10.0f
,
4332 (float)pidProfile()->bank_fw
.pid
[PID_POS_HEADING
].D
/ 100.0f
,
4339 void navigationInit(void)
4342 posControl
.navState
= NAV_STATE_IDLE
;
4344 posControl
.flags
.horizontalPositionDataNew
= false;
4345 posControl
.flags
.verticalPositionDataNew
= false;
4347 posControl
.flags
.estAltStatus
= EST_NONE
;
4348 posControl
.flags
.estPosStatus
= EST_NONE
;
4349 posControl
.flags
.estVelStatus
= EST_NONE
;
4350 posControl
.flags
.estHeadingStatus
= EST_NONE
;
4351 posControl
.flags
.estAglStatus
= EST_NONE
;
4353 posControl
.flags
.forcedRTHActivated
= false;
4354 posControl
.flags
.forcedEmergLandingActivated
= false;
4355 posControl
.waypointCount
= 0;
4356 posControl
.activeWaypointIndex
= 0;
4357 posControl
.waypointListValid
= false;
4358 posControl
.wpPlannerActiveWPIndex
= 0;
4359 posControl
.flags
.wpMissionPlannerActive
= false;
4360 posControl
.startWpIndex
= 0;
4361 posControl
.safehomeState
.isApplied
= false;
4362 #ifdef USE_MULTI_MISSION
4363 posControl
.multiMissionCount
= 0;
4365 /* Set initial surface invalid */
4366 posControl
.actualState
.surfaceMin
= -1.0f
;
4368 /* Reset statistics */
4369 posControl
.totalTripDistance
= 0.0f
;
4371 /* Use system config */
4372 navigationUsePIDs();
4374 #if defined(NAV_NON_VOLATILE_WAYPOINT_STORAGE)
4375 /* configure WP missions at boot */
4376 #ifdef USE_MULTI_MISSION
4377 for (int8_t i
= 0; i
< NAV_MAX_WAYPOINTS
; i
++) { // check number missions in NVM
4378 if (checkMissionCount(i
)) {
4382 /* set index to 1 if saved mission index > available missions */
4383 if (navConfig()->general
.waypoint_multi_mission_index
> posControl
.multiMissionCount
) {
4384 navConfigMutable()->general
.waypoint_multi_mission_index
= 1;
4387 /* load mission on boot */
4388 if (navConfig()->general
.waypoint_load_on_boot
) {
4389 loadNonVolatileWaypointList(false);
4394 /*-----------------------------------------------------------
4395 * Access to estimated position/velocity data
4396 *-----------------------------------------------------------*/
4397 float getEstimatedActualVelocity(int axis
)
4399 return navGetCurrentActualPositionAndVelocity()->vel
.v
[axis
];
4402 float getEstimatedActualPosition(int axis
)
4404 return navGetCurrentActualPositionAndVelocity()->pos
.v
[axis
];
4407 /*-----------------------------------------------------------
4408 * Ability to execute RTH on external event
4409 *-----------------------------------------------------------*/
4410 void activateForcedRTH(void)
4412 abortFixedWingLaunch();
4413 posControl
.flags
.forcedRTHActivated
= true;
4414 #ifdef USE_SAFE_HOME
4415 checkSafeHomeState(true);
4417 navProcessFSMEvents(selectNavEventFromBoxModeInput());
4420 void abortForcedRTH(void)
4422 // Disable failsafe RTH and make sure we back out of navigation mode to IDLE
4423 // If any navigation mode was active prior to RTH it will be re-enabled with next RX update
4424 posControl
.flags
.forcedRTHActivated
= false;
4425 #ifdef USE_SAFE_HOME
4426 checkSafeHomeState(false);
4428 navProcessFSMEvents(NAV_FSM_EVENT_SWITCH_TO_IDLE
);
4431 rthState_e
getStateOfForcedRTH(void)
4433 /* If forced RTH activated and in AUTO_RTH or EMERG state */
4434 if (posControl
.flags
.forcedRTHActivated
&& (navGetStateFlags(posControl
.navState
) & (NAV_AUTO_RTH
| NAV_CTL_EMERG
| NAV_MIXERAT
))) {
4435 if (posControl
.navState
== NAV_STATE_RTH_FINISHED
|| posControl
.navState
== NAV_STATE_EMERGENCY_LANDING_FINISHED
) {
4436 return RTH_HAS_LANDED
;
4439 return RTH_IN_PROGRESS
;
4447 /*-----------------------------------------------------------
4448 * Ability to execute Emergency Landing on external event
4449 *-----------------------------------------------------------*/
4450 void activateForcedEmergLanding(void)
4452 abortFixedWingLaunch();
4453 posControl
.flags
.forcedEmergLandingActivated
= true;
4454 navProcessFSMEvents(selectNavEventFromBoxModeInput());
4457 void abortForcedEmergLanding(void)
4459 // Disable emergency landing and make sure we back out of navigation mode to IDLE
4460 // If any navigation mode was active prior to emergency landing it will be re-enabled with next RX update
4461 posControl
.flags
.forcedEmergLandingActivated
= false;
4462 navProcessFSMEvents(NAV_FSM_EVENT_SWITCH_TO_IDLE
);
4465 emergLandState_e
getStateOfForcedEmergLanding(void)
4467 /* If forced emergency landing activated and in EMERG state */
4468 if (posControl
.flags
.forcedEmergLandingActivated
&& (navGetStateFlags(posControl
.navState
) & NAV_CTL_EMERG
)) {
4469 if (posControl
.navState
== NAV_STATE_EMERGENCY_LANDING_FINISHED
) {
4470 return EMERG_LAND_HAS_LANDED
;
4472 return EMERG_LAND_IN_PROGRESS
;
4475 return EMERG_LAND_IDLE
;
4479 bool isWaypointMissionRTHActive(void)
4481 return (navGetStateFlags(posControl
.navState
) & NAV_AUTO_RTH
) && IS_RC_MODE_ACTIVE(BOXNAVWP
) &&
4482 !(IS_RC_MODE_ACTIVE(BOXNAVRTH
) || posControl
.flags
.forcedRTHActivated
);
4485 bool navigationIsExecutingAnEmergencyLanding(void)
4487 return navGetCurrentStateFlags() & NAV_CTL_EMERG
;
4490 bool navigationInAutomaticThrottleMode(void)
4492 navigationFSMStateFlags_t stateFlags
= navGetCurrentStateFlags();
4493 return (stateFlags
& (NAV_CTL_ALT
| NAV_CTL_EMERG
| NAV_CTL_LAND
)) ||
4494 ((stateFlags
& NAV_CTL_LAUNCH
) && !navConfig()->fw
.launch_manual_throttle
);
4497 bool navigationIsControllingThrottle(void)
4499 // Note that this makes a detour into mixer code to evaluate actual motor status
4500 return navigationInAutomaticThrottleMode() && getMotorStatus() != MOTOR_STOPPED_USER
&& !FLIGHT_MODE(SOARING_MODE
);
4503 bool navigationIsControllingAltitude(void) {
4504 navigationFSMStateFlags_t stateFlags
= navGetCurrentStateFlags();
4505 return (stateFlags
& NAV_CTL_ALT
);
4508 bool navigationIsFlyingAutonomousMode(void)
4510 navigationFSMStateFlags_t stateFlags
= navGetCurrentStateFlags();
4511 return (stateFlags
& (NAV_AUTO_RTH
| NAV_AUTO_WP
));
4514 bool navigationRTHAllowsLanding(void)
4516 // WP mission RTH landing setting
4517 if (isWaypointMissionRTHActive() && isWaypointMissionValid()) {
4518 return posControl
.waypointList
[posControl
.startWpIndex
+ posControl
.waypointCount
- 1].p1
> 0;
4521 // normal RTH landing setting
4522 navRTHAllowLanding_e allow
= navConfig()->general
.flags
.rth_allow_landing
;
4523 return allow
== NAV_RTH_ALLOW_LANDING_ALWAYS
||
4524 (allow
== NAV_RTH_ALLOW_LANDING_FS_ONLY
&& FLIGHT_MODE(FAILSAFE_MODE
));
4527 bool isNavLaunchEnabled(void)
4529 return (IS_RC_MODE_ACTIVE(BOXNAVLAUNCH
) || feature(FEATURE_FW_LAUNCH
)) && STATE(AIRPLANE
);
4532 bool abortLaunchAllowed(void)
4534 // allow NAV_LAUNCH_MODE to be aborted if throttle is low or throttle stick position is < launch idle throttle setting
4535 return throttleStickIsLow() || throttleStickMixedValue() < currentBatteryProfile
->nav
.fw
.launch_idle_throttle
;
4538 int32_t navigationGetHomeHeading(void)
4540 return posControl
.rthState
.homePosition
.heading
;
4544 float calculateAverageSpeed(void) {
4545 float flightTime
= getFlightTime();
4546 if (flightTime
== 0.0f
) return 0;
4547 return (float)getTotalTravelDistance() / (flightTime
* 100);
4550 const navigationPIDControllers_t
* getNavigationPIDControllers(void) {
4551 return &posControl
.pids
;
4554 bool isAdjustingPosition(void) {
4555 return posControl
.flags
.isAdjustingPosition
;
4558 bool isAdjustingHeading(void) {
4559 return posControl
.flags
.isAdjustingHeading
;
4562 int32_t getCruiseHeadingAdjustment(void) {
4563 return wrap_18000(posControl
.cruise
.course
- posControl
.cruise
.previousCourse
);
4566 int32_t navigationGetHeadingError(void)
4568 return wrap_18000(posControl
.desiredState
.yaw
- posControl
.actualState
.cog
);