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"
52 #include "flight/wind_estimator.h"
54 #include "io/beeper.h"
57 #include "navigation/navigation.h"
58 #include "navigation/navigation_private.h"
62 #include "sensors/sensors.h"
63 #include "sensors/acceleration.h"
64 #include "sensors/boardalignment.h"
65 #include "sensors/battery.h"
66 #include "sensors/gyro.h"
68 #include "programming/global_variables.h"
69 #include "sensors/rangefinder.h"
72 #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)
73 #define MR_RTH_CLIMB_MARGIN_MIN_CM 100 // start cruising home this amount of cm *before* reaching the cruise altitude (while continuing the ascend)
74 #define MR_RTH_CLIMB_MARGIN_PERCENT 15 // on high RTH altitudes use even bigger margin - percent of the altitude set
75 #define MR_RTH_LAND_MARGIN_CM 2000 // pause landing if this amount of cm *before* remaining to the home point (2D distance)
78 #define FW_RTH_CLIMB_OVERSHOOT_CM 100
79 #define FW_RTH_CLIMB_MARGIN_MIN_CM 100
80 #define FW_RTH_CLIMB_MARGIN_PERCENT 15
81 #define FW_LAND_LOITER_MIN_TIME 30000000 // usec (30 sec)
82 #define FW_LAND_LOITER_ALT_TOLERANCE 150
84 /*-----------------------------------------------------------
85 * Compatibility for home position
86 *-----------------------------------------------------------*/
87 gpsLocation_t GPS_home
;
88 uint32_t GPS_distanceToHome
; // distance to home point in meters
89 int16_t GPS_directionToHome
; // direction to home point in degrees
91 radar_pois_t radar_pois
[RADAR_MAX_POIS
];
93 #ifdef USE_FW_AUTOLAND
94 PG_REGISTER_WITH_RESET_TEMPLATE(navFwAutolandConfig_t
, navFwAutolandConfig
, PG_FW_AUTOLAND_CONFIG
, 0);
96 PG_REGISTER_ARRAY(navFwAutolandApproach_t
, MAX_FW_LAND_APPOACH_SETTINGS
, fwAutolandApproachConfig
, PG_FW_AUTOLAND_APPROACH_CONFIG
, 0);
98 PG_RESET_TEMPLATE(navFwAutolandConfig_t
, navFwAutolandConfig
,
99 .approachLength
= SETTING_NAV_FW_LAND_APPROACH_LENGTH_DEFAULT
,
100 .finalApproachPitchToThrottleMod
= SETTING_NAV_FW_LAND_FINAL_APPROACH_PITCH2THROTTLE_MOD_DEFAULT
,
101 .flareAltitude
= SETTING_NAV_FW_LAND_FLARE_ALT_DEFAULT
,
102 .glideAltitude
= SETTING_NAV_FW_LAND_GLIDE_ALT_DEFAULT
,
103 .flarePitch
= SETTING_NAV_FW_LAND_FLARE_PITCH_DEFAULT
,
104 .maxTailwind
= SETTING_NAV_FW_LAND_MAX_TAILWIND_DEFAULT
,
105 .glidePitch
= SETTING_NAV_FW_LAND_GLIDE_PITCH_DEFAULT
,
109 #if defined(USE_SAFE_HOME)
110 PG_REGISTER_ARRAY(navSafeHome_t
, MAX_SAFE_HOMES
, safeHomeConfig
, PG_SAFE_HOME_CONFIG
, 0);
113 // waypoint 254, 255 are special waypoints
114 STATIC_ASSERT(NAV_MAX_WAYPOINTS
< 254, NAV_MAX_WAYPOINTS_exceeded_allowable_range
);
116 #if defined(NAV_NON_VOLATILE_WAYPOINT_STORAGE)
117 PG_REGISTER_ARRAY(navWaypoint_t
, NAV_MAX_WAYPOINTS
, nonVolatileWaypointList
, PG_WAYPOINT_MISSION_STORAGE
, 2);
120 PG_REGISTER_WITH_RESET_TEMPLATE(navConfig_t
, navConfig
, PG_NAV_CONFIG
, 6);
122 PG_RESET_TEMPLATE(navConfig_t
, navConfig
,
126 .extra_arming_safety
= SETTING_NAV_EXTRA_ARMING_SAFETY_DEFAULT
,
127 .user_control_mode
= SETTING_NAV_USER_CONTROL_MODE_DEFAULT
,
128 .rth_alt_control_mode
= SETTING_NAV_RTH_ALT_MODE_DEFAULT
,
129 .rth_climb_first
= SETTING_NAV_RTH_CLIMB_FIRST_DEFAULT
, // Climb first, turn after reaching safe altitude
130 .rth_climb_first_stage_mode
= SETTING_NAV_RTH_CLIMB_FIRST_STAGE_MODE_DEFAULT
, // To determine how rth_climb_first_stage_altitude is used
131 .rth_climb_ignore_emerg
= SETTING_NAV_RTH_CLIMB_IGNORE_EMERG_DEFAULT
, // Ignore GPS loss on initial climb
132 .rth_tail_first
= SETTING_NAV_RTH_TAIL_FIRST_DEFAULT
,
133 .disarm_on_landing
= SETTING_NAV_DISARM_ON_LANDING_DEFAULT
,
134 .rth_allow_landing
= SETTING_NAV_RTH_ALLOW_LANDING_DEFAULT
,
135 .rth_alt_control_override
= SETTING_NAV_RTH_ALT_CONTROL_OVERRIDE_DEFAULT
, // Override RTH Altitude and Climb First using Pitch and Roll stick
136 .nav_overrides_motor_stop
= SETTING_NAV_OVERRIDES_MOTOR_STOP_DEFAULT
,
137 .safehome_usage_mode
= SETTING_SAFEHOME_USAGE_MODE_DEFAULT
,
138 .mission_planner_reset
= SETTING_NAV_MISSION_PLANNER_RESET_DEFAULT
, // Allow mode switch toggle to reset Mission Planner WPs
139 .waypoint_mission_restart
= SETTING_NAV_WP_MISSION_RESTART_DEFAULT
, // WP mission restart action
140 .soaring_motor_stop
= SETTING_NAV_FW_SOARING_MOTOR_STOP_DEFAULT
, // stops motor when Saoring mode enabled
141 .rth_trackback_mode
= SETTING_NAV_RTH_TRACKBACK_MODE_DEFAULT
, // RTH trackback useage mode
142 .rth_use_linear_descent
= SETTING_NAV_RTH_USE_LINEAR_DESCENT_DEFAULT
, // Use linear descent during RTH
143 .landing_bump_detection
= SETTING_NAV_LANDING_BUMP_DETECTION_DEFAULT
, // Detect landing based on touchdown G bump
146 // General navigation parameters
147 .pos_failure_timeout
= SETTING_NAV_POSITION_TIMEOUT_DEFAULT
, // 5 sec
148 .waypoint_radius
= SETTING_NAV_WP_RADIUS_DEFAULT
, // 2m diameter
149 .waypoint_safe_distance
= SETTING_NAV_WP_MAX_SAFE_DISTANCE_DEFAULT
, // Metres - first waypoint should be closer than this
150 #ifdef USE_MULTI_MISSION
151 .waypoint_multi_mission_index
= SETTING_NAV_WP_MULTI_MISSION_INDEX_DEFAULT
, // mission index selected from multi mission WP entry
153 .waypoint_load_on_boot
= SETTING_NAV_WP_LOAD_ON_BOOT_DEFAULT
, // load waypoints automatically during boot
154 .auto_speed
= SETTING_NAV_AUTO_SPEED_DEFAULT
, // speed in autonomous modes (3 m/s = 10.8 km/h)
155 .min_ground_speed
= SETTING_NAV_MIN_GROUND_SPEED_DEFAULT
, // Minimum ground speed (m/s)
156 .max_auto_speed
= SETTING_NAV_MAX_AUTO_SPEED_DEFAULT
, // max allowed speed autonomous modes
157 .max_auto_climb_rate
= SETTING_NAV_AUTO_CLIMB_RATE_DEFAULT
, // 5 m/s
158 .max_manual_speed
= SETTING_NAV_MANUAL_SPEED_DEFAULT
,
159 .max_manual_climb_rate
= SETTING_NAV_MANUAL_CLIMB_RATE_DEFAULT
,
160 .land_slowdown_minalt
= SETTING_NAV_LAND_SLOWDOWN_MINALT_DEFAULT
, // altitude in centimeters
161 .land_slowdown_maxalt
= SETTING_NAV_LAND_SLOWDOWN_MAXALT_DEFAULT
, // altitude in meters
162 .land_minalt_vspd
= SETTING_NAV_LAND_MINALT_VSPD_DEFAULT
, // centimeters/s
163 .land_maxalt_vspd
= SETTING_NAV_LAND_MAXALT_VSPD_DEFAULT
, // centimeters/s
164 .emerg_descent_rate
= SETTING_NAV_EMERG_LANDING_SPEED_DEFAULT
, // centimeters/s
165 .min_rth_distance
= SETTING_NAV_MIN_RTH_DISTANCE_DEFAULT
, // centimeters, if closer than this land immediately
166 .rth_altitude
= SETTING_NAV_RTH_ALTITUDE_DEFAULT
, // altitude in centimeters
167 .rth_home_altitude
= SETTING_NAV_RTH_HOME_ALTITUDE_DEFAULT
, // altitude in centimeters
168 .rth_climb_first_stage_altitude
= SETTING_NAV_RTH_CLIMB_FIRST_STAGE_ALTITUDE_DEFAULT
, // altitude in centimetres, 0= off
169 .rth_abort_threshold
= SETTING_NAV_RTH_ABORT_THRESHOLD_DEFAULT
, // centimeters - 500m should be safe for all aircraft
170 .max_terrain_follow_altitude
= SETTING_NAV_MAX_TERRAIN_FOLLOW_ALT_DEFAULT
, // max altitude in centimeters in terrain following mode
171 .safehome_max_distance
= SETTING_SAFEHOME_MAX_DISTANCE_DEFAULT
, // Max distance that a safehome is from the arming point
172 .max_altitude
= SETTING_NAV_MAX_ALTITUDE_DEFAULT
,
173 .rth_trackback_distance
= SETTING_NAV_RTH_TRACKBACK_DISTANCE_DEFAULT
, // Max distance allowed for RTH trackback
174 .waypoint_enforce_altitude
= SETTING_NAV_WP_ENFORCE_ALTITUDE_DEFAULT
, // Forces set wp altitude to be achieved
175 .land_detect_sensitivity
= SETTING_NAV_LAND_DETECT_SENSITIVITY_DEFAULT
, // Changes sensitivity of landing detection
176 .auto_disarm_delay
= SETTING_NAV_AUTO_DISARM_DELAY_DEFAULT
, // 2000 ms - time delay to disarm when auto disarm after landing enabled
177 .rth_linear_descent_start_distance
= SETTING_NAV_RTH_LINEAR_DESCENT_START_DISTANCE_DEFAULT
,
178 .cruise_yaw_rate
= SETTING_NAV_CRUISE_YAW_RATE_DEFAULT
, // 20dps
179 .rth_fs_landing_delay
= SETTING_NAV_RTH_FS_LANDING_DELAY_DEFAULT
, // Delay before landing in FS. 0 = immedate landing
184 .max_bank_angle
= SETTING_NAV_MC_BANK_ANGLE_DEFAULT
, // degrees
186 #ifdef USE_MR_BRAKING_MODE
187 .braking_speed_threshold
= SETTING_NAV_MC_BRAKING_SPEED_THRESHOLD_DEFAULT
, // Braking can become active above 1m/s
188 .braking_disengage_speed
= SETTING_NAV_MC_BRAKING_DISENGAGE_SPEED_DEFAULT
, // Stop when speed goes below 0.75m/s
189 .braking_timeout
= SETTING_NAV_MC_BRAKING_TIMEOUT_DEFAULT
, // Timeout barking after 2s
190 .braking_boost_factor
= SETTING_NAV_MC_BRAKING_BOOST_FACTOR_DEFAULT
, // A 100% boost by default
191 .braking_boost_timeout
= SETTING_NAV_MC_BRAKING_BOOST_TIMEOUT_DEFAULT
, // Timout boost after 750ms
192 .braking_boost_speed_threshold
= SETTING_NAV_MC_BRAKING_BOOST_SPEED_THRESHOLD_DEFAULT
, // Boost can happen only above 1.5m/s
193 .braking_boost_disengage_speed
= SETTING_NAV_MC_BRAKING_BOOST_DISENGAGE_SPEED_DEFAULT
, // Disable boost at 1m/s
194 .braking_bank_angle
= SETTING_NAV_MC_BRAKING_BANK_ANGLE_DEFAULT
, // Max braking angle
197 .posDecelerationTime
= SETTING_NAV_MC_POS_DECELERATION_TIME_DEFAULT
, // posDecelerationTime * 100
198 .posResponseExpo
= SETTING_NAV_MC_POS_EXPO_DEFAULT
, // posResponseExpo * 100
199 .slowDownForTurning
= SETTING_NAV_MC_WP_SLOWDOWN_DEFAULT
,
200 .althold_throttle_type
= SETTING_NAV_MC_ALTHOLD_THROTTLE_DEFAULT
, // STICK
205 .max_bank_angle
= SETTING_NAV_FW_BANK_ANGLE_DEFAULT
, // degrees
206 .max_climb_angle
= SETTING_NAV_FW_CLIMB_ANGLE_DEFAULT
, // degrees
207 .max_dive_angle
= SETTING_NAV_FW_DIVE_ANGLE_DEFAULT
, // degrees
208 .cruise_speed
= SETTING_NAV_FW_CRUISE_SPEED_DEFAULT
, // cm/s
209 .control_smoothness
= SETTING_NAV_FW_CONTROL_SMOOTHNESS_DEFAULT
,
210 .pitch_to_throttle_smooth
= SETTING_NAV_FW_PITCH2THR_SMOOTHING_DEFAULT
,
211 .pitch_to_throttle_thresh
= SETTING_NAV_FW_PITCH2THR_THRESHOLD_DEFAULT
,
212 .minThrottleDownPitchAngle
= SETTING_FW_MIN_THROTTLE_DOWN_PITCH_DEFAULT
,
213 .loiter_radius
= SETTING_NAV_FW_LOITER_RADIUS_DEFAULT
, // 75m
214 .loiter_direction
= SETTING_FW_LOITER_DIRECTION_DEFAULT
,
217 .land_dive_angle
= SETTING_NAV_FW_LAND_DIVE_ANGLE_DEFAULT
, // 2 degrees dive by default
220 .launch_velocity_thresh
= SETTING_NAV_FW_LAUNCH_VELOCITY_DEFAULT
, // 3 m/s
221 .launch_accel_thresh
= SETTING_NAV_FW_LAUNCH_ACCEL_DEFAULT
, // cm/s/s (1.9*G)
222 .launch_time_thresh
= SETTING_NAV_FW_LAUNCH_DETECT_TIME_DEFAULT
, // 40ms
223 .launch_motor_timer
= SETTING_NAV_FW_LAUNCH_MOTOR_DELAY_DEFAULT
, // ms
224 .launch_idle_motor_timer
= SETTING_NAV_FW_LAUNCH_IDLE_MOTOR_DELAY_DEFAULT
, // ms
225 .launch_motor_spinup_time
= SETTING_NAV_FW_LAUNCH_SPINUP_TIME_DEFAULT
, // ms, time to gredually increase throttle from idle to launch
226 .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
227 .launch_min_time
= SETTING_NAV_FW_LAUNCH_MIN_TIME_DEFAULT
, // ms, min time in launch mode
228 .launch_timeout
= SETTING_NAV_FW_LAUNCH_TIMEOUT_DEFAULT
, // ms, timeout for launch procedure
229 .launch_max_altitude
= SETTING_NAV_FW_LAUNCH_MAX_ALTITUDE_DEFAULT
, // cm, altitude where to consider launch ended
230 .launch_climb_angle
= SETTING_NAV_FW_LAUNCH_CLIMB_ANGLE_DEFAULT
, // 18 degrees
231 .launch_max_angle
= SETTING_NAV_FW_LAUNCH_MAX_ANGLE_DEFAULT
, // 45 deg
232 .launch_manual_throttle
= SETTING_NAV_FW_LAUNCH_MANUAL_THROTTLE_DEFAULT
,// OFF
233 .launch_land_abort_deadband
= SETTING_NAV_FW_LAUNCH_LAND_ABORT_DEADBAND_DEFAULT
, // 100 us
235 .allow_manual_thr_increase
= SETTING_NAV_FW_ALLOW_MANUAL_THR_INCREASE_DEFAULT
,
236 .useFwNavYawControl
= SETTING_NAV_USE_FW_YAW_CONTROL_DEFAULT
,
237 .yawControlDeadband
= SETTING_NAV_FW_YAW_DEADBAND_DEFAULT
,
238 .soaring_pitch_deadband
= SETTING_NAV_FW_SOARING_PITCH_DEADBAND_DEFAULT
,// pitch angle mode deadband when Saoring mode enabled
239 .wp_tracking_accuracy
= SETTING_NAV_FW_WP_TRACKING_ACCURACY_DEFAULT
, // 0, improves course tracking accuracy during FW WP missions
240 .wp_tracking_max_angle
= SETTING_NAV_FW_WP_TRACKING_MAX_ANGLE_DEFAULT
, // 60 degs
241 .wp_turn_smoothing
= SETTING_NAV_FW_WP_TURN_SMOOTHING_DEFAULT
, // 0, smooths turns during FW WP mode missions
246 static navWapointHeading_t wpHeadingControl
;
247 navigationPosControl_t posControl
;
248 navSystemStatus_t NAV_Status
;
249 static bool landingDetectorIsActive
;
251 EXTENDED_FASTRAM multicopterPosXyCoefficients_t multicopterPosXyCoefficients
;
254 int16_t navCurrentState
;
255 int16_t navActualVelocity
[3];
256 int16_t navDesiredVelocity
[3];
257 int32_t navTargetPosition
[3];
258 int32_t navLatestActualPosition
[3];
259 int16_t navActualHeading
;
260 uint16_t navDesiredHeading
;
261 int16_t navActualSurface
;
265 int16_t navAccNEU
[3];
266 //End of blackbox states
268 static fpVector3_t
* rthGetHomeTargetPosition(rthTargetMode_e mode
);
269 static void updateDesiredRTHAltitude(void);
270 static void resetAltitudeController(bool useTerrainFollowing
);
271 static void resetPositionController(void);
272 static void setupAltitudeController(void);
273 static void resetHeadingController(void);
275 #ifdef USE_FW_AUTOLAND
276 static void resetFwAutoland(void);
279 void resetGCSFlags(void);
281 static void setupJumpCounters(void);
282 static void resetJumpCounter(void);
283 static void clearJumpCounters(void);
285 static void calculateAndSetActiveWaypoint(const navWaypoint_t
* waypoint
);
286 static void calculateAndSetActiveWaypointToLocalPosition(const fpVector3_t
* pos
);
287 void calculateInitialHoldPosition(fpVector3_t
* pos
);
288 void calculateFarAwayPos(fpVector3_t
* farAwayPos
, const fpVector3_t
*start
, int32_t bearing
, int32_t distance
);
289 void calculateFarAwayTarget(fpVector3_t
* farAwayPos
, int32_t bearing
, int32_t distance
);
290 static bool isWaypointReached(const fpVector3_t
* waypointPos
, const int32_t * waypointBearing
);
291 bool isWaypointAltitudeReached(void);
292 static void mapWaypointToLocalPosition(fpVector3_t
* localPos
, const navWaypoint_t
* waypoint
, geoAltitudeConversionMode_e altConv
);
293 static navigationFSMEvent_t
nextForNonGeoStates(void);
294 static bool isWaypointMissionValid(void);
295 void missionPlannerSetWaypoint(void);
297 void initializeRTHSanityChecker(void);
298 bool validateRTHSanityChecker(void);
299 void updateHomePosition(void);
300 bool abortLaunchAllowed(void);
302 static bool rthAltControlStickOverrideCheck(unsigned axis
);
304 static void updateRthTrackback(bool forceSaveTrackPoint
);
305 static fpVector3_t
* rthGetTrackbackPos(void);
307 #ifdef USE_FW_AUTOLAND
308 static float getLandAltitude(void);
309 static int32_t calcWindDiff(int32_t heading
, int32_t windHeading
);
310 static int32_t calcFinalApproachHeading(int32_t approachHeading
, int32_t windAngle
);
311 static void setLandWaypoint(const fpVector3_t
*pos
, const fpVector3_t
*nextWpPos
);
314 /*************************************************************************************************/
315 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_IDLE(navigationFSMState_t previousState
);
316 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_ALTHOLD_INITIALIZE(navigationFSMState_t previousState
);
317 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_ALTHOLD_IN_PROGRESS(navigationFSMState_t previousState
);
318 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_POSHOLD_3D_INITIALIZE(navigationFSMState_t previousState
);
319 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_POSHOLD_3D_IN_PROGRESS(navigationFSMState_t previousState
);
320 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_COURSE_HOLD_INITIALIZE(navigationFSMState_t previousState
);
321 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_COURSE_HOLD_IN_PROGRESS(navigationFSMState_t previousState
);
322 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_COURSE_HOLD_ADJUSTING(navigationFSMState_t previousState
);
323 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_CRUISE_INITIALIZE(navigationFSMState_t previousState
);
324 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_CRUISE_IN_PROGRESS(navigationFSMState_t previousState
);
325 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_CRUISE_ADJUSTING(navigationFSMState_t previousState
);
326 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_INITIALIZE(navigationFSMState_t previousState
);
327 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_CLIMB_TO_SAFE_ALT(navigationFSMState_t previousState
);
328 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_TRACKBACK(navigationFSMState_t previousState
);
329 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_HEAD_HOME(navigationFSMState_t previousState
);
330 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_HOVER_PRIOR_TO_LANDING(navigationFSMState_t previousState
);
331 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_HOVER_ABOVE_HOME(navigationFSMState_t previousState
);
332 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_LANDING(navigationFSMState_t previousState
);
333 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_FINISHING(navigationFSMState_t previousState
);
334 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_FINISHED(navigationFSMState_t previousState
);
335 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_WAYPOINT_INITIALIZE(navigationFSMState_t previousState
);
336 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_WAYPOINT_PRE_ACTION(navigationFSMState_t previousState
);
337 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_WAYPOINT_IN_PROGRESS(navigationFSMState_t previousState
);
338 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_WAYPOINT_REACHED(navigationFSMState_t previousState
);
339 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_WAYPOINT_HOLD_TIME(navigationFSMState_t previousState
);
340 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_WAYPOINT_NEXT(navigationFSMState_t previousState
);
341 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_WAYPOINT_FINISHED(navigationFSMState_t previousState
);
342 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_WAYPOINT_RTH_LAND(navigationFSMState_t previousState
);
343 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_EMERGENCY_LANDING_INITIALIZE(navigationFSMState_t previousState
);
344 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_EMERGENCY_LANDING_IN_PROGRESS(navigationFSMState_t previousState
);
345 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_EMERGENCY_LANDING_FINISHED(navigationFSMState_t previousState
);
346 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_LAUNCH_INITIALIZE(navigationFSMState_t previousState
);
347 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_LAUNCH_WAIT(navigationFSMState_t previousState
);
348 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_LAUNCH_IN_PROGRESS(navigationFSMState_t previousState
);
349 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_MIXERAT_INITIALIZE(navigationFSMState_t previousState
);
350 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_MIXERAT_IN_PROGRESS(navigationFSMState_t previousState
);
351 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_MIXERAT_ABORT(navigationFSMState_t previousState
);
352 #ifdef USE_FW_AUTOLAND
353 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_FW_LANDING_CLIMB_TO_LOITER(navigationFSMState_t previousState
);
354 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_FW_LANDING_LOITER(navigationFSMState_t previousState
);
355 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_FW_LANDING_APPROACH(navigationFSMState_t previousState
);
356 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_FW_LANDING_GLIDE(navigationFSMState_t previousState
);
357 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_FW_LANDING_FLARE(navigationFSMState_t previousState
);
358 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_FW_LANDING_ABORT(navigationFSMState_t previousState
);
361 static const navigationFSMStateDescriptor_t navFSM
[NAV_STATE_COUNT
] = {
362 /** Idle state ******************************************************/
364 .persistentId
= NAV_PERSISTENT_ID_IDLE
,
365 .onEntry
= navOnEnteringState_NAV_STATE_IDLE
,
368 .mapToFlightModes
= 0,
369 .mwState
= MW_NAV_STATE_NONE
,
370 .mwError
= MW_NAV_ERROR_NONE
,
372 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
373 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
374 [NAV_FSM_EVENT_SWITCH_TO_RTH
] = NAV_STATE_RTH_INITIALIZE
,
375 [NAV_FSM_EVENT_SWITCH_TO_WAYPOINT
] = NAV_STATE_WAYPOINT_INITIALIZE
,
376 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
377 [NAV_FSM_EVENT_SWITCH_TO_LAUNCH
] = NAV_STATE_LAUNCH_INITIALIZE
,
378 [NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD
] = NAV_STATE_COURSE_HOLD_INITIALIZE
,
379 [NAV_FSM_EVENT_SWITCH_TO_CRUISE
] = NAV_STATE_CRUISE_INITIALIZE
,
380 [NAV_FSM_EVENT_SWITCH_TO_MIXERAT
] = NAV_STATE_MIXERAT_INITIALIZE
,
384 /** ALTHOLD mode ***************************************************/
385 [NAV_STATE_ALTHOLD_INITIALIZE
] = {
386 .persistentId
= NAV_PERSISTENT_ID_ALTHOLD_INITIALIZE
,
387 .onEntry
= navOnEnteringState_NAV_STATE_ALTHOLD_INITIALIZE
,
389 .stateFlags
= NAV_CTL_ALT
| NAV_REQUIRE_ANGLE_FW
| NAV_REQUIRE_THRTILT
,
390 .mapToFlightModes
= NAV_ALTHOLD_MODE
,
391 .mwState
= MW_NAV_STATE_NONE
,
392 .mwError
= MW_NAV_ERROR_NONE
,
394 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_ALTHOLD_IN_PROGRESS
,
395 [NAV_FSM_EVENT_ERROR
] = NAV_STATE_IDLE
,
396 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
400 [NAV_STATE_ALTHOLD_IN_PROGRESS
] = {
401 .persistentId
= NAV_PERSISTENT_ID_ALTHOLD_IN_PROGRESS
,
402 .onEntry
= navOnEnteringState_NAV_STATE_ALTHOLD_IN_PROGRESS
,
404 .stateFlags
= NAV_CTL_ALT
| NAV_REQUIRE_ANGLE_FW
| NAV_REQUIRE_THRTILT
| NAV_RC_ALT
,
405 .mapToFlightModes
= NAV_ALTHOLD_MODE
,
406 .mwState
= MW_NAV_STATE_NONE
,
407 .mwError
= MW_NAV_ERROR_NONE
,
409 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_ALTHOLD_IN_PROGRESS
, // re-process the state
410 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
411 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
412 [NAV_FSM_EVENT_SWITCH_TO_RTH
] = NAV_STATE_RTH_INITIALIZE
,
413 [NAV_FSM_EVENT_SWITCH_TO_WAYPOINT
] = NAV_STATE_WAYPOINT_INITIALIZE
,
414 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
415 [NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD
] = NAV_STATE_COURSE_HOLD_INITIALIZE
,
416 [NAV_FSM_EVENT_SWITCH_TO_CRUISE
] = NAV_STATE_CRUISE_INITIALIZE
,
420 /** POSHOLD_3D mode ************************************************/
421 [NAV_STATE_POSHOLD_3D_INITIALIZE
] = {
422 .persistentId
= NAV_PERSISTENT_ID_POSHOLD_3D_INITIALIZE
,
423 .onEntry
= navOnEnteringState_NAV_STATE_POSHOLD_3D_INITIALIZE
,
425 .stateFlags
= NAV_CTL_ALT
| NAV_CTL_POS
| NAV_REQUIRE_ANGLE
| NAV_REQUIRE_THRTILT
,
426 .mapToFlightModes
= NAV_ALTHOLD_MODE
| NAV_POSHOLD_MODE
,
427 .mwState
= MW_NAV_STATE_HOLD_INFINIT
,
428 .mwError
= MW_NAV_ERROR_NONE
,
430 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_POSHOLD_3D_IN_PROGRESS
,
431 [NAV_FSM_EVENT_ERROR
] = NAV_STATE_IDLE
,
432 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
436 [NAV_STATE_POSHOLD_3D_IN_PROGRESS
] = {
437 .persistentId
= NAV_PERSISTENT_ID_POSHOLD_3D_IN_PROGRESS
,
438 .onEntry
= navOnEnteringState_NAV_STATE_POSHOLD_3D_IN_PROGRESS
,
440 .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
,
441 .mapToFlightModes
= NAV_ALTHOLD_MODE
| NAV_POSHOLD_MODE
,
442 .mwState
= MW_NAV_STATE_HOLD_INFINIT
,
443 .mwError
= MW_NAV_ERROR_NONE
,
445 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_POSHOLD_3D_IN_PROGRESS
, // re-process the state
446 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
447 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
448 [NAV_FSM_EVENT_SWITCH_TO_RTH
] = NAV_STATE_RTH_INITIALIZE
,
449 [NAV_FSM_EVENT_SWITCH_TO_WAYPOINT
] = NAV_STATE_WAYPOINT_INITIALIZE
,
450 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
451 [NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD
] = NAV_STATE_COURSE_HOLD_INITIALIZE
,
452 [NAV_FSM_EVENT_SWITCH_TO_CRUISE
] = NAV_STATE_CRUISE_INITIALIZE
,
455 /** CRUISE_HOLD mode ************************************************/
456 [NAV_STATE_COURSE_HOLD_INITIALIZE
] = {
457 .persistentId
= NAV_PERSISTENT_ID_COURSE_HOLD_INITIALIZE
,
458 .onEntry
= navOnEnteringState_NAV_STATE_COURSE_HOLD_INITIALIZE
,
460 .stateFlags
= NAV_REQUIRE_ANGLE
,
461 .mapToFlightModes
= NAV_COURSE_HOLD_MODE
,
462 .mwState
= MW_NAV_STATE_NONE
,
463 .mwError
= MW_NAV_ERROR_NONE
,
465 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_COURSE_HOLD_IN_PROGRESS
,
466 [NAV_FSM_EVENT_ERROR
] = NAV_STATE_IDLE
,
467 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
471 [NAV_STATE_COURSE_HOLD_IN_PROGRESS
] = {
472 .persistentId
= NAV_PERSISTENT_ID_COURSE_HOLD_IN_PROGRESS
,
473 .onEntry
= navOnEnteringState_NAV_STATE_COURSE_HOLD_IN_PROGRESS
,
475 .stateFlags
= NAV_CTL_POS
| NAV_CTL_YAW
| NAV_REQUIRE_ANGLE
| NAV_REQUIRE_MAGHOLD
| NAV_RC_POS
| NAV_RC_YAW
,
476 .mapToFlightModes
= NAV_COURSE_HOLD_MODE
,
477 .mwState
= MW_NAV_STATE_NONE
,
478 .mwError
= MW_NAV_ERROR_NONE
,
480 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_COURSE_HOLD_IN_PROGRESS
, // re-process the state
481 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
482 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
483 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
484 [NAV_FSM_EVENT_SWITCH_TO_CRUISE
] = NAV_STATE_CRUISE_INITIALIZE
,
485 [NAV_FSM_EVENT_SWITCH_TO_COURSE_ADJ
] = NAV_STATE_COURSE_HOLD_ADJUSTING
,
486 [NAV_FSM_EVENT_SWITCH_TO_RTH
] = NAV_STATE_RTH_INITIALIZE
,
487 [NAV_FSM_EVENT_SWITCH_TO_WAYPOINT
] = NAV_STATE_WAYPOINT_INITIALIZE
,
488 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
492 [NAV_STATE_COURSE_HOLD_ADJUSTING
] = {
493 .persistentId
= NAV_PERSISTENT_ID_COURSE_HOLD_ADJUSTING
,
494 .onEntry
= navOnEnteringState_NAV_STATE_COURSE_HOLD_ADJUSTING
,
496 .stateFlags
= NAV_REQUIRE_ANGLE
| NAV_RC_POS
,
497 .mapToFlightModes
= NAV_COURSE_HOLD_MODE
,
498 .mwState
= MW_NAV_STATE_NONE
,
499 .mwError
= MW_NAV_ERROR_NONE
,
501 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_COURSE_HOLD_IN_PROGRESS
,
502 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_COURSE_HOLD_ADJUSTING
,
503 [NAV_FSM_EVENT_ERROR
] = NAV_STATE_IDLE
,
504 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
505 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
506 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
507 [NAV_FSM_EVENT_SWITCH_TO_CRUISE
] = NAV_STATE_CRUISE_INITIALIZE
,
508 [NAV_FSM_EVENT_SWITCH_TO_RTH
] = NAV_STATE_RTH_INITIALIZE
,
509 [NAV_FSM_EVENT_SWITCH_TO_WAYPOINT
] = NAV_STATE_WAYPOINT_INITIALIZE
,
510 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
514 /** CRUISE_3D mode ************************************************/
515 [NAV_STATE_CRUISE_INITIALIZE
] = {
516 .persistentId
= NAV_PERSISTENT_ID_CRUISE_INITIALIZE
,
517 .onEntry
= navOnEnteringState_NAV_STATE_CRUISE_INITIALIZE
,
519 .stateFlags
= NAV_REQUIRE_ANGLE
,
520 .mapToFlightModes
= NAV_ALTHOLD_MODE
| NAV_COURSE_HOLD_MODE
,
521 .mwState
= MW_NAV_STATE_NONE
,
522 .mwError
= MW_NAV_ERROR_NONE
,
524 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_CRUISE_IN_PROGRESS
,
525 [NAV_FSM_EVENT_ERROR
] = NAV_STATE_IDLE
,
526 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
530 [NAV_STATE_CRUISE_IN_PROGRESS
] = {
531 .persistentId
= NAV_PERSISTENT_ID_CRUISE_IN_PROGRESS
,
532 .onEntry
= navOnEnteringState_NAV_STATE_CRUISE_IN_PROGRESS
,
534 .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
,
535 .mapToFlightModes
= NAV_ALTHOLD_MODE
| NAV_COURSE_HOLD_MODE
,
536 .mwState
= MW_NAV_STATE_NONE
,
537 .mwError
= MW_NAV_ERROR_NONE
,
539 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_CRUISE_IN_PROGRESS
, // re-process the state
540 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
541 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
542 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
543 [NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD
] = NAV_STATE_COURSE_HOLD_INITIALIZE
,
544 [NAV_FSM_EVENT_SWITCH_TO_COURSE_ADJ
] = NAV_STATE_CRUISE_ADJUSTING
,
545 [NAV_FSM_EVENT_SWITCH_TO_RTH
] = NAV_STATE_RTH_INITIALIZE
,
546 [NAV_FSM_EVENT_SWITCH_TO_WAYPOINT
] = NAV_STATE_WAYPOINT_INITIALIZE
,
547 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
551 [NAV_STATE_CRUISE_ADJUSTING
] = {
552 .persistentId
= NAV_PERSISTENT_ID_CRUISE_ADJUSTING
,
553 .onEntry
= navOnEnteringState_NAV_STATE_CRUISE_ADJUSTING
,
555 .stateFlags
= NAV_CTL_ALT
| NAV_REQUIRE_ANGLE
| NAV_RC_POS
| NAV_RC_ALT
,
556 .mapToFlightModes
= NAV_ALTHOLD_MODE
| NAV_COURSE_HOLD_MODE
,
557 .mwState
= MW_NAV_STATE_NONE
,
558 .mwError
= MW_NAV_ERROR_NONE
,
560 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_CRUISE_IN_PROGRESS
,
561 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_CRUISE_ADJUSTING
,
562 [NAV_FSM_EVENT_ERROR
] = NAV_STATE_IDLE
,
563 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
564 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
565 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
566 [NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD
] = NAV_STATE_COURSE_HOLD_INITIALIZE
,
567 [NAV_FSM_EVENT_SWITCH_TO_RTH
] = NAV_STATE_RTH_INITIALIZE
,
568 [NAV_FSM_EVENT_SWITCH_TO_WAYPOINT
] = NAV_STATE_WAYPOINT_INITIALIZE
,
569 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
573 /** RTH_3D mode ************************************************/
574 [NAV_STATE_RTH_INITIALIZE
] = {
575 .persistentId
= NAV_PERSISTENT_ID_RTH_INITIALIZE
,
576 .onEntry
= navOnEnteringState_NAV_STATE_RTH_INITIALIZE
,
578 .stateFlags
= NAV_CTL_ALT
| NAV_CTL_POS
| NAV_CTL_YAW
| NAV_REQUIRE_ANGLE
| NAV_REQUIRE_MAGHOLD
| NAV_REQUIRE_THRTILT
| NAV_AUTO_RTH
,
579 .mapToFlightModes
= NAV_RTH_MODE
| NAV_ALTHOLD_MODE
,
580 .mwState
= MW_NAV_STATE_RTH_START
,
581 .mwError
= MW_NAV_ERROR_NONE
,
583 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_RTH_INITIALIZE
, // re-process the state
584 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_RTH_CLIMB_TO_SAFE_ALT
,
585 [NAV_FSM_EVENT_SWITCH_TO_NAV_STATE_RTH_TRACKBACK
] = NAV_STATE_RTH_TRACKBACK
,
586 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
587 [NAV_FSM_EVENT_SWITCH_TO_RTH_LANDING
] = NAV_STATE_RTH_HOVER_PRIOR_TO_LANDING
,
588 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
592 [NAV_STATE_RTH_CLIMB_TO_SAFE_ALT
] = {
593 .persistentId
= NAV_PERSISTENT_ID_RTH_CLIMB_TO_SAFE_ALT
,
594 .onEntry
= navOnEnteringState_NAV_STATE_RTH_CLIMB_TO_SAFE_ALT
,
596 .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
597 .mapToFlightModes
= NAV_RTH_MODE
| NAV_ALTHOLD_MODE
,
598 .mwState
= MW_NAV_STATE_RTH_CLIMB
,
599 .mwError
= MW_NAV_ERROR_WAIT_FOR_RTH_ALT
,
601 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_RTH_CLIMB_TO_SAFE_ALT
, // re-process the state
602 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_RTH_HEAD_HOME
,
603 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
604 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
605 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
606 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
607 [NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD
] = NAV_STATE_COURSE_HOLD_INITIALIZE
,
608 [NAV_FSM_EVENT_SWITCH_TO_CRUISE
] = NAV_STATE_CRUISE_INITIALIZE
,
612 [NAV_STATE_RTH_TRACKBACK
] = {
613 .persistentId
= NAV_PERSISTENT_ID_RTH_TRACKBACK
,
614 .onEntry
= navOnEnteringState_NAV_STATE_RTH_TRACKBACK
,
616 .stateFlags
= NAV_CTL_ALT
| NAV_CTL_POS
| NAV_CTL_YAW
| NAV_REQUIRE_ANGLE
| NAV_REQUIRE_MAGHOLD
| NAV_REQUIRE_THRTILT
| NAV_AUTO_RTH
,
617 .mapToFlightModes
= NAV_RTH_MODE
| NAV_ALTHOLD_MODE
,
618 .mwState
= MW_NAV_STATE_RTH_ENROUTE
,
619 .mwError
= MW_NAV_ERROR_NONE
,
621 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_RTH_TRACKBACK
, // re-process the state
622 [NAV_FSM_EVENT_SWITCH_TO_NAV_STATE_RTH_INITIALIZE
] = NAV_STATE_RTH_INITIALIZE
,
623 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
624 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
625 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
626 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
627 [NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD
] = NAV_STATE_COURSE_HOLD_INITIALIZE
,
628 [NAV_FSM_EVENT_SWITCH_TO_CRUISE
] = NAV_STATE_CRUISE_INITIALIZE
,
632 [NAV_STATE_RTH_HEAD_HOME
] = {
633 .persistentId
= NAV_PERSISTENT_ID_RTH_HEAD_HOME
,
634 .onEntry
= navOnEnteringState_NAV_STATE_RTH_HEAD_HOME
,
636 .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
,
637 .mapToFlightModes
= NAV_RTH_MODE
| NAV_ALTHOLD_MODE
,
638 .mwState
= MW_NAV_STATE_RTH_ENROUTE
,
639 .mwError
= MW_NAV_ERROR_NONE
,
641 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_RTH_HEAD_HOME
, // re-process the state
642 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_RTH_HOVER_PRIOR_TO_LANDING
,
643 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
644 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
645 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
646 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
647 [NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD
] = NAV_STATE_COURSE_HOLD_INITIALIZE
,
648 [NAV_FSM_EVENT_SWITCH_TO_CRUISE
] = NAV_STATE_CRUISE_INITIALIZE
,
649 [NAV_FSM_EVENT_SWITCH_TO_MIXERAT
] = NAV_STATE_MIXERAT_INITIALIZE
,
653 [NAV_STATE_RTH_HOVER_PRIOR_TO_LANDING
] = {
654 .persistentId
= NAV_PERSISTENT_ID_RTH_HOVER_PRIOR_TO_LANDING
,
655 .onEntry
= navOnEnteringState_NAV_STATE_RTH_HOVER_PRIOR_TO_LANDING
,
657 .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
,
658 .mapToFlightModes
= NAV_RTH_MODE
| NAV_ALTHOLD_MODE
,
659 .mwState
= MW_NAV_STATE_LAND_SETTLE
,
660 .mwError
= MW_NAV_ERROR_NONE
,
662 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_RTH_HOVER_PRIOR_TO_LANDING
,
663 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_RTH_LANDING
,
664 [NAV_FSM_EVENT_SWITCH_TO_RTH_HOVER_ABOVE_HOME
] = NAV_STATE_RTH_HOVER_ABOVE_HOME
,
665 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
666 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
667 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
668 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
669 [NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD
] = NAV_STATE_COURSE_HOLD_INITIALIZE
,
670 [NAV_FSM_EVENT_SWITCH_TO_CRUISE
] = NAV_STATE_CRUISE_INITIALIZE
,
674 [NAV_STATE_RTH_HOVER_ABOVE_HOME
] = {
675 .persistentId
= NAV_PERSISTENT_ID_RTH_HOVER_ABOVE_HOME
,
676 .onEntry
= navOnEnteringState_NAV_STATE_RTH_HOVER_ABOVE_HOME
,
678 .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
,
679 .mapToFlightModes
= NAV_RTH_MODE
| NAV_ALTHOLD_MODE
,
680 .mwState
= MW_NAV_STATE_HOVER_ABOVE_HOME
,
681 .mwError
= MW_NAV_ERROR_NONE
,
683 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_RTH_HOVER_ABOVE_HOME
,
684 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
685 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
686 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
687 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
688 [NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD
] = NAV_STATE_COURSE_HOLD_INITIALIZE
,
689 [NAV_FSM_EVENT_SWITCH_TO_CRUISE
] = NAV_STATE_CRUISE_INITIALIZE
,
693 [NAV_STATE_RTH_LANDING
] = {
694 .persistentId
= NAV_PERSISTENT_ID_RTH_LANDING
,
695 .onEntry
= navOnEnteringState_NAV_STATE_RTH_LANDING
,
697 .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
,
698 .mapToFlightModes
= NAV_RTH_MODE
| NAV_ALTHOLD_MODE
,
699 .mwState
= MW_NAV_STATE_LAND_IN_PROGRESS
,
700 .mwError
= MW_NAV_ERROR_LANDING
,
702 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_RTH_LANDING
, // re-process state
703 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_RTH_FINISHING
,
704 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
705 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
706 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
707 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
708 [NAV_FSM_EVENT_SWITCH_TO_MIXERAT
] = NAV_STATE_MIXERAT_INITIALIZE
,
709 [NAV_FSM_EVENT_SWITCH_TO_NAV_STATE_FW_LANDING
] = NAV_STATE_FW_LANDING_CLIMB_TO_LOITER
,
710 [NAV_FSM_EVENT_SWITCH_TO_RTH_HOVER_ABOVE_HOME
] = NAV_STATE_RTH_HOVER_ABOVE_HOME
,
714 [NAV_STATE_RTH_FINISHING
] = {
715 .persistentId
= NAV_PERSISTENT_ID_RTH_FINISHING
,
716 .onEntry
= navOnEnteringState_NAV_STATE_RTH_FINISHING
,
718 .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
,
719 .mapToFlightModes
= NAV_RTH_MODE
| NAV_ALTHOLD_MODE
,
720 .mwState
= MW_NAV_STATE_LAND_IN_PROGRESS
,
721 .mwError
= MW_NAV_ERROR_LANDING
,
723 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_RTH_FINISHED
,
724 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
728 [NAV_STATE_RTH_FINISHED
] = {
729 .persistentId
= NAV_PERSISTENT_ID_RTH_FINISHED
,
730 .onEntry
= navOnEnteringState_NAV_STATE_RTH_FINISHED
,
732 .stateFlags
= NAV_CTL_ALT
| NAV_CTL_LAND
| NAV_REQUIRE_ANGLE
| NAV_REQUIRE_THRTILT
| NAV_AUTO_RTH
,
733 .mapToFlightModes
= NAV_RTH_MODE
| NAV_ALTHOLD_MODE
,
734 .mwState
= MW_NAV_STATE_LANDED
,
735 .mwError
= MW_NAV_ERROR_NONE
,
737 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_RTH_FINISHED
, // re-process state
738 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
739 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
740 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
741 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
745 /** WAYPOINT mode ************************************************/
746 [NAV_STATE_WAYPOINT_INITIALIZE
] = {
747 .persistentId
= NAV_PERSISTENT_ID_WAYPOINT_INITIALIZE
,
748 .onEntry
= navOnEnteringState_NAV_STATE_WAYPOINT_INITIALIZE
,
750 .stateFlags
= NAV_CTL_ALT
| NAV_CTL_POS
| NAV_CTL_YAW
| NAV_REQUIRE_ANGLE
| NAV_REQUIRE_MAGHOLD
| NAV_REQUIRE_THRTILT
| NAV_AUTO_WP
,
751 .mapToFlightModes
= NAV_WP_MODE
| NAV_ALTHOLD_MODE
,
752 .mwState
= MW_NAV_STATE_PROCESS_NEXT
,
753 .mwError
= MW_NAV_ERROR_NONE
,
755 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_WAYPOINT_PRE_ACTION
,
756 [NAV_FSM_EVENT_ERROR
] = NAV_STATE_IDLE
,
757 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
758 [NAV_FSM_EVENT_SWITCH_TO_WAYPOINT_FINISHED
] = NAV_STATE_WAYPOINT_FINISHED
,
762 [NAV_STATE_WAYPOINT_PRE_ACTION
] = {
763 .persistentId
= NAV_PERSISTENT_ID_WAYPOINT_PRE_ACTION
,
764 .onEntry
= navOnEnteringState_NAV_STATE_WAYPOINT_PRE_ACTION
,
766 .stateFlags
= NAV_CTL_ALT
| NAV_CTL_POS
| NAV_CTL_YAW
| NAV_REQUIRE_ANGLE
| NAV_REQUIRE_MAGHOLD
| NAV_REQUIRE_THRTILT
| NAV_AUTO_WP
,
767 .mapToFlightModes
= NAV_WP_MODE
| NAV_ALTHOLD_MODE
,
768 .mwState
= MW_NAV_STATE_PROCESS_NEXT
,
769 .mwError
= MW_NAV_ERROR_NONE
,
771 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_WAYPOINT_PRE_ACTION
, // re-process the state (for JUMP)
772 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_WAYPOINT_IN_PROGRESS
,
773 [NAV_FSM_EVENT_ERROR
] = NAV_STATE_IDLE
,
774 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
775 [NAV_FSM_EVENT_SWITCH_TO_RTH
] = NAV_STATE_RTH_INITIALIZE
,
776 [NAV_FSM_EVENT_SWITCH_TO_WAYPOINT_FINISHED
] = NAV_STATE_WAYPOINT_FINISHED
,
780 [NAV_STATE_WAYPOINT_IN_PROGRESS
] = {
781 .persistentId
= NAV_PERSISTENT_ID_WAYPOINT_IN_PROGRESS
,
782 .onEntry
= navOnEnteringState_NAV_STATE_WAYPOINT_IN_PROGRESS
,
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_WP_ENROUTE
,
787 .mwError
= MW_NAV_ERROR_NONE
,
789 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_WAYPOINT_IN_PROGRESS
, // re-process the state
790 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_WAYPOINT_REACHED
, // 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_REACHED
] = {
802 .persistentId
= NAV_PERSISTENT_ID_WAYPOINT_REACHED
,
803 .onEntry
= navOnEnteringState_NAV_STATE_WAYPOINT_REACHED
,
805 .stateFlags
= NAV_CTL_ALT
| NAV_CTL_POS
| NAV_CTL_YAW
| 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_PROCESS_NEXT
,
808 .mwError
= MW_NAV_ERROR_NONE
,
810 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_WAYPOINT_REACHED
, // re-process state
811 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_WAYPOINT_NEXT
,
812 [NAV_FSM_EVENT_SWITCH_TO_WAYPOINT_HOLD_TIME
] = NAV_STATE_WAYPOINT_HOLD_TIME
,
813 [NAV_FSM_EVENT_SWITCH_TO_WAYPOINT_FINISHED
] = NAV_STATE_WAYPOINT_FINISHED
,
814 [NAV_FSM_EVENT_SWITCH_TO_WAYPOINT_RTH_LAND
] = NAV_STATE_WAYPOINT_RTH_LAND
,
815 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
816 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
817 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
818 [NAV_FSM_EVENT_SWITCH_TO_RTH
] = NAV_STATE_RTH_INITIALIZE
,
819 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
820 [NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD
] = NAV_STATE_COURSE_HOLD_INITIALIZE
,
821 [NAV_FSM_EVENT_SWITCH_TO_CRUISE
] = NAV_STATE_CRUISE_INITIALIZE
,
825 [NAV_STATE_WAYPOINT_HOLD_TIME
] = {
826 .persistentId
= NAV_PERSISTENT_ID_WAYPOINT_HOLD_TIME
, // There is no state for timed hold?
827 .onEntry
= navOnEnteringState_NAV_STATE_WAYPOINT_HOLD_TIME
,
829 .stateFlags
= NAV_CTL_ALT
| NAV_CTL_POS
| NAV_CTL_YAW
| NAV_REQUIRE_ANGLE
| NAV_REQUIRE_MAGHOLD
| NAV_REQUIRE_THRTILT
| NAV_AUTO_WP
,
830 .mapToFlightModes
= NAV_WP_MODE
| NAV_ALTHOLD_MODE
,
831 .mwState
= MW_NAV_STATE_HOLD_TIMED
,
832 .mwError
= MW_NAV_ERROR_NONE
,
834 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_WAYPOINT_HOLD_TIME
, // re-process the state
835 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_WAYPOINT_NEXT
, // successfully reached waypoint
836 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
837 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
838 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
839 [NAV_FSM_EVENT_SWITCH_TO_RTH
] = NAV_STATE_RTH_INITIALIZE
,
840 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
841 [NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD
] = NAV_STATE_COURSE_HOLD_INITIALIZE
,
842 [NAV_FSM_EVENT_SWITCH_TO_CRUISE
] = NAV_STATE_CRUISE_INITIALIZE
,
846 [NAV_STATE_WAYPOINT_RTH_LAND
] = {
847 .persistentId
= NAV_PERSISTENT_ID_WAYPOINT_RTH_LAND
,
848 .onEntry
= navOnEnteringState_NAV_STATE_WAYPOINT_RTH_LAND
,
850 .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
,
851 .mapToFlightModes
= NAV_WP_MODE
| NAV_ALTHOLD_MODE
,
852 .mwState
= MW_NAV_STATE_LAND_IN_PROGRESS
,
853 .mwError
= MW_NAV_ERROR_LANDING
,
855 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_WAYPOINT_RTH_LAND
, // re-process state
856 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_WAYPOINT_FINISHED
,
857 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
858 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
859 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
860 [NAV_FSM_EVENT_SWITCH_TO_RTH
] = NAV_STATE_RTH_INITIALIZE
,
861 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
862 [NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD
] = NAV_STATE_COURSE_HOLD_INITIALIZE
,
863 [NAV_FSM_EVENT_SWITCH_TO_CRUISE
] = NAV_STATE_CRUISE_INITIALIZE
,
864 [NAV_FSM_EVENT_SWITCH_TO_MIXERAT
] = NAV_STATE_MIXERAT_INITIALIZE
,
865 [NAV_FSM_EVENT_SWITCH_TO_NAV_STATE_FW_LANDING
] = NAV_STATE_FW_LANDING_CLIMB_TO_LOITER
,
866 [NAV_FSM_EVENT_SWITCH_TO_WAYPOINT_FINISHED
] = NAV_STATE_WAYPOINT_FINISHED
,
870 [NAV_STATE_WAYPOINT_NEXT
] = {
871 .persistentId
= NAV_PERSISTENT_ID_WAYPOINT_NEXT
,
872 .onEntry
= navOnEnteringState_NAV_STATE_WAYPOINT_NEXT
,
874 .stateFlags
= NAV_CTL_ALT
| NAV_CTL_POS
| NAV_CTL_YAW
| NAV_REQUIRE_ANGLE
| NAV_REQUIRE_MAGHOLD
| NAV_REQUIRE_THRTILT
| NAV_AUTO_WP
,
875 .mapToFlightModes
= NAV_WP_MODE
| NAV_ALTHOLD_MODE
,
876 .mwState
= MW_NAV_STATE_PROCESS_NEXT
,
877 .mwError
= MW_NAV_ERROR_NONE
,
879 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_WAYPOINT_PRE_ACTION
,
880 [NAV_FSM_EVENT_SWITCH_TO_WAYPOINT_FINISHED
] = NAV_STATE_WAYPOINT_FINISHED
,
884 [NAV_STATE_WAYPOINT_FINISHED
] = {
885 .persistentId
= NAV_PERSISTENT_ID_WAYPOINT_FINISHED
,
886 .onEntry
= navOnEnteringState_NAV_STATE_WAYPOINT_FINISHED
,
888 .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
,
889 .mapToFlightModes
= NAV_WP_MODE
| NAV_ALTHOLD_MODE
,
890 .mwState
= MW_NAV_STATE_WP_ENROUTE
,
891 .mwError
= MW_NAV_ERROR_FINISH
,
893 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_WAYPOINT_FINISHED
, // re-process state
894 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
895 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
896 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
897 [NAV_FSM_EVENT_SWITCH_TO_RTH
] = NAV_STATE_RTH_INITIALIZE
,
898 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
899 [NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD
] = NAV_STATE_COURSE_HOLD_INITIALIZE
,
900 [NAV_FSM_EVENT_SWITCH_TO_CRUISE
] = NAV_STATE_CRUISE_INITIALIZE
,
904 /** EMERGENCY LANDING ************************************************/
905 [NAV_STATE_EMERGENCY_LANDING_INITIALIZE
] = {
906 .persistentId
= NAV_PERSISTENT_ID_EMERGENCY_LANDING_INITIALIZE
,
907 .onEntry
= navOnEnteringState_NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
909 .stateFlags
= NAV_CTL_EMERG
| NAV_REQUIRE_ANGLE
,
910 .mapToFlightModes
= 0,
911 .mwState
= MW_NAV_STATE_EMERGENCY_LANDING
,
912 .mwError
= MW_NAV_ERROR_LANDING
,
914 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_EMERGENCY_LANDING_IN_PROGRESS
,
915 [NAV_FSM_EVENT_ERROR
] = NAV_STATE_IDLE
,
916 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
917 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
918 [NAV_FSM_EVENT_SWITCH_TO_RTH
] = NAV_STATE_RTH_INITIALIZE
,
919 [NAV_FSM_EVENT_SWITCH_TO_WAYPOINT
] = NAV_STATE_WAYPOINT_INITIALIZE
,
923 [NAV_STATE_EMERGENCY_LANDING_IN_PROGRESS
] = {
924 .persistentId
= NAV_PERSISTENT_ID_EMERGENCY_LANDING_IN_PROGRESS
,
925 .onEntry
= navOnEnteringState_NAV_STATE_EMERGENCY_LANDING_IN_PROGRESS
,
927 .stateFlags
= NAV_CTL_EMERG
| NAV_REQUIRE_ANGLE
,
928 .mapToFlightModes
= 0,
929 .mwState
= MW_NAV_STATE_EMERGENCY_LANDING
,
930 .mwError
= MW_NAV_ERROR_LANDING
,
932 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_EMERGENCY_LANDING_IN_PROGRESS
, // re-process the state
933 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_EMERGENCY_LANDING_FINISHED
,
934 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
935 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
936 [NAV_FSM_EVENT_SWITCH_TO_RTH
] = NAV_STATE_RTH_INITIALIZE
,
937 [NAV_FSM_EVENT_SWITCH_TO_WAYPOINT
] = NAV_STATE_WAYPOINT_INITIALIZE
,
941 [NAV_STATE_EMERGENCY_LANDING_FINISHED
] = {
942 .persistentId
= NAV_PERSISTENT_ID_EMERGENCY_LANDING_FINISHED
,
943 .onEntry
= navOnEnteringState_NAV_STATE_EMERGENCY_LANDING_FINISHED
,
945 .stateFlags
= NAV_CTL_EMERG
| NAV_REQUIRE_ANGLE
,
946 .mapToFlightModes
= 0,
947 .mwState
= MW_NAV_STATE_LANDED
,
948 .mwError
= MW_NAV_ERROR_LANDING
,
950 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_EMERGENCY_LANDING_FINISHED
,
951 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
955 [NAV_STATE_LAUNCH_INITIALIZE
] = {
956 .persistentId
= NAV_PERSISTENT_ID_LAUNCH_INITIALIZE
,
957 .onEntry
= navOnEnteringState_NAV_STATE_LAUNCH_INITIALIZE
,
959 .stateFlags
= NAV_REQUIRE_ANGLE
,
960 .mapToFlightModes
= NAV_LAUNCH_MODE
,
961 .mwState
= MW_NAV_STATE_NONE
,
962 .mwError
= MW_NAV_ERROR_NONE
,
964 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_LAUNCH_WAIT
,
965 [NAV_FSM_EVENT_ERROR
] = NAV_STATE_IDLE
,
966 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
970 [NAV_STATE_LAUNCH_WAIT
] = {
971 .persistentId
= NAV_PERSISTENT_ID_LAUNCH_WAIT
,
972 .onEntry
= navOnEnteringState_NAV_STATE_LAUNCH_WAIT
,
974 .stateFlags
= NAV_CTL_LAUNCH
| NAV_REQUIRE_ANGLE
,
975 .mapToFlightModes
= NAV_LAUNCH_MODE
,
976 .mwState
= MW_NAV_STATE_NONE
,
977 .mwError
= MW_NAV_ERROR_NONE
,
979 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_LAUNCH_WAIT
, // re-process the state
980 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_LAUNCH_IN_PROGRESS
,
981 [NAV_FSM_EVENT_ERROR
] = NAV_STATE_IDLE
,
982 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
986 [NAV_STATE_LAUNCH_IN_PROGRESS
] = {
987 .persistentId
= NAV_PERSISTENT_ID_LAUNCH_IN_PROGRESS
,
988 .onEntry
= navOnEnteringState_NAV_STATE_LAUNCH_IN_PROGRESS
,
990 .stateFlags
= NAV_CTL_LAUNCH
| NAV_REQUIRE_ANGLE
,
991 .mapToFlightModes
= NAV_LAUNCH_MODE
,
992 .mwState
= MW_NAV_STATE_NONE
,
993 .mwError
= MW_NAV_ERROR_NONE
,
995 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_LAUNCH_IN_PROGRESS
, // re-process the state
996 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_IDLE
,
997 [NAV_FSM_EVENT_ERROR
] = NAV_STATE_IDLE
,
998 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
1002 /** MIXER AUTOMATED TRANSITION mode, alternated althod ***************************************************/
1003 [NAV_STATE_MIXERAT_INITIALIZE
] = {
1004 .persistentId
= NAV_PERSISTENT_ID_MIXERAT_INITIALIZE
,
1005 .onEntry
= navOnEnteringState_NAV_STATE_MIXERAT_INITIALIZE
,
1007 .stateFlags
= NAV_CTL_ALT
| NAV_REQUIRE_ANGLE
| NAV_REQUIRE_THRTILT
| NAV_MIXERAT
,
1008 .mapToFlightModes
= NAV_ALTHOLD_MODE
,
1009 .mwState
= MW_NAV_STATE_NONE
,
1010 .mwError
= MW_NAV_ERROR_NONE
,
1012 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_MIXERAT_IN_PROGRESS
,
1013 [NAV_FSM_EVENT_ERROR
] = NAV_STATE_IDLE
,
1014 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
1018 [NAV_STATE_MIXERAT_IN_PROGRESS
] = {
1019 .persistentId
= NAV_PERSISTENT_ID_MIXERAT_IN_PROGRESS
,
1020 .onEntry
= navOnEnteringState_NAV_STATE_MIXERAT_IN_PROGRESS
,
1022 .stateFlags
= NAV_CTL_ALT
| NAV_REQUIRE_ANGLE
| NAV_REQUIRE_THRTILT
| NAV_MIXERAT
,
1023 .mapToFlightModes
= NAV_ALTHOLD_MODE
,
1024 .mwState
= MW_NAV_STATE_NONE
,
1025 .mwError
= MW_NAV_ERROR_NONE
,
1027 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_MIXERAT_IN_PROGRESS
, // re-process the state
1028 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_MIXERAT_ABORT
,
1029 [NAV_FSM_EVENT_SWITCH_TO_RTH_HEAD_HOME
] = NAV_STATE_RTH_HEAD_HOME
, //switch to its pending state
1030 [NAV_FSM_EVENT_SWITCH_TO_RTH_LANDING
] = NAV_STATE_RTH_LANDING
, //switch to its pending state
1033 [NAV_STATE_MIXERAT_ABORT
] = {
1034 .persistentId
= NAV_PERSISTENT_ID_MIXERAT_ABORT
,
1035 .onEntry
= navOnEnteringState_NAV_STATE_MIXERAT_ABORT
, //will not switch to its pending state
1037 .stateFlags
= NAV_CTL_ALT
| NAV_REQUIRE_ANGLE
| NAV_REQUIRE_THRTILT
,
1038 .mapToFlightModes
= NAV_ALTHOLD_MODE
,
1039 .mwState
= MW_NAV_STATE_NONE
,
1040 .mwError
= MW_NAV_ERROR_NONE
,
1042 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_IDLE
,
1043 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
1048 #ifdef USE_FW_AUTOLAND
1049 [NAV_STATE_FW_LANDING_CLIMB_TO_LOITER
] = {
1050 .persistentId
= NAV_PERSISTENT_ID_FW_LANDING_CLIMB_TO_LOITER
,
1051 .onEntry
= navOnEnteringState_NAV_STATE_FW_LANDING_CLIMB_TO_LOITER
,
1053 .stateFlags
= NAV_CTL_ALT
| NAV_CTL_POS
| NAV_CTL_YAW
| NAV_REQUIRE_ANGLE
| NAV_REQUIRE_MAGHOLD
| NAV_REQUIRE_THRTILT
| NAV_AUTO_RTH
,
1054 .mapToFlightModes
= NAV_RTH_MODE
| NAV_ALTHOLD_MODE
,
1055 .mwState
= MW_NAV_STATE_LAND_IN_PROGRESS
,
1056 .mwError
= MW_NAV_ERROR_NONE
,
1058 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_FW_LANDING_CLIMB_TO_LOITER
,
1059 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_FW_LANDING_LOITER
,
1060 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
1061 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
1062 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
1063 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
1064 [NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD
] = NAV_STATE_COURSE_HOLD_INITIALIZE
,
1065 [NAV_FSM_EVENT_SWITCH_TO_CRUISE
] = NAV_STATE_CRUISE_INITIALIZE
,
1066 [NAV_FSM_EVENT_SWITCH_TO_NAV_STATE_FW_LANDING_ABORT
] = NAV_STATE_FW_LANDING_ABORT
,
1070 [NAV_STATE_FW_LANDING_LOITER
] = {
1071 .persistentId
= NAV_PERSISTENT_ID_FW_LANDING_LOITER
,
1072 .onEntry
= navOnEnteringState_NAV_STATE_FW_LANDING_LOITER
,
1074 .stateFlags
= NAV_CTL_ALT
| NAV_CTL_POS
| NAV_CTL_YAW
| NAV_REQUIRE_ANGLE
| NAV_REQUIRE_MAGHOLD
| NAV_REQUIRE_THRTILT
| NAV_AUTO_RTH
,
1075 .mapToFlightModes
= NAV_RTH_MODE
| NAV_ALTHOLD_MODE
,
1076 .mwState
= MW_NAV_STATE_LAND_IN_PROGRESS
,
1077 .mwError
= MW_NAV_ERROR_NONE
,
1079 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_FW_LANDING_LOITER
,
1080 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_FW_LANDING_APPROACH
,
1081 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
1082 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
1083 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
1084 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
1085 [NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD
] = NAV_STATE_COURSE_HOLD_INITIALIZE
,
1086 [NAV_FSM_EVENT_SWITCH_TO_CRUISE
] = NAV_STATE_CRUISE_INITIALIZE
,
1087 [NAV_FSM_EVENT_SWITCH_TO_NAV_STATE_FW_LANDING_ABORT
] = NAV_STATE_FW_LANDING_ABORT
,
1091 [NAV_STATE_FW_LANDING_APPROACH
] = {
1092 .persistentId
= NAV_PERSISTENT_ID_FW_LANDING_APPROACH
,
1093 .onEntry
= navOnEnteringState_NAV_STATE_FW_LANDING_APPROACH
,
1095 .stateFlags
= NAV_CTL_ALT
| NAV_CTL_POS
| NAV_CTL_YAW
| NAV_REQUIRE_ANGLE
| NAV_REQUIRE_MAGHOLD
| NAV_REQUIRE_THRTILT
| NAV_AUTO_WP
,
1096 .mapToFlightModes
= NAV_WP_MODE
| NAV_ALTHOLD_MODE
,
1097 .mwState
= MW_NAV_STATE_LAND_IN_PROGRESS
,
1098 .mwError
= MW_NAV_ERROR_NONE
,
1100 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_FW_LANDING_APPROACH
,
1101 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_FW_LANDING_GLIDE
,
1102 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
1103 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
1104 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
1105 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
1106 [NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD
] = NAV_STATE_COURSE_HOLD_INITIALIZE
,
1107 [NAV_FSM_EVENT_SWITCH_TO_CRUISE
] = NAV_STATE_CRUISE_INITIALIZE
,
1108 [NAV_FSM_EVENT_SWITCH_TO_NAV_STATE_FW_LANDING_ABORT
] = NAV_STATE_FW_LANDING_ABORT
,
1112 [NAV_STATE_FW_LANDING_GLIDE
] = {
1113 .persistentId
= NAV_PERSISTENT_ID_FW_LANDING_GLIDE
,
1114 .onEntry
= navOnEnteringState_NAV_STATE_FW_LANDING_GLIDE
,
1116 .stateFlags
= NAV_CTL_POS
| NAV_CTL_YAW
| NAV_REQUIRE_ANGLE
| NAV_RC_POS
| NAV_RC_YAW
,
1117 .mapToFlightModes
= NAV_COURSE_HOLD_MODE
,
1118 .mwState
= MW_NAV_STATE_LAND_IN_PROGRESS
,
1119 .mwError
= MW_NAV_ERROR_NONE
,
1121 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_FW_LANDING_GLIDE
,
1122 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_FW_LANDING_FLARE
,
1123 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
1124 [NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
] = NAV_STATE_ALTHOLD_INITIALIZE
,
1125 [NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
] = NAV_STATE_POSHOLD_3D_INITIALIZE
,
1126 [NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE
,
1127 [NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD
] = NAV_STATE_COURSE_HOLD_INITIALIZE
,
1128 [NAV_FSM_EVENT_SWITCH_TO_CRUISE
] = NAV_STATE_CRUISE_INITIALIZE
,
1129 [NAV_FSM_EVENT_SWITCH_TO_NAV_STATE_FW_LANDING_ABORT
] = NAV_STATE_FW_LANDING_ABORT
,
1133 [NAV_STATE_FW_LANDING_FLARE
] = {
1134 .persistentId
= NAV_PERSISTENT_ID_FW_LANDING_FLARE
,
1135 .onEntry
= navOnEnteringState_NAV_STATE_FW_LANDING_FLARE
,
1137 .stateFlags
= NAV_CTL_POS
| NAV_CTL_YAW
| NAV_REQUIRE_ANGLE
| NAV_RC_POS
| NAV_RC_YAW
,
1138 .mapToFlightModes
= NAV_COURSE_HOLD_MODE
,
1139 .mwState
= MW_NAV_STATE_LAND_IN_PROGRESS
,
1140 .mwError
= MW_NAV_ERROR_NONE
,
1142 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_FW_LANDING_FLARE
, // re-process the state
1143 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_IDLE
,
1144 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
1147 [NAV_STATE_FW_LANDING_ABORT
] = {
1148 .persistentId
= NAV_PERSISTENT_ID_FW_LANDING_ABORT
,
1149 .onEntry
= navOnEnteringState_NAV_STATE_FW_LANDING_ABORT
,
1151 .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
,
1152 .mapToFlightModes
= NAV_WP_MODE
| NAV_ALTHOLD_MODE
,
1153 .mwState
= MW_NAV_STATE_LAND_IN_PROGRESS
,
1154 .mwError
= MW_NAV_ERROR_NONE
,
1156 [NAV_FSM_EVENT_TIMEOUT
] = NAV_STATE_FW_LANDING_ABORT
,
1157 [NAV_FSM_EVENT_SUCCESS
] = NAV_STATE_IDLE
,
1158 [NAV_FSM_EVENT_SWITCH_TO_RTH
] = NAV_STATE_RTH_INITIALIZE
,
1159 [NAV_FSM_EVENT_SWITCH_TO_IDLE
] = NAV_STATE_IDLE
,
1160 [NAV_FSM_EVENT_SWITCH_TO_WAYPOINT
] = NAV_STATE_WAYPOINT_INITIALIZE
,
1166 static navigationFSMStateFlags_t
navGetStateFlags(navigationFSMState_t state
)
1168 return navFSM
[state
].stateFlags
;
1171 flightModeFlags_e
navGetMappedFlightModes(navigationFSMState_t state
)
1173 return navFSM
[state
].mapToFlightModes
;
1176 navigationFSMStateFlags_t
navGetCurrentStateFlags(void)
1178 return navGetStateFlags(posControl
.navState
);
1181 static bool navTerrainFollowingRequested(void)
1183 // Terrain following not supported on FIXED WING aircraft yet
1184 return !STATE(FIXED_WING_LEGACY
) && IS_RC_MODE_ACTIVE(BOXSURFACE
);
1187 /*************************************************************************************************/
1188 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_IDLE(navigationFSMState_t previousState
)
1190 UNUSED(previousState
);
1192 resetAltitudeController(false);
1193 resetHeadingController();
1194 resetPositionController();
1195 #ifdef USE_FW_AUTOLAND
1199 return NAV_FSM_EVENT_NONE
;
1202 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_ALTHOLD_INITIALIZE(navigationFSMState_t previousState
)
1204 const navigationFSMStateFlags_t prevFlags
= navGetStateFlags(previousState
);
1205 const bool terrainFollowingToggled
= (posControl
.flags
.isTerrainFollowEnabled
!= navTerrainFollowingRequested());
1209 // Prepare altitude controller if idle, RTH or WP modes active or surface mode status changed
1210 if (!(prevFlags
& NAV_CTL_ALT
) || (prevFlags
& NAV_AUTO_RTH
) || (prevFlags
& NAV_AUTO_WP
) || terrainFollowingToggled
) {
1211 resetAltitudeController(navTerrainFollowingRequested());
1212 setupAltitudeController();
1213 setDesiredPosition(&navGetCurrentActualPositionAndVelocity()->pos
, posControl
.actualState
.yaw
, NAV_POS_UPDATE_Z
); // This will reset surface offset
1216 return NAV_FSM_EVENT_SUCCESS
;
1219 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_ALTHOLD_IN_PROGRESS(navigationFSMState_t previousState
)
1221 UNUSED(previousState
);
1223 // If GCS was disabled - reset altitude setpoint
1224 if (posControl
.flags
.isGCSAssistedNavigationReset
) {
1225 setDesiredPosition(&navGetCurrentActualPositionAndVelocity()->pos
, posControl
.actualState
.yaw
, NAV_POS_UPDATE_Z
);
1229 return NAV_FSM_EVENT_NONE
;
1232 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_POSHOLD_3D_INITIALIZE(navigationFSMState_t previousState
)
1234 const navigationFSMStateFlags_t prevFlags
= navGetStateFlags(previousState
);
1235 const bool terrainFollowingToggled
= (posControl
.flags
.isTerrainFollowEnabled
!= navTerrainFollowingRequested());
1239 // Prepare altitude controller if idle, RTH or WP modes active or surface mode status changed
1240 if (!(prevFlags
& NAV_CTL_ALT
) || (prevFlags
& NAV_AUTO_RTH
) || (prevFlags
& NAV_AUTO_WP
) || terrainFollowingToggled
) {
1241 resetAltitudeController(navTerrainFollowingRequested());
1242 setupAltitudeController();
1243 setDesiredPosition(&navGetCurrentActualPositionAndVelocity()->pos
, posControl
.actualState
.yaw
, NAV_POS_UPDATE_Z
); // This will reset surface offset
1246 // Prepare position controller if idle or current Mode NOT active in position hold state
1247 if (previousState
!= NAV_STATE_RTH_HOVER_PRIOR_TO_LANDING
&& previousState
!= NAV_STATE_RTH_HOVER_ABOVE_HOME
&&
1248 previousState
!= NAV_STATE_RTH_LANDING
&& previousState
!= NAV_STATE_WAYPOINT_RTH_LAND
&&
1249 previousState
!= NAV_STATE_WAYPOINT_FINISHED
&& previousState
!= NAV_STATE_WAYPOINT_HOLD_TIME
)
1251 resetPositionController();
1253 fpVector3_t targetHoldPos
;
1254 calculateInitialHoldPosition(&targetHoldPos
);
1255 setDesiredPosition(&targetHoldPos
, posControl
.actualState
.yaw
, NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_HEADING
);
1258 return NAV_FSM_EVENT_SUCCESS
;
1261 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_POSHOLD_3D_IN_PROGRESS(navigationFSMState_t previousState
)
1263 UNUSED(previousState
);
1265 // If GCS was disabled - reset 2D pos setpoint
1266 if (posControl
.flags
.isGCSAssistedNavigationReset
) {
1267 fpVector3_t targetHoldPos
;
1268 calculateInitialHoldPosition(&targetHoldPos
);
1269 setDesiredPosition(&navGetCurrentActualPositionAndVelocity()->pos
, posControl
.actualState
.yaw
, NAV_POS_UPDATE_Z
);
1270 setDesiredPosition(&targetHoldPos
, posControl
.actualState
.yaw
, NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_HEADING
);
1274 return NAV_FSM_EVENT_NONE
;
1277 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_COURSE_HOLD_INITIALIZE(navigationFSMState_t previousState
)
1279 UNUSED(previousState
);
1281 if (STATE(MULTIROTOR
) && !navConfig()->general
.cruise_yaw_rate
) { // course hold not possible on MR without yaw control
1282 return NAV_FSM_EVENT_ERROR
;
1285 DEBUG_SET(DEBUG_CRUISE
, 0, 1);
1286 // Switch to IDLE if we do not have an healty position. Try the next iteration.
1287 if (checkForPositionSensorTimeout()) {
1288 return NAV_FSM_EVENT_SWITCH_TO_IDLE
;
1291 resetPositionController();
1293 if (STATE(AIRPLANE
)) {
1294 posControl
.cruise
.course
= posControl
.actualState
.cog
; // Store the course to follow
1295 } else { // Multicopter
1296 posControl
.cruise
.course
= posControl
.actualState
.yaw
;
1297 posControl
.cruise
.multicopterSpeed
= constrainf(posControl
.actualState
.velXY
, 10.0f
, navConfig()->general
.max_manual_speed
);
1298 posControl
.desiredState
.pos
= posControl
.actualState
.abs
.pos
;
1300 posControl
.cruise
.previousCourse
= posControl
.cruise
.course
;
1301 posControl
.cruise
.lastCourseAdjustmentTime
= 0;
1303 return NAV_FSM_EVENT_SUCCESS
; // Go to NAV_STATE_COURSE_HOLD_IN_PROGRESS state
1306 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_COURSE_HOLD_IN_PROGRESS(navigationFSMState_t previousState
)
1308 UNUSED(previousState
);
1310 const timeMs_t currentTimeMs
= millis();
1312 // Switch to IDLE if we do not have an healty position. Do the CRUISE init the next iteration.
1313 if (checkForPositionSensorTimeout()) {
1314 return NAV_FSM_EVENT_SWITCH_TO_IDLE
;
1317 DEBUG_SET(DEBUG_CRUISE
, 0, 2);
1318 DEBUG_SET(DEBUG_CRUISE
, 2, 0);
1320 if (STATE(AIRPLANE
) && posControl
.flags
.isAdjustingPosition
) { // inhibit for MR, pitch/roll only adjusts speed using pitch
1321 return NAV_FSM_EVENT_SWITCH_TO_COURSE_ADJ
;
1324 const bool mcRollStickHeadingAdjustmentActive
= STATE(MULTIROTOR
) && ABS(rcCommand
[ROLL
]) > rcControlsConfig()->pos_hold_deadband
;
1326 // User demanding yaw -> yaw stick on FW, yaw or roll sticks on MR
1327 // We record the desired course and change the desired target in the meanwhile
1328 if (posControl
.flags
.isAdjustingHeading
|| mcRollStickHeadingAdjustmentActive
) {
1329 int16_t cruiseYawRate
= DEGREES_TO_CENTIDEGREES(navConfig()->general
.cruise_yaw_rate
);
1330 int16_t headingAdjustCommand
= rcCommand
[YAW
];
1331 if (mcRollStickHeadingAdjustmentActive
&& ABS(rcCommand
[ROLL
]) > ABS(headingAdjustCommand
)) {
1332 headingAdjustCommand
= -rcCommand
[ROLL
];
1335 timeMs_t timeDifference
= currentTimeMs
- posControl
.cruise
.lastCourseAdjustmentTime
;
1336 if (timeDifference
> 100) timeDifference
= 0; // if adjustment was called long time ago, reset the time difference.
1337 float rateTarget
= scaleRangef((float)headingAdjustCommand
, -500.0f
, 500.0f
, -cruiseYawRate
, cruiseYawRate
);
1338 float centidegsPerIteration
= rateTarget
* MS2S(timeDifference
);
1339 posControl
.cruise
.course
= wrap_36000(posControl
.cruise
.course
- centidegsPerIteration
);
1340 DEBUG_SET(DEBUG_CRUISE
, 1, CENTIDEGREES_TO_DEGREES(posControl
.cruise
.course
));
1341 posControl
.cruise
.lastCourseAdjustmentTime
= currentTimeMs
;
1342 } else if (currentTimeMs
- posControl
.cruise
.lastCourseAdjustmentTime
> 4000) {
1343 posControl
.cruise
.previousCourse
= posControl
.cruise
.course
;
1346 setDesiredPosition(NULL
, posControl
.cruise
.course
, NAV_POS_UPDATE_HEADING
);
1348 return NAV_FSM_EVENT_NONE
;
1351 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_COURSE_HOLD_ADJUSTING(navigationFSMState_t previousState
)
1353 UNUSED(previousState
);
1354 DEBUG_SET(DEBUG_CRUISE
, 0, 3);
1356 // User is rolling, changing manually direction. Wait until it is done and then restore CRUISE
1357 if (posControl
.flags
.isAdjustingPosition
) {
1358 posControl
.cruise
.course
= posControl
.actualState
.cog
; //store current course
1359 posControl
.cruise
.lastCourseAdjustmentTime
= millis();
1360 return NAV_FSM_EVENT_NONE
; // reprocess the state
1363 resetPositionController();
1365 return NAV_FSM_EVENT_SUCCESS
; // go back to NAV_STATE_COURSE_HOLD_IN_PROGRESS state
1368 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_CRUISE_INITIALIZE(navigationFSMState_t previousState
)
1370 if (STATE(MULTIROTOR
) && !navConfig()->general
.cruise_yaw_rate
) { // course hold not possible on MR without yaw control
1371 return NAV_FSM_EVENT_ERROR
;
1374 navOnEnteringState_NAV_STATE_ALTHOLD_INITIALIZE(previousState
);
1376 return navOnEnteringState_NAV_STATE_COURSE_HOLD_INITIALIZE(previousState
);
1379 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_CRUISE_IN_PROGRESS(navigationFSMState_t previousState
)
1381 navOnEnteringState_NAV_STATE_ALTHOLD_IN_PROGRESS(previousState
);
1383 return navOnEnteringState_NAV_STATE_COURSE_HOLD_IN_PROGRESS(previousState
);
1386 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_CRUISE_ADJUSTING(navigationFSMState_t previousState
)
1388 navOnEnteringState_NAV_STATE_ALTHOLD_IN_PROGRESS(previousState
);
1390 return navOnEnteringState_NAV_STATE_COURSE_HOLD_ADJUSTING(previousState
);
1393 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_INITIALIZE(navigationFSMState_t previousState
)
1396 if (navConfig()->general
.flags
.rth_use_linear_descent
&& posControl
.rthState
.rthLinearDescentActive
)
1397 posControl
.rthState
.rthLinearDescentActive
= false;
1399 if ((posControl
.flags
.estHeadingStatus
== EST_NONE
) || (posControl
.flags
.estAltStatus
== EST_NONE
) || !STATE(GPS_FIX_HOME
)) {
1400 // Heading sensor, altitude sensor and HOME fix are mandatory for RTH. If not satisfied - switch to emergency landing
1401 // Relevant to failsafe forced RTH only. Switched RTH blocked in selectNavEventFromBoxModeInput if sensors unavailable.
1402 // If we are in dead-reckoning mode - also fail, since coordinates may be unreliable
1403 return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
;
1406 if (previousState
!= NAV_STATE_FW_LANDING_ABORT
) {
1407 #ifdef USE_FW_AUTOLAND
1408 posControl
.fwLandState
.landAborted
= false;
1410 if (STATE(FIXED_WING_LEGACY
) && (posControl
.homeDistance
< navConfig()->general
.min_rth_distance
) && !posControl
.flags
.forcedRTHActivated
) {
1411 // Prevent RTH from activating on airplanes if too close to home unless it's a failsafe RTH
1412 return NAV_FSM_EVENT_SWITCH_TO_IDLE
;
1416 // If we have valid position sensor or configured to ignore it's loss at initial stage - continue
1417 if ((posControl
.flags
.estPosStatus
>= EST_USABLE
) || navConfig()->general
.flags
.rth_climb_ignore_emerg
) {
1418 // Prepare controllers
1419 resetPositionController();
1420 resetAltitudeController(false); // Make sure surface tracking is not enabled - RTH uses global altitude, not AGL
1421 setupAltitudeController();
1423 // If close to home - reset home position and land
1424 if (posControl
.homeDistance
< navConfig()->general
.min_rth_distance
) {
1425 setHomePosition(&navGetCurrentActualPositionAndVelocity()->pos
, posControl
.actualState
.yaw
, NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_HEADING
, NAV_HOME_VALID_ALL
);
1426 setDesiredPosition(&navGetCurrentActualPositionAndVelocity()->pos
, posControl
.actualState
.yaw
, NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_Z
| NAV_POS_UPDATE_HEADING
);
1428 return NAV_FSM_EVENT_SWITCH_TO_RTH_LANDING
; // NAV_STATE_RTH_HOVER_PRIOR_TO_LANDING
1431 // Switch to RTH trackback
1432 bool trackbackActive
= navConfig()->general
.flags
.rth_trackback_mode
== RTH_TRACKBACK_ON
||
1433 (navConfig()->general
.flags
.rth_trackback_mode
== RTH_TRACKBACK_FS
&& posControl
.flags
.forcedRTHActivated
);
1435 if (trackbackActive
&& posControl
.activeRthTBPointIndex
>= 0 && !isWaypointMissionRTHActive()) {
1436 updateRthTrackback(true); // save final trackpoint for altitude and max trackback distance reference
1437 posControl
.flags
.rthTrackbackActive
= true;
1438 calculateAndSetActiveWaypointToLocalPosition(rthGetTrackbackPos());
1439 return NAV_FSM_EVENT_SWITCH_TO_NAV_STATE_RTH_TRACKBACK
;
1442 fpVector3_t targetHoldPos
;
1444 if (STATE(FIXED_WING_LEGACY
)) {
1445 // Airplane - climbout before heading home
1446 if (navConfig()->general
.flags
.rth_climb_first
== RTH_CLIMB_ON_FW_SPIRAL
) {
1447 // Spiral climb centered at xy of RTH activation
1448 calculateInitialHoldPosition(&targetHoldPos
);
1450 calculateFarAwayTarget(&targetHoldPos
, posControl
.actualState
.cog
, 100000.0f
); // 1km away Linear climb
1453 // Multicopter, hover and climb
1454 calculateInitialHoldPosition(&targetHoldPos
);
1456 // Initialize RTH sanity check to prevent fly-aways on RTH
1457 // For airplanes this is delayed until climb-out is finished
1458 initializeRTHSanityChecker();
1461 setDesiredPosition(&targetHoldPos
, posControl
.actualState
.yaw
, NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_HEADING
);
1463 return NAV_FSM_EVENT_SUCCESS
; // NAV_STATE_RTH_CLIMB_TO_SAFE_ALT
1466 /* Position sensor failure timeout - land. Land immediately if failsafe RTH and timeout disabled (set to 0) */
1467 else if (checkForPositionSensorTimeout() || (!navConfig()->general
.pos_failure_timeout
&& posControl
.flags
.forcedRTHActivated
)) {
1468 return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
;
1470 /* No valid POS sensor but still within valid timeout - wait */
1471 return NAV_FSM_EVENT_NONE
;
1474 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_CLIMB_TO_SAFE_ALT(navigationFSMState_t previousState
)
1476 UNUSED(previousState
);
1478 if (!STATE(ALTITUDE_CONTROL
)) {
1479 //If altitude control is not a thing, switch to RTH in progress instead
1480 return NAV_FSM_EVENT_SUCCESS
; //Will cause NAV_STATE_RTH_HEAD_HOME
1483 rthAltControlStickOverrideCheck(PITCH
);
1485 /* Position sensor failure timeout and not configured to ignore GPS loss - land */
1486 if ((posControl
.flags
.estHeadingStatus
== EST_NONE
) ||
1487 (checkForPositionSensorTimeout() && !navConfig()->general
.flags
.rth_climb_ignore_emerg
)) {
1488 return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
;
1491 const uint8_t rthClimbMarginPercent
= STATE(FIXED_WING_LEGACY
) ? FW_RTH_CLIMB_MARGIN_PERCENT
: MR_RTH_CLIMB_MARGIN_PERCENT
;
1492 const float rthAltitudeMargin
= MAX(FW_RTH_CLIMB_MARGIN_MIN_CM
, (rthClimbMarginPercent
/100.0f
) * fabsf(posControl
.rthState
.rthInitialAltitude
- posControl
.rthState
.homePosition
.pos
.z
));
1494 // If we reached desired initial RTH altitude or we don't want to climb first
1495 if (((navGetCurrentActualPositionAndVelocity()->pos
.z
- posControl
.rthState
.rthInitialAltitude
) > -rthAltitudeMargin
) || (navConfig()->general
.flags
.rth_climb_first
== RTH_CLIMB_OFF
) || rthAltControlStickOverrideCheck(ROLL
) || rthClimbStageActiveAndComplete()) {
1497 // Delayed initialization for RTH sanity check on airplanes - allow to finish climb first as it can take some distance
1498 if (STATE(FIXED_WING_LEGACY
)) {
1499 initializeRTHSanityChecker();
1502 // Save initial home distance for future use
1503 posControl
.rthState
.rthInitialDistance
= posControl
.homeDistance
;
1504 fpVector3_t
* tmpHomePos
= rthGetHomeTargetPosition(RTH_HOME_ENROUTE_INITIAL
);
1506 if (navConfig()->general
.flags
.rth_tail_first
&& !STATE(FIXED_WING_LEGACY
)) {
1507 setDesiredPosition(tmpHomePos
, 0, NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_Z
| NAV_POS_UPDATE_BEARING_TAIL_FIRST
);
1510 setDesiredPosition(tmpHomePos
, 0, NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_Z
| NAV_POS_UPDATE_BEARING
);
1513 return NAV_FSM_EVENT_SUCCESS
; // NAV_STATE_RTH_HEAD_HOME
1517 fpVector3_t
* tmpHomePos
= rthGetHomeTargetPosition(RTH_HOME_ENROUTE_INITIAL
);
1519 /* For multi-rotors execute sanity check during initial ascent as well */
1520 if (!STATE(FIXED_WING_LEGACY
) && !validateRTHSanityChecker()) {
1521 return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
;
1524 // Climb to safe altitude and turn to correct direction
1525 // Until the initial climb phase is complete target slightly *above* the cruise altitude to ensure we actually reach
1526 // it in a reasonable time. Immediately after we finish this phase - target the original altitude.
1527 if (STATE(FIXED_WING_LEGACY
)) {
1528 tmpHomePos
->z
+= FW_RTH_CLIMB_OVERSHOOT_CM
;
1529 setDesiredPosition(tmpHomePos
, 0, NAV_POS_UPDATE_Z
);
1531 tmpHomePos
->z
+= MR_RTH_CLIMB_OVERSHOOT_CM
;
1533 if (navConfig()->general
.flags
.rth_tail_first
) {
1534 setDesiredPosition(tmpHomePos
, 0, NAV_POS_UPDATE_Z
| NAV_POS_UPDATE_BEARING_TAIL_FIRST
);
1536 setDesiredPosition(tmpHomePos
, 0, NAV_POS_UPDATE_Z
| NAV_POS_UPDATE_BEARING
);
1540 return NAV_FSM_EVENT_NONE
;
1544 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_TRACKBACK(navigationFSMState_t previousState
)
1546 UNUSED(previousState
);
1548 /* If position sensors unavailable - land immediately */
1549 if ((posControl
.flags
.estHeadingStatus
== EST_NONE
) || checkForPositionSensorTimeout()) {
1550 return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
;
1553 if (posControl
.flags
.estPosStatus
>= EST_USABLE
) {
1554 const int32_t distFromStartTrackback
= calculateDistanceToDestination(&posControl
.rthTBPointsList
[posControl
.rthTBLastSavedIndex
]) / 100;
1555 #ifdef USE_MULTI_FUNCTIONS
1556 const bool overrideTrackback
= rthAltControlStickOverrideCheck(ROLL
) || MULTI_FUNC_FLAG(MF_SUSPEND_TRACKBACK
);
1558 const bool overrideTrackback
= rthAltControlStickOverrideCheck(ROLL
);
1560 const bool cancelTrackback
= distFromStartTrackback
> navConfig()->general
.rth_trackback_distance
||
1561 (overrideTrackback
&& !posControl
.flags
.forcedRTHActivated
);
1563 if (posControl
.activeRthTBPointIndex
< 0 || cancelTrackback
) {
1564 posControl
.rthTBWrapAroundCounter
= posControl
.activeRthTBPointIndex
= -1;
1565 posControl
.flags
.rthTrackbackActive
= false;
1566 return NAV_FSM_EVENT_SWITCH_TO_NAV_STATE_RTH_INITIALIZE
; // procede to home after final trackback point
1569 if (isWaypointReached(&posControl
.activeWaypoint
.pos
, &posControl
.activeWaypoint
.bearing
)) {
1570 posControl
.activeRthTBPointIndex
--;
1572 if (posControl
.rthTBWrapAroundCounter
> -1 && posControl
.activeRthTBPointIndex
< 0) {
1573 posControl
.activeRthTBPointIndex
= NAV_RTH_TRACKBACK_POINTS
- 1;
1575 calculateAndSetActiveWaypointToLocalPosition(rthGetTrackbackPos());
1577 if (posControl
.activeRthTBPointIndex
- posControl
.rthTBWrapAroundCounter
== 0) {
1578 posControl
.rthTBWrapAroundCounter
= posControl
.activeRthTBPointIndex
= -1;
1581 setDesiredPosition(rthGetTrackbackPos(), 0, NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_Z
| NAV_POS_UPDATE_BEARING
);
1585 return NAV_FSM_EVENT_NONE
;
1588 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_HEAD_HOME(navigationFSMState_t previousState
)
1590 UNUSED(previousState
);
1592 rthAltControlStickOverrideCheck(PITCH
);
1594 /* If position sensors unavailable - land immediately */
1595 if ((posControl
.flags
.estHeadingStatus
== EST_NONE
) || !validateRTHSanityChecker()) {
1596 return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
;
1599 if (checkMixerATRequired(MIXERAT_REQUEST_RTH
) && (calculateDistanceToDestination(&posControl
.rthState
.homePosition
.pos
) > (navConfig()->fw
.loiter_radius
* 3))){
1600 return NAV_FSM_EVENT_SWITCH_TO_MIXERAT
;
1603 if (navConfig()->general
.flags
.rth_use_linear_descent
&& navConfig()->general
.rth_home_altitude
> 0) {
1604 // Check linear descent status
1605 uint32_t homeDistance
= calculateDistanceToDestination(&posControl
.rthState
.homePosition
.pos
);
1607 if (homeDistance
<= METERS_TO_CENTIMETERS(navConfig()->general
.rth_linear_descent_start_distance
)) {
1608 posControl
.rthState
.rthFinalAltitude
= posControl
.rthState
.homePosition
.pos
.z
+ navConfig()->general
.rth_home_altitude
;
1609 posControl
.rthState
.rthLinearDescentActive
= true;
1613 // If we have position sensor - continue home
1614 if ((posControl
.flags
.estPosStatus
>= EST_USABLE
)) {
1615 fpVector3_t
* tmpHomePos
= rthGetHomeTargetPosition(RTH_HOME_ENROUTE_PROPORTIONAL
);
1617 if (isWaypointReached(tmpHomePos
, 0)) {
1618 // Successfully reached position target - update XYZ-position
1619 setDesiredPosition(tmpHomePos
, posControl
.rthState
.homePosition
.heading
, NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_Z
| NAV_POS_UPDATE_HEADING
);
1621 posControl
.landingDelay
= 0;
1623 if (navConfig()->general
.flags
.rth_use_linear_descent
&& posControl
.rthState
.rthLinearDescentActive
)
1624 posControl
.rthState
.rthLinearDescentActive
= false;
1626 return NAV_FSM_EVENT_SUCCESS
; // NAV_STATE_RTH_HOVER_PRIOR_TO_LANDING
1628 setDesiredPosition(tmpHomePos
, 0, NAV_POS_UPDATE_Z
| NAV_POS_UPDATE_XY
);
1629 return NAV_FSM_EVENT_NONE
;
1632 /* Position sensor failure timeout - land */
1633 else if (checkForPositionSensorTimeout()) {
1634 return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
;
1636 /* No valid POS sensor but still within valid timeout - wait */
1637 return NAV_FSM_EVENT_NONE
;
1640 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_HOVER_PRIOR_TO_LANDING(navigationFSMState_t previousState
)
1642 UNUSED(previousState
);
1644 //On ROVER and BOAT we immediately switch to the next event
1645 if (!STATE(ALTITUDE_CONTROL
)) {
1646 return NAV_FSM_EVENT_SUCCESS
;
1649 /* If position sensors unavailable - land immediately (wait for timeout on GPS) */
1650 if ((posControl
.flags
.estHeadingStatus
== EST_NONE
) || checkForPositionSensorTimeout() || !validateRTHSanityChecker()) {
1651 return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
;
1654 // Action delay before landing if in FS and option enabled
1655 bool pauseLanding
= false;
1656 navRTHAllowLanding_e allow
= navConfig()->general
.flags
.rth_allow_landing
;
1657 if ((allow
== NAV_RTH_ALLOW_LANDING_ALWAYS
|| allow
== NAV_RTH_ALLOW_LANDING_FS_ONLY
) && FLIGHT_MODE(FAILSAFE_MODE
) && navConfig()->general
.rth_fs_landing_delay
> 0) {
1658 if (posControl
.landingDelay
== 0)
1659 posControl
.landingDelay
= millis() + S2MS(navConfig()->general
.rth_fs_landing_delay
);
1661 batteryState_e batteryState
= getBatteryState();
1663 if (millis() < posControl
.landingDelay
&& batteryState
!= BATTERY_WARNING
&& batteryState
!= BATTERY_CRITICAL
)
1664 pauseLanding
= true;
1666 posControl
.landingDelay
= 0;
1669 // If landing is not temporarily paused (FS only), position ok, OR within valid timeout - continue
1670 // Wait until target heading is reached for MR (with 15 deg margin for error), or continue for Fixed Wing
1671 if (!pauseLanding
&& ((ABS(wrap_18000(posControl
.rthState
.homePosition
.heading
- posControl
.actualState
.yaw
)) < DEGREES_TO_CENTIDEGREES(15)) || STATE(FIXED_WING_LEGACY
))) {
1672 resetLandingDetector(); // force reset landing detector just in case
1673 updateClimbRateToAltitudeController(0, 0, ROC_TO_ALT_RESET
);
1674 return navigationRTHAllowsLanding() ? NAV_FSM_EVENT_SUCCESS
: NAV_FSM_EVENT_SWITCH_TO_RTH_HOVER_ABOVE_HOME
; // success = land
1676 fpVector3_t
* tmpHomePos
= rthGetHomeTargetPosition(RTH_HOME_ENROUTE_FINAL
);
1677 setDesiredPosition(tmpHomePos
, posControl
.rthState
.homePosition
.heading
, NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_Z
| NAV_POS_UPDATE_HEADING
);
1678 return NAV_FSM_EVENT_NONE
;
1682 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_HOVER_ABOVE_HOME(navigationFSMState_t previousState
)
1684 UNUSED(previousState
);
1686 /* If position sensors unavailable - land immediately (wait for timeout on GPS) */
1687 if (posControl
.flags
.estHeadingStatus
== EST_NONE
|| checkForPositionSensorTimeout() || !validateRTHSanityChecker()) {
1688 return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
;
1691 fpVector3_t
* tmpHomePos
= rthGetHomeTargetPosition(RTH_HOME_FINAL_HOVER
);
1692 setDesiredPosition(tmpHomePos
, 0, NAV_POS_UPDATE_Z
);
1694 return NAV_FSM_EVENT_NONE
;
1697 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_LANDING(navigationFSMState_t previousState
)
1699 #ifndef USE_FW_AUTOLAND
1700 UNUSED(previousState
);
1703 //On ROVER and BOAT we immediately switch to the next event
1704 if (!STATE(ALTITUDE_CONTROL
)) {
1705 return NAV_FSM_EVENT_SUCCESS
;
1708 if (!ARMING_FLAG(ARMED
) || STATE(LANDING_DETECTED
)) {
1709 return NAV_FSM_EVENT_SUCCESS
;
1712 /* If position sensors unavailable - land immediately (wait for timeout on GPS)
1713 * Continue to check for RTH sanity during landing */
1714 if (posControl
.flags
.estHeadingStatus
== EST_NONE
|| checkForPositionSensorTimeout() || !validateRTHSanityChecker()) {
1715 return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
;
1718 if (checkMixerATRequired(MIXERAT_REQUEST_LAND
)){
1719 return NAV_FSM_EVENT_SWITCH_TO_MIXERAT
;
1722 #ifdef USE_FW_AUTOLAND
1723 if (STATE(AIRPLANE
)) {
1724 int8_t missionIdx
= -1, shIdx
= -1, missionFwLandConfigStartIdx
= 8;
1725 #ifdef USE_MULTI_MISSION
1726 missionIdx
= posControl
.loadedMultiMissionIndex
- 1;
1729 #ifdef USE_SAFE_HOME
1730 shIdx
= posControl
.safehomeState
.index
;
1731 missionFwLandConfigStartIdx
= MAX_SAFE_HOMES
;
1733 if (!posControl
.fwLandState
.landAborted
&& (shIdx
>= 0 || missionIdx
>= 0) && (fwAutolandApproachConfig(shIdx
)->landApproachHeading1
!= 0 || fwAutolandApproachConfig(shIdx
)->landApproachHeading2
!= 0)) {
1735 if (previousState
== NAV_STATE_WAYPOINT_REACHED
) {
1736 posControl
.fwLandState
.landPos
= posControl
.activeWaypoint
.pos
;
1737 posControl
.fwLandState
.approachSettingIdx
= missionFwLandConfigStartIdx
+ missionIdx
;
1738 posControl
.fwLandState
.landWp
= true;
1740 posControl
.fwLandState
.landPos
= posControl
.safehomeState
.nearestSafeHome
;
1741 posControl
.fwLandState
.approachSettingIdx
= shIdx
;
1742 posControl
.fwLandState
.landWp
= false;
1745 posControl
.fwLandState
.landAltAgl
= fwAutolandApproachConfig(posControl
.fwLandState
.approachSettingIdx
)->isSeaLevelRef
? fwAutolandApproachConfig(posControl
.fwLandState
.approachSettingIdx
)->landAlt
- GPS_home
.alt
: fwAutolandApproachConfig(posControl
.fwLandState
.approachSettingIdx
)->landAlt
;
1746 posControl
.fwLandState
.landAproachAltAgl
= fwAutolandApproachConfig(posControl
.fwLandState
.approachSettingIdx
)->isSeaLevelRef
? fwAutolandApproachConfig(posControl
.fwLandState
.approachSettingIdx
)->approachAlt
- GPS_home
.alt
: fwAutolandApproachConfig(posControl
.fwLandState
.approachSettingIdx
)->approachAlt
;
1747 return NAV_FSM_EVENT_SWITCH_TO_NAV_STATE_FW_LANDING
;
1752 float descentVelLimited
= 0;
1753 int32_t landingElevation
= posControl
.rthState
.homeTmpWaypoint
.z
;
1754 fpVector3_t tmpHomePos
= posControl
.rthState
.homeTmpWaypoint
;
1755 uint32_t remainingDistance
= calculateDistanceToDestination(&tmpHomePos
);
1757 // A safeguard - if surface altitude sensor is available and is reading < 50cm altitude - drop to min descend speed.
1758 // Also slow down to min descent speed during RTH MR landing if MR drifted too far away from home position.
1759 bool minDescentSpeedRequired
= (posControl
.flags
.estAglStatus
== EST_TRUSTED
&& posControl
.actualState
.agl
.pos
.z
< 50.0f
) ||
1760 (FLIGHT_MODE(NAV_RTH_MODE
) && STATE(MULTIROTOR
) && remainingDistance
> MR_RTH_LAND_MARGIN_CM
);
1762 // Do not allow descent velocity slower than -30cm/s so the landing detector works (limited by land_minalt_vspd).
1763 if (minDescentSpeedRequired
) {
1764 descentVelLimited
= navConfig()->general
.land_minalt_vspd
;
1766 // Ramp down descent velocity from max speed at maxAlt altitude to min speed from minAlt to 0cm.
1767 float descentVelScaled
= scaleRangef(navGetCurrentActualPositionAndVelocity()->pos
.z
,
1768 navConfig()->general
.land_slowdown_minalt
+ landingElevation
,
1769 navConfig()->general
.land_slowdown_maxalt
+ landingElevation
,
1770 navConfig()->general
.land_minalt_vspd
, navConfig()->general
.land_maxalt_vspd
);
1772 descentVelLimited
= constrainf(descentVelScaled
, navConfig()->general
.land_minalt_vspd
, navConfig()->general
.land_maxalt_vspd
);
1775 updateClimbRateToAltitudeController(-descentVelLimited
, 0, ROC_TO_ALT_CONSTANT
);
1777 return NAV_FSM_EVENT_NONE
;
1780 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_FINISHING(navigationFSMState_t previousState
)
1782 UNUSED(previousState
);
1784 //On ROVER and BOAT disarm immediately
1785 if (!STATE(ALTITUDE_CONTROL
)) {
1786 disarm(DISARM_NAVIGATION
);
1789 return NAV_FSM_EVENT_SUCCESS
;
1792 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_RTH_FINISHED(navigationFSMState_t previousState
)
1794 // Stay in this state
1795 UNUSED(previousState
);
1797 if (STATE(ALTITUDE_CONTROL
)) {
1798 updateClimbRateToAltitudeController(-1.1f
* navConfig()->general
.land_minalt_vspd
, 0, ROC_TO_ALT_CONSTANT
); // FIXME
1801 // Prevent I-terms growing when already landed
1802 pidResetErrorAccumulators();
1803 return NAV_FSM_EVENT_NONE
;
1806 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_WAYPOINT_INITIALIZE(navigationFSMState_t previousState
)
1808 UNUSED(previousState
);
1810 if (!posControl
.waypointCount
|| !posControl
.waypointListValid
) {
1811 return NAV_FSM_EVENT_ERROR
;
1814 // Prepare controllers
1815 resetPositionController();
1816 resetAltitudeController(false); // Make sure surface tracking is not enabled - WP uses global altitude, not AGL
1818 if (posControl
.activeWaypointIndex
== posControl
.startWpIndex
|| posControl
.wpMissionRestart
) {
1819 /* Use p3 as the volatile jump counter, allowing embedded, rearmed jumps
1820 Using p3 minimises the risk of saving an invalid counter if a mission is aborted */
1821 setupJumpCounters();
1822 posControl
.activeWaypointIndex
= posControl
.startWpIndex
;
1823 wpHeadingControl
.mode
= NAV_WP_HEAD_MODE_NONE
;
1826 if (navConfig()->general
.flags
.waypoint_mission_restart
== WP_MISSION_SWITCH
) {
1827 posControl
.wpMissionRestart
= posControl
.activeWaypointIndex
> posControl
.startWpIndex
? !posControl
.wpMissionRestart
: false;
1829 posControl
.wpMissionRestart
= navConfig()->general
.flags
.waypoint_mission_restart
== WP_MISSION_START
;
1832 return NAV_FSM_EVENT_SUCCESS
; // will switch to NAV_STATE_WAYPOINT_PRE_ACTION
1835 static navigationFSMEvent_t
nextForNonGeoStates(void)
1837 /* simple helper for non-geographical states that just set other data */
1838 if (isLastMissionWaypoint()) { // non-geo state is the last waypoint, switch to finish.
1839 return NAV_FSM_EVENT_SWITCH_TO_WAYPOINT_FINISHED
;
1840 } else { // Finished non-geo, move to next WP
1841 posControl
.activeWaypointIndex
++;
1842 return NAV_FSM_EVENT_NONE
; // re-process the state passing to the next WP
1846 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_WAYPOINT_PRE_ACTION(navigationFSMState_t previousState
)
1848 /* A helper function to do waypoint-specific action */
1849 UNUSED(previousState
);
1851 switch ((navWaypointActions_e
)posControl
.waypointList
[posControl
.activeWaypointIndex
].action
) {
1852 case NAV_WP_ACTION_HOLD_TIME
:
1853 case NAV_WP_ACTION_WAYPOINT
:
1854 case NAV_WP_ACTION_LAND
:
1855 calculateAndSetActiveWaypoint(&posControl
.waypointList
[posControl
.activeWaypointIndex
]);
1856 posControl
.wpInitialDistance
= calculateDistanceToDestination(&posControl
.activeWaypoint
.pos
);
1857 posControl
.wpInitialAltitude
= posControl
.actualState
.abs
.pos
.z
;
1858 posControl
.wpAltitudeReached
= false;
1859 return NAV_FSM_EVENT_SUCCESS
; // will switch to NAV_STATE_WAYPOINT_IN_PROGRESS
1861 case NAV_WP_ACTION_JUMP
:
1862 // We use p3 as the volatile jump counter (p2 is the static value)
1863 if (posControl
.waypointList
[posControl
.activeWaypointIndex
].p3
!= -1) {
1864 if (posControl
.waypointList
[posControl
.activeWaypointIndex
].p3
== 0) {
1866 return nextForNonGeoStates();
1870 posControl
.waypointList
[posControl
.activeWaypointIndex
].p3
--;
1873 posControl
.activeWaypointIndex
= posControl
.waypointList
[posControl
.activeWaypointIndex
].p1
+ posControl
.startWpIndex
;
1874 return NAV_FSM_EVENT_NONE
; // re-process the state passing to the next WP
1876 case NAV_WP_ACTION_SET_POI
:
1877 if (STATE(MULTIROTOR
)) {
1878 wpHeadingControl
.mode
= NAV_WP_HEAD_MODE_POI
;
1879 mapWaypointToLocalPosition(&wpHeadingControl
.poi_pos
,
1880 &posControl
.waypointList
[posControl
.activeWaypointIndex
], GEO_ALT_RELATIVE
);
1882 return nextForNonGeoStates();
1884 case NAV_WP_ACTION_SET_HEAD
:
1885 if (STATE(MULTIROTOR
)) {
1886 if (posControl
.waypointList
[posControl
.activeWaypointIndex
].p1
< 0 ||
1887 posControl
.waypointList
[posControl
.activeWaypointIndex
].p1
> 359) {
1888 wpHeadingControl
.mode
= NAV_WP_HEAD_MODE_NONE
;
1890 wpHeadingControl
.mode
= NAV_WP_HEAD_MODE_FIXED
;
1891 wpHeadingControl
.heading
= DEGREES_TO_CENTIDEGREES(posControl
.waypointList
[posControl
.activeWaypointIndex
].p1
);
1894 return nextForNonGeoStates();
1896 case NAV_WP_ACTION_RTH
:
1897 posControl
.wpMissionRestart
= true;
1898 return NAV_FSM_EVENT_SWITCH_TO_RTH
;
1904 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_WAYPOINT_IN_PROGRESS(navigationFSMState_t previousState
)
1906 UNUSED(previousState
);
1908 // If no position sensor available - land immediately
1909 if ((posControl
.flags
.estPosStatus
>= EST_USABLE
) && (posControl
.flags
.estHeadingStatus
>= EST_USABLE
)) {
1910 switch ((navWaypointActions_e
)posControl
.waypointList
[posControl
.activeWaypointIndex
].action
) {
1911 case NAV_WP_ACTION_HOLD_TIME
:
1912 case NAV_WP_ACTION_WAYPOINT
:
1913 case NAV_WP_ACTION_LAND
:
1914 if (isWaypointReached(&posControl
.activeWaypoint
.pos
, &posControl
.activeWaypoint
.bearing
)) {
1915 return NAV_FSM_EVENT_SUCCESS
; // will switch to NAV_STATE_WAYPOINT_REACHED
1918 fpVector3_t tmpWaypoint
;
1919 tmpWaypoint
.x
= posControl
.activeWaypoint
.pos
.x
;
1920 tmpWaypoint
.y
= posControl
.activeWaypoint
.pos
.y
;
1921 tmpWaypoint
.z
= scaleRangef(constrainf(posControl
.wpDistance
, posControl
.wpInitialDistance
/ 10.0f
, posControl
.wpInitialDistance
),
1922 posControl
.wpInitialDistance
, posControl
.wpInitialDistance
/ 10.0f
,
1923 posControl
.wpInitialAltitude
, posControl
.activeWaypoint
.pos
.z
);
1924 setDesiredPosition(&tmpWaypoint
, 0, NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_Z
| NAV_POS_UPDATE_BEARING
);
1925 if(STATE(MULTIROTOR
)) {
1926 switch (wpHeadingControl
.mode
) {
1927 case NAV_WP_HEAD_MODE_NONE
:
1929 case NAV_WP_HEAD_MODE_FIXED
:
1930 setDesiredPosition(NULL
, wpHeadingControl
.heading
, NAV_POS_UPDATE_HEADING
);
1932 case NAV_WP_HEAD_MODE_POI
:
1933 setDesiredPosition(&wpHeadingControl
.poi_pos
, 0, NAV_POS_UPDATE_BEARING
);
1937 return NAV_FSM_EVENT_NONE
; // will re-process state in >10ms
1941 case NAV_WP_ACTION_JUMP
:
1942 case NAV_WP_ACTION_SET_HEAD
:
1943 case NAV_WP_ACTION_SET_POI
:
1944 case NAV_WP_ACTION_RTH
:
1948 /* If position sensors unavailable - land immediately (wait for timeout on GPS) */
1949 else if (checkForPositionSensorTimeout() || (posControl
.flags
.estHeadingStatus
== EST_NONE
)) {
1950 return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
;
1953 return NAV_FSM_EVENT_NONE
; // will re-process state in >10ms
1956 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_WAYPOINT_REACHED(navigationFSMState_t previousState
)
1958 UNUSED(previousState
);
1960 if (navConfig()->general
.waypoint_enforce_altitude
) {
1961 posControl
.wpAltitudeReached
= isWaypointAltitudeReached();
1964 switch ((navWaypointActions_e
)posControl
.waypointList
[posControl
.activeWaypointIndex
].action
) {
1965 case NAV_WP_ACTION_WAYPOINT
:
1966 if (navConfig()->general
.waypoint_enforce_altitude
&& !posControl
.wpAltitudeReached
) {
1967 return NAV_FSM_EVENT_SWITCH_TO_WAYPOINT_HOLD_TIME
;
1969 return NAV_FSM_EVENT_SUCCESS
; // NAV_STATE_WAYPOINT_NEXT
1972 case NAV_WP_ACTION_JUMP
:
1973 case NAV_WP_ACTION_SET_HEAD
:
1974 case NAV_WP_ACTION_SET_POI
:
1975 case NAV_WP_ACTION_RTH
:
1978 case NAV_WP_ACTION_LAND
:
1979 return NAV_FSM_EVENT_SWITCH_TO_WAYPOINT_RTH_LAND
;
1981 case NAV_WP_ACTION_HOLD_TIME
:
1982 // Save the current time for the time the waypoint was reached
1983 posControl
.wpReachedTime
= millis();
1984 return NAV_FSM_EVENT_SWITCH_TO_WAYPOINT_HOLD_TIME
;
1990 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_WAYPOINT_HOLD_TIME(navigationFSMState_t previousState
)
1992 UNUSED(previousState
);
1994 /* If position sensors unavailable - land immediately (wait for timeout on GPS) */
1995 if (posControl
.flags
.estHeadingStatus
== EST_NONE
|| checkForPositionSensorTimeout()) {
1996 return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
;
1999 if (navConfig()->general
.waypoint_enforce_altitude
&& !posControl
.wpAltitudeReached
) {
2000 // Adjust altitude to waypoint setting
2001 setDesiredPosition(&posControl
.activeWaypoint
.pos
, 0, NAV_POS_UPDATE_Z
);
2003 posControl
.wpAltitudeReached
= isWaypointAltitudeReached();
2005 if (posControl
.wpAltitudeReached
) {
2006 posControl
.wpReachedTime
= millis();
2008 return NAV_FSM_EVENT_NONE
;
2012 timeMs_t currentTime
= millis();
2014 if (posControl
.waypointList
[posControl
.activeWaypointIndex
].p1
<= 0 ||
2015 posControl
.waypointList
[posControl
.activeWaypointIndex
].action
== NAV_WP_ACTION_WAYPOINT
||
2016 (posControl
.wpReachedTime
!= 0 && currentTime
- posControl
.wpReachedTime
>= (timeMs_t
)posControl
.waypointList
[posControl
.activeWaypointIndex
].p1
*1000L)) {
2017 return NAV_FSM_EVENT_SUCCESS
;
2020 return NAV_FSM_EVENT_NONE
; // will re-process state in >10ms
2023 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_WAYPOINT_RTH_LAND(navigationFSMState_t previousState
)
2025 UNUSED(previousState
);
2027 const navigationFSMEvent_t landEvent
= navOnEnteringState_NAV_STATE_RTH_LANDING(previousState
);
2029 if (landEvent
== NAV_FSM_EVENT_SWITCH_TO_NAV_STATE_FW_LANDING
) {
2030 return NAV_FSM_EVENT_SWITCH_TO_NAV_STATE_FW_LANDING
;
2031 } else if (landEvent
== NAV_FSM_EVENT_SWITCH_TO_RTH_HOVER_ABOVE_HOME
) {
2032 return NAV_FSM_EVENT_SWITCH_TO_WAYPOINT_FINISHED
;
2033 } else if (landEvent
== NAV_FSM_EVENT_SUCCESS
) {
2034 // Landing controller returned success - invoke RTH finishing state and finish the waypoint
2035 navOnEnteringState_NAV_STATE_RTH_FINISHING(previousState
);
2036 return NAV_FSM_EVENT_SUCCESS
;
2039 return NAV_FSM_EVENT_NONE
;
2043 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_WAYPOINT_NEXT(navigationFSMState_t previousState
)
2045 UNUSED(previousState
);
2047 if (isLastMissionWaypoint()) { // Last waypoint reached
2048 return NAV_FSM_EVENT_SWITCH_TO_WAYPOINT_FINISHED
;
2051 // Waypoint reached, do something and move on to next waypoint
2052 posControl
.activeWaypointIndex
++;
2053 return NAV_FSM_EVENT_SUCCESS
; // will switch to NAV_STATE_WAYPOINT_PRE_ACTION
2057 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_WAYPOINT_FINISHED(navigationFSMState_t previousState
)
2059 UNUSED(previousState
);
2061 clearJumpCounters();
2062 posControl
.wpMissionRestart
= true;
2064 /* If position sensors unavailable - land immediately (wait for timeout on GPS) */
2065 if (posControl
.flags
.estHeadingStatus
== EST_NONE
|| checkForPositionSensorTimeout()) {
2066 return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
;
2069 return NAV_FSM_EVENT_NONE
; // will re-process state in >10ms
2072 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_EMERGENCY_LANDING_INITIALIZE(navigationFSMState_t previousState
)
2074 UNUSED(previousState
);
2076 #ifdef USE_FW_AUTOLAND
2077 posControl
.fwLandState
.landState
= FW_AUTOLAND_STATE_IDLE
;
2080 if ((posControl
.flags
.estPosStatus
>= EST_USABLE
)) {
2081 resetPositionController();
2082 setDesiredPosition(&navGetCurrentActualPositionAndVelocity()->pos
, 0, NAV_POS_UPDATE_XY
);
2085 // Emergency landing MAY use common altitude controller if vertical position is valid - initialize it
2086 // Make sure terrain following is not enabled
2087 resetAltitudeController(false);
2089 return NAV_FSM_EVENT_SUCCESS
;
2092 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_EMERGENCY_LANDING_IN_PROGRESS(navigationFSMState_t previousState
)
2094 UNUSED(previousState
);
2096 // Reset target position if too far away for some reason, e.g. GPS recovered since start landing.
2097 if (posControl
.flags
.estPosStatus
>= EST_USABLE
) {
2098 float targetPosLimit
= STATE(MULTIROTOR
) ? 2000.0f
: navConfig()->fw
.loiter_radius
* 2.0f
;
2099 if (calculateDistanceToDestination(&posControl
.desiredState
.pos
) > targetPosLimit
) {
2100 setDesiredPosition(&navGetCurrentActualPositionAndVelocity()->pos
, 0, NAV_POS_UPDATE_XY
);
2104 if (STATE(LANDING_DETECTED
)) {
2105 return NAV_FSM_EVENT_SUCCESS
;
2108 return NAV_FSM_EVENT_NONE
;
2111 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_EMERGENCY_LANDING_FINISHED(navigationFSMState_t previousState
)
2113 UNUSED(previousState
);
2115 return NAV_FSM_EVENT_NONE
;
2118 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_LAUNCH_INITIALIZE(navigationFSMState_t previousState
)
2120 const timeUs_t currentTimeUs
= micros();
2121 UNUSED(previousState
);
2123 resetFixedWingLaunchController(currentTimeUs
);
2125 return NAV_FSM_EVENT_SUCCESS
; // NAV_STATE_LAUNCH_WAIT
2128 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_LAUNCH_WAIT(navigationFSMState_t previousState
)
2130 const timeUs_t currentTimeUs
= micros();
2131 UNUSED(previousState
);
2133 // Continue immediately to launch in progress if manual launch throttle used
2134 if (navConfig()->fw
.launch_manual_throttle
) {
2135 return NAV_FSM_EVENT_SUCCESS
;
2138 if (fixedWingLaunchStatus() == FW_LAUNCH_DETECTED
) {
2139 enableFixedWingLaunchController(currentTimeUs
);
2140 return NAV_FSM_EVENT_SUCCESS
; // NAV_STATE_LAUNCH_IN_PROGRESS
2143 // abort NAV_LAUNCH_MODE by moving sticks with low throttle or throttle stick < launch idle throttle
2144 if (abortLaunchAllowed() && isRollPitchStickDeflected(navConfig()->fw
.launch_land_abort_deadband
)) {
2145 abortFixedWingLaunch();
2146 return NAV_FSM_EVENT_SWITCH_TO_IDLE
;
2149 return NAV_FSM_EVENT_NONE
;
2152 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_LAUNCH_IN_PROGRESS(navigationFSMState_t previousState
)
2154 UNUSED(previousState
);
2156 if (fixedWingLaunchStatus() >= FW_LAUNCH_ABORTED
) {
2157 return NAV_FSM_EVENT_SUCCESS
;
2160 return NAV_FSM_EVENT_NONE
;
2163 navigationFSMState_t navMixerATPendingState
= NAV_STATE_IDLE
;
2164 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_MIXERAT_INITIALIZE(navigationFSMState_t previousState
)
2166 const navigationFSMStateFlags_t prevFlags
= navGetStateFlags(previousState
);
2168 // Prepare altitude controller if idle, RTH or WP modes active or surface mode status changed
2169 if (!(prevFlags
& NAV_CTL_ALT
) || (prevFlags
& NAV_AUTO_RTH
) || (prevFlags
& NAV_AUTO_WP
)) {
2170 resetAltitudeController(false);
2171 setupAltitudeController();
2173 setDesiredPosition(&navGetCurrentActualPositionAndVelocity()->pos
, posControl
.actualState
.yaw
, NAV_POS_UPDATE_Z
);
2174 navMixerATPendingState
= previousState
;
2175 return NAV_FSM_EVENT_SUCCESS
;
2178 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_MIXERAT_IN_PROGRESS(navigationFSMState_t previousState
)
2180 UNUSED(previousState
);
2181 mixerProfileATRequest_e required_action
;
2182 switch (navMixerATPendingState
)
2184 case NAV_STATE_RTH_HEAD_HOME
:
2185 required_action
= MIXERAT_REQUEST_RTH
;
2187 case NAV_STATE_RTH_LANDING
:
2188 required_action
= MIXERAT_REQUEST_LAND
;
2191 required_action
= MIXERAT_REQUEST_NONE
;
2194 if (mixerATUpdateState(required_action
)){
2195 // MixerAT is done, switch to next state
2196 resetPositionController();
2197 resetAltitudeController(false); // Make sure surface tracking is not enabled uses global altitude, not AGL
2198 mixerATUpdateState(MIXERAT_REQUEST_ABORT
);
2199 switch (navMixerATPendingState
)
2201 case NAV_STATE_RTH_HEAD_HOME
:
2202 setupAltitudeController();
2203 return NAV_FSM_EVENT_SWITCH_TO_RTH_HEAD_HOME
;
2205 case NAV_STATE_RTH_LANDING
:
2206 setupAltitudeController();
2207 return NAV_FSM_EVENT_SWITCH_TO_RTH_LANDING
;
2210 return NAV_FSM_EVENT_SWITCH_TO_IDLE
;
2215 setDesiredPosition(&navGetCurrentActualPositionAndVelocity()->pos
, posControl
.actualState
.yaw
, NAV_POS_UPDATE_Z
);
2217 return NAV_FSM_EVENT_NONE
;
2220 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_MIXERAT_ABORT(navigationFSMState_t previousState
)
2222 UNUSED(previousState
);
2223 mixerATUpdateState(MIXERAT_REQUEST_ABORT
);
2224 return NAV_FSM_EVENT_SUCCESS
;
2227 #ifdef USE_FW_AUTOLAND
2228 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_FW_LANDING_CLIMB_TO_LOITER(navigationFSMState_t previousState
)
2230 UNUSED(previousState
);
2232 if (isRollPitchStickDeflected(navConfig()->fw
.launch_land_abort_deadband
)) {
2233 return NAV_FSM_EVENT_SWITCH_TO_NAV_STATE_FW_LANDING_ABORT
;
2236 if (posControl
.fwLandState
.loiterStartTime
== 0) {
2237 posControl
.fwLandState
.loiterStartTime
= micros();
2240 if (ABS(getEstimatedActualPosition(Z
) - posControl
.fwLandState
.landAproachAltAgl
) < (navConfig()->general
.waypoint_enforce_altitude
> 0 ? navConfig()->general
.waypoint_enforce_altitude
: FW_LAND_LOITER_ALT_TOLERANCE
)) {
2241 updateClimbRateToAltitudeController(0, 0, ROC_TO_ALT_RESET
);
2242 posControl
.fwLandState
.landState
= FW_AUTOLAND_STATE_LOITER
;
2243 return NAV_FSM_EVENT_SUCCESS
;
2246 fpVector3_t tmpHomePos
= posControl
.rthState
.homePosition
.pos
;
2247 tmpHomePos
.z
= posControl
.fwLandState
.landAproachAltAgl
;
2248 setDesiredPosition(&tmpHomePos
, 0, NAV_POS_UPDATE_Z
);
2250 return NAV_FSM_EVENT_NONE
;
2253 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_FW_LANDING_LOITER(navigationFSMState_t previousState
)
2255 UNUSED(previousState
);
2256 /* If position sensors unavailable - land immediately (wait for timeout on GPS) */
2257 if ((posControl
.flags
.estHeadingStatus
== EST_NONE
) || checkForPositionSensorTimeout()) {
2258 return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
;
2261 if (isRollPitchStickDeflected(navConfig()->fw
.launch_land_abort_deadband
)) {
2262 return NAV_FSM_EVENT_SWITCH_TO_NAV_STATE_FW_LANDING_ABORT
;
2265 if (micros() - posControl
.fwLandState
.loiterStartTime
> FW_LAND_LOITER_MIN_TIME
) {
2266 if (isEstimatedWindSpeedValid()) {
2268 uint16_t windAngle
= 0;
2269 int32_t approachHeading
= -1;
2270 float windSpeed
= getEstimatedHorizontalWindSpeed(&windAngle
);
2271 windAngle
= wrap_36000(windAngle
+ 18000);
2273 // Ignore low wind speed, could be the error of the wind estimator
2274 if (windSpeed
< navFwAutolandConfig()->maxTailwind
) {
2275 if (fwAutolandApproachConfig(posControl
.fwLandState
.approachSettingIdx
)->landApproachHeading1
!= 0) {
2276 approachHeading
= posControl
.fwLandState
.landingDirection
= ABS(DEGREES_TO_CENTIDEGREES(fwAutolandApproachConfig(posControl
.fwLandState
.approachSettingIdx
)->landApproachHeading1
));
2277 } else if ((fwAutolandApproachConfig(posControl
.fwLandState
.approachSettingIdx
)->landApproachHeading2
!= 0) ) {
2278 approachHeading
= posControl
.fwLandState
.landingDirection
= ABS(DEGREES_TO_CENTIDEGREES(fwAutolandApproachConfig(posControl
.fwLandState
.approachSettingIdx
)->landApproachHeading2
));
2281 int32_t heading1
= calcFinalApproachHeading(DEGREES_TO_CENTIDEGREES(fwAutolandApproachConfig(posControl
.fwLandState
.approachSettingIdx
)->landApproachHeading1
), windAngle
);
2282 int32_t heading2
= calcFinalApproachHeading(DEGREES_TO_CENTIDEGREES(fwAutolandApproachConfig(posControl
.fwLandState
.approachSettingIdx
)->landApproachHeading2
), windAngle
);
2284 if (heading1
== heading2
|| heading1
== wrap_36000(heading2
+ 18000)) {
2288 if (heading1
== -1 && heading2
>= 0) {
2289 posControl
.fwLandState
.landingDirection
= heading2
;
2290 approachHeading
= DEGREES_TO_CENTIDEGREES(fwAutolandApproachConfig(posControl
.fwLandState
.approachSettingIdx
)->landApproachHeading2
);
2291 } else if (heading1
>= 0 && heading2
== -1) {
2292 posControl
.fwLandState
.landingDirection
= heading1
;
2293 approachHeading
= DEGREES_TO_CENTIDEGREES(fwAutolandApproachConfig(posControl
.fwLandState
.approachSettingIdx
)->landApproachHeading1
);
2295 if (calcWindDiff(heading1
, windAngle
) < calcWindDiff(heading2
, windAngle
)) {
2296 posControl
.fwLandState
.landingDirection
= heading1
;
2297 approachHeading
= DEGREES_TO_CENTIDEGREES(fwAutolandApproachConfig(posControl
.fwLandState
.approachSettingIdx
)->landApproachHeading1
);
2299 posControl
.fwLandState
.landingDirection
= heading2
;
2300 approachHeading
= DEGREES_TO_CENTIDEGREES(fwAutolandApproachConfig(posControl
.fwLandState
.approachSettingIdx
)->landApproachHeading2
);
2305 if (posControl
.fwLandState
.landingDirection
>= 0) {
2308 int32_t finalApproachAlt
= posControl
.fwLandState
.landAproachAltAgl
/ 3 * 2;
2310 if (fwAutolandApproachConfig(posControl
.fwLandState
.approachSettingIdx
)->approachDirection
== FW_AUTOLAND_APPROACH_DIRECTION_LEFT
) {
2311 dir
= wrap_36000(ABS(approachHeading
) - 9000);
2313 dir
= wrap_36000(ABS(approachHeading
) + 9000);
2316 calculateFarAwayPos(&tmpPos
, &posControl
.fwLandState
.landPos
, posControl
.fwLandState
.landingDirection
, navFwAutolandConfig()->approachLength
);
2317 tmpPos
.z
= posControl
.fwLandState
.landAltAgl
- finalApproachAlt
;
2318 posControl
.fwLandState
.landWaypoints
[FW_AUTOLAND_WP_LAND
] = tmpPos
;
2320 calculateFarAwayPos(&tmpPos
, &posControl
.fwLandState
.landPos
, wrap_36000(posControl
.fwLandState
.landingDirection
+ 18000), navFwAutolandConfig()->approachLength
);
2321 tmpPos
.z
= finalApproachAlt
;
2322 posControl
.fwLandState
.landWaypoints
[FW_AUTOLAND_WP_FINAL_APPROACH
] = tmpPos
;
2324 calculateFarAwayPos(&tmpPos
, &posControl
.fwLandState
.landWaypoints
[FW_AUTOLAND_WP_FINAL_APPROACH
], dir
, MAX((uint32_t)navConfig()->fw
.loiter_radius
* 4, navFwAutolandConfig()->approachLength
/ 2));
2325 tmpPos
.z
= posControl
.fwLandState
.landAproachAltAgl
;
2326 posControl
.fwLandState
.landWaypoints
[FW_AUTOLAND_WP_TURN
] = tmpPos
;
2328 setLandWaypoint(&posControl
.fwLandState
.landWaypoints
[FW_AUTOLAND_WP_TURN
], &posControl
.fwLandState
.landWaypoints
[FW_AUTOLAND_WP_FINAL_APPROACH
]);
2329 posControl
.fwLandState
.landCurrentWp
= FW_AUTOLAND_WP_TURN
;
2330 posControl
.fwLandState
.landState
= FW_AUTOLAND_STATE_DOWNWIND
;
2332 return NAV_FSM_EVENT_SUCCESS
;
2334 posControl
.fwLandState
.loiterStartTime
= micros();
2337 posControl
.fwLandState
.loiterStartTime
= micros();
2341 fpVector3_t tmpPoint
= posControl
.fwLandState
.landPos
;
2342 tmpPoint
.z
= posControl
.fwLandState
.landAproachAltAgl
;
2343 setDesiredPosition(&tmpPoint
, posControl
.fwLandState
.landPosHeading
, NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_Z
| NAV_POS_UPDATE_HEADING
);
2345 return NAV_FSM_EVENT_NONE
;
2347 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_FW_LANDING_APPROACH(navigationFSMState_t previousState
)
2349 UNUSED(previousState
);
2351 if ((posControl
.flags
.estHeadingStatus
== EST_NONE
) || checkForPositionSensorTimeout()) {
2352 return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
;
2355 if (isRollPitchStickDeflected(navConfig()->fw
.launch_land_abort_deadband
)) {
2356 return NAV_FSM_EVENT_SWITCH_TO_NAV_STATE_FW_LANDING_ABORT
;
2359 if (isLandingDetected()) {
2360 posControl
.fwLandState
.landState
= FW_AUTOLAND_STATE_IDLE
;
2361 disarm(DISARM_LANDING
);
2362 return NAV_FSM_EVENT_SWITCH_TO_IDLE
;
2365 if (getLandAltitude() <= fwAutolandApproachConfig(posControl
.fwLandState
.approachSettingIdx
)->landAlt
+ navFwAutolandConfig()->glideAltitude
- (fwAutolandApproachConfig(posControl
.fwLandState
.approachSettingIdx
)->isSeaLevelRef
? GPS_home
.alt
: 0)) {
2366 resetPositionController();
2367 posControl
.cruise
.course
= posControl
.fwLandState
.landingDirection
;
2368 posControl
.cruise
.previousCourse
= posControl
.cruise
.course
;
2369 posControl
.cruise
.lastCourseAdjustmentTime
= 0;
2370 posControl
.fwLandState
.landState
= FW_AUTOLAND_STATE_GLIDE
;
2371 return NAV_FSM_EVENT_SUCCESS
;
2372 } else if (isWaypointReached(&posControl
.fwLandState
.landWaypoints
[posControl
.fwLandState
.landCurrentWp
], &posControl
.activeWaypoint
.bearing
)) {
2373 if (posControl
.fwLandState
.landCurrentWp
== FW_AUTOLAND_WP_TURN
) {
2374 setLandWaypoint(&posControl
.fwLandState
.landWaypoints
[FW_AUTOLAND_WP_FINAL_APPROACH
], &posControl
.fwLandState
.landWaypoints
[FW_AUTOLAND_WP_LAND
]);
2375 posControl
.fwLandState
.landCurrentWp
= FW_AUTOLAND_WP_FINAL_APPROACH
;
2376 posControl
.fwLandState
.landState
= FW_AUTOLAND_STATE_BASE_LEG
;
2377 return NAV_FSM_EVENT_NONE
;
2378 } else if (posControl
.fwLandState
.landCurrentWp
== FW_AUTOLAND_WP_FINAL_APPROACH
) {
2379 setLandWaypoint(&posControl
.fwLandState
.landWaypoints
[FW_AUTOLAND_WP_LAND
], NULL
);
2380 posControl
.fwLandState
.landCurrentWp
= FW_AUTOLAND_WP_LAND
;
2381 posControl
.fwLandState
.landState
= FW_AUTOLAND_STATE_FINAL_APPROACH
;
2382 return NAV_FSM_EVENT_NONE
;
2386 fpVector3_t tmpWaypoint
;
2387 tmpWaypoint
.x
= posControl
.activeWaypoint
.pos
.x
;
2388 tmpWaypoint
.y
= posControl
.activeWaypoint
.pos
.y
;
2389 tmpWaypoint
.z
= scaleRangef(constrainf(posControl
.wpDistance
, posControl
.wpInitialDistance
/ 10.0f
, posControl
.wpInitialDistance
),
2390 posControl
.wpInitialDistance
, posControl
.wpInitialDistance
/ 10.0f
,
2391 posControl
.wpInitialAltitude
, posControl
.activeWaypoint
.pos
.z
);
2392 setDesiredPosition(&tmpWaypoint
, 0, NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_Z
| NAV_POS_UPDATE_BEARING
);
2394 return NAV_FSM_EVENT_NONE
;
2397 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_FW_LANDING_GLIDE(navigationFSMState_t previousState
)
2399 UNUSED(previousState
);
2401 if (isRollPitchStickDeflected(navConfig()->fw
.launch_land_abort_deadband
)) {
2402 return NAV_FSM_EVENT_SWITCH_TO_NAV_STATE_FW_LANDING_ABORT
;
2405 if (getLandAltitude() <= posControl
.fwLandState
.landAltAgl
+ navFwAutolandConfig()->flareAltitude
) {
2406 posControl
.fwLandState
.landState
= FW_AUTOLAND_STATE_FLARE
;
2407 return NAV_FSM_EVENT_SUCCESS
;
2410 if (isLandingDetected()) {
2411 posControl
.fwLandState
.landState
= FW_AUTOLAND_STATE_IDLE
;
2412 disarm(DISARM_LANDING
);
2413 return NAV_FSM_EVENT_SWITCH_TO_IDLE
;
2416 setDesiredPosition(NULL
, posControl
.cruise
.course
, NAV_POS_UPDATE_HEADING
);
2417 return NAV_FSM_EVENT_NONE
;
2420 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_FW_LANDING_FLARE(navigationFSMState_t previousState
)
2422 UNUSED(previousState
);
2424 if (isLandingDetected()) {
2425 posControl
.fwLandState
.landState
= FW_AUTOLAND_STATE_IDLE
;
2426 disarm(DISARM_LANDING
);
2427 return NAV_FSM_EVENT_SUCCESS
;
2429 setDesiredPosition(NULL
, posControl
.cruise
.course
, NAV_POS_UPDATE_HEADING
);
2431 return NAV_FSM_EVENT_NONE
;
2434 static navigationFSMEvent_t
navOnEnteringState_NAV_STATE_FW_LANDING_ABORT(navigationFSMState_t previousState
)
2436 UNUSED(previousState
);
2437 posControl
.fwLandState
.landAborted
= true;
2438 posControl
.fwLandState
.landState
= FW_AUTOLAND_STATE_IDLE
;
2440 return posControl
.fwLandState
.landWp
? NAV_FSM_EVENT_SWITCH_TO_WAYPOINT
: NAV_FSM_EVENT_SWITCH_TO_RTH
;
2444 static navigationFSMState_t
navSetNewFSMState(navigationFSMState_t newState
)
2446 navigationFSMState_t previousState
;
2448 previousState
= posControl
.navState
;
2449 if (posControl
.navState
!= newState
) {
2450 posControl
.navState
= newState
;
2451 posControl
.navPersistentId
= navFSM
[newState
].persistentId
;
2453 return previousState
;
2456 static void navProcessFSMEvents(navigationFSMEvent_t injectedEvent
)
2458 const timeMs_t currentMillis
= millis();
2459 navigationFSMState_t previousState
= NAV_STATE_UNDEFINED
;
2460 static timeMs_t lastStateProcessTime
= 0;
2462 /* Process new injected event if event defined,
2463 * otherwise process timeout event if defined */
2464 if (injectedEvent
!= NAV_FSM_EVENT_NONE
&& navFSM
[posControl
.navState
].onEvent
[injectedEvent
] != NAV_STATE_UNDEFINED
) {
2466 previousState
= navSetNewFSMState(navFSM
[posControl
.navState
].onEvent
[injectedEvent
]);
2467 } else if ((navFSM
[posControl
.navState
].timeoutMs
> 0) && (navFSM
[posControl
.navState
].onEvent
[NAV_FSM_EVENT_TIMEOUT
] != NAV_STATE_UNDEFINED
) &&
2468 ((currentMillis
- lastStateProcessTime
) >= navFSM
[posControl
.navState
].timeoutMs
)) {
2470 previousState
= navSetNewFSMState(navFSM
[posControl
.navState
].onEvent
[NAV_FSM_EVENT_TIMEOUT
]);
2473 if (previousState
) { /* If state updated call new state's entry function */
2474 while (navFSM
[posControl
.navState
].onEntry
) {
2475 navigationFSMEvent_t newEvent
= navFSM
[posControl
.navState
].onEntry(previousState
);
2477 if ((newEvent
!= NAV_FSM_EVENT_NONE
) && (navFSM
[posControl
.navState
].onEvent
[newEvent
] != NAV_STATE_UNDEFINED
)) {
2478 previousState
= navSetNewFSMState(navFSM
[posControl
.navState
].onEvent
[newEvent
]);
2485 lastStateProcessTime
= currentMillis
;
2488 /* Update public system state information */
2489 NAV_Status
.mode
= MW_GPS_MODE_NONE
;
2491 if (ARMING_FLAG(ARMED
)) {
2492 navigationFSMStateFlags_t navStateFlags
= navGetStateFlags(posControl
.navState
);
2494 if (navStateFlags
& NAV_AUTO_RTH
) {
2495 NAV_Status
.mode
= MW_GPS_MODE_RTH
;
2497 else if (navStateFlags
& NAV_AUTO_WP
) {
2498 NAV_Status
.mode
= MW_GPS_MODE_NAV
;
2500 else if (navStateFlags
& NAV_CTL_EMERG
) {
2501 NAV_Status
.mode
= MW_GPS_MODE_EMERG
;
2503 else if (navStateFlags
& NAV_CTL_POS
) {
2504 NAV_Status
.mode
= MW_GPS_MODE_HOLD
;
2508 NAV_Status
.state
= navFSM
[posControl
.navState
].mwState
;
2509 NAV_Status
.error
= navFSM
[posControl
.navState
].mwError
;
2511 NAV_Status
.flags
= 0;
2512 if (posControl
.flags
.isAdjustingPosition
) NAV_Status
.flags
|= MW_NAV_FLAG_ADJUSTING_POSITION
;
2513 if (posControl
.flags
.isAdjustingAltitude
) NAV_Status
.flags
|= MW_NAV_FLAG_ADJUSTING_ALTITUDE
;
2515 NAV_Status
.activeWpIndex
= posControl
.activeWaypointIndex
- posControl
.startWpIndex
;
2516 NAV_Status
.activeWpNumber
= NAV_Status
.activeWpIndex
+ 1;
2518 NAV_Status
.activeWpAction
= 0;
2519 if ((posControl
.activeWaypointIndex
>= 0) && (posControl
.activeWaypointIndex
< NAV_MAX_WAYPOINTS
)) {
2520 NAV_Status
.activeWpAction
= posControl
.waypointList
[posControl
.activeWaypointIndex
].action
;
2524 static fpVector3_t
* rthGetHomeTargetPosition(rthTargetMode_e mode
)
2526 posControl
.rthState
.homeTmpWaypoint
= posControl
.rthState
.homePosition
.pos
;
2529 case RTH_HOME_ENROUTE_INITIAL
:
2530 posControl
.rthState
.homeTmpWaypoint
.z
= posControl
.rthState
.rthInitialAltitude
;
2533 case RTH_HOME_ENROUTE_PROPORTIONAL
:
2535 float rthTotalDistanceToTravel
= posControl
.rthState
.rthInitialDistance
- (STATE(FIXED_WING_LEGACY
) ? navConfig()->fw
.loiter_radius
: 0);
2536 if (rthTotalDistanceToTravel
>= 100) {
2537 float ratioNotTravelled
= constrainf(posControl
.homeDistance
/ rthTotalDistanceToTravel
, 0.0f
, 1.0f
);
2538 posControl
.rthState
.homeTmpWaypoint
.z
= (posControl
.rthState
.rthInitialAltitude
* ratioNotTravelled
) + (posControl
.rthState
.rthFinalAltitude
* (1.0f
- ratioNotTravelled
));
2541 posControl
.rthState
.homeTmpWaypoint
.z
= posControl
.rthState
.rthFinalAltitude
;
2546 case RTH_HOME_ENROUTE_FINAL
:
2547 posControl
.rthState
.homeTmpWaypoint
.z
= posControl
.rthState
.rthFinalAltitude
;
2550 case RTH_HOME_FINAL_HOVER
:
2551 if (navConfig()->general
.rth_home_altitude
) {
2552 posControl
.rthState
.homeTmpWaypoint
.z
= posControl
.rthState
.homePosition
.pos
.z
+ navConfig()->general
.rth_home_altitude
;
2555 // If home altitude not defined - fall back to final ENROUTE altitude
2556 posControl
.rthState
.homeTmpWaypoint
.z
= posControl
.rthState
.rthFinalAltitude
;
2560 case RTH_HOME_FINAL_LAND
:
2561 // if WP mission p2 > 0 use p2 value as landing elevation (in meters !) (otherwise default to takeoff home elevation)
2562 if (FLIGHT_MODE(NAV_WP_MODE
) && posControl
.waypointList
[posControl
.activeWaypointIndex
].action
== NAV_WP_ACTION_LAND
&& posControl
.waypointList
[posControl
.activeWaypointIndex
].p2
!= 0) {
2563 posControl
.rthState
.homeTmpWaypoint
.z
= posControl
.waypointList
[posControl
.activeWaypointIndex
].p2
* 100; // 100 -> m to cm
2564 if (waypointMissionAltConvMode(posControl
.waypointList
[posControl
.activeWaypointIndex
].p3
) == GEO_ALT_ABSOLUTE
) {
2565 posControl
.rthState
.homeTmpWaypoint
.z
-= posControl
.gpsOrigin
.alt
; // correct to relative if absolute SL altitude datum used
2571 return &posControl
.rthState
.homeTmpWaypoint
;
2574 /*-----------------------------------------------------------
2575 * Detects if thrust vector is facing downwards
2576 *-----------------------------------------------------------*/
2577 bool isThrustFacingDownwards(void)
2579 // Tilt angle <= 80 deg; cos(80) = 0.17364817766693034885171662676931
2580 return (calculateCosTiltAngle() >= 0.173648178f
);
2583 /*-----------------------------------------------------------
2584 * Checks if position sensor (GPS) is failing for a specified timeout (if enabled)
2585 *-----------------------------------------------------------*/
2586 bool checkForPositionSensorTimeout(void)
2588 if (navConfig()->general
.pos_failure_timeout
) {
2589 if ((posControl
.flags
.estPosStatus
== EST_NONE
) && ((millis() - posControl
.lastValidPositionTimeMs
) > (1000 * navConfig()->general
.pos_failure_timeout
))) {
2597 // Timeout not defined, never fail
2602 /*-----------------------------------------------------------
2603 * Processes an update to XY-position and velocity
2604 *-----------------------------------------------------------*/
2605 void updateActualHorizontalPositionAndVelocity(bool estPosValid
, bool estVelValid
, float newX
, float newY
, float newVelX
, float newVelY
)
2607 posControl
.actualState
.abs
.pos
.x
= newX
;
2608 posControl
.actualState
.abs
.pos
.y
= newY
;
2609 posControl
.actualState
.abs
.vel
.x
= newVelX
;
2610 posControl
.actualState
.abs
.vel
.y
= newVelY
;
2612 posControl
.actualState
.agl
.pos
.x
= newX
;
2613 posControl
.actualState
.agl
.pos
.y
= newY
;
2614 posControl
.actualState
.agl
.vel
.x
= newVelX
;
2615 posControl
.actualState
.agl
.vel
.y
= newVelY
;
2617 posControl
.actualState
.velXY
= calc_length_pythagorean_2D(newVelX
, newVelY
);
2619 // CASE 1: POS & VEL valid
2620 if (estPosValid
&& estVelValid
) {
2621 posControl
.flags
.estPosStatus
= EST_TRUSTED
;
2622 posControl
.flags
.estVelStatus
= EST_TRUSTED
;
2623 posControl
.flags
.horizontalPositionDataNew
= true;
2624 posControl
.lastValidPositionTimeMs
= millis();
2626 // CASE 1: POS invalid, VEL valid
2627 else if (!estPosValid
&& estVelValid
) {
2628 posControl
.flags
.estPosStatus
= EST_USABLE
; // Pos usable, but not trusted
2629 posControl
.flags
.estVelStatus
= EST_TRUSTED
;
2630 posControl
.flags
.horizontalPositionDataNew
= true;
2631 posControl
.lastValidPositionTimeMs
= millis();
2633 // CASE 3: can't use pos/vel data
2635 posControl
.flags
.estPosStatus
= EST_NONE
;
2636 posControl
.flags
.estVelStatus
= EST_NONE
;
2637 posControl
.flags
.horizontalPositionDataNew
= false;
2640 //Update blackbox data
2641 navLatestActualPosition
[X
] = newX
;
2642 navLatestActualPosition
[Y
] = newY
;
2643 navActualVelocity
[X
] = constrain(newVelX
, -32678, 32767);
2644 navActualVelocity
[Y
] = constrain(newVelY
, -32678, 32767);
2647 /*-----------------------------------------------------------
2648 * Processes an update to Z-position and velocity
2649 *-----------------------------------------------------------*/
2650 void updateActualAltitudeAndClimbRate(bool estimateValid
, float newAltitude
, float newVelocity
, float surfaceDistance
, float surfaceVelocity
, navigationEstimateStatus_e surfaceStatus
, float gpsCfEstimatedAltitudeError
)
2652 posControl
.actualState
.abs
.pos
.z
= newAltitude
;
2653 posControl
.actualState
.abs
.vel
.z
= newVelocity
;
2655 posControl
.actualState
.agl
.pos
.z
= surfaceDistance
;
2656 posControl
.actualState
.agl
.vel
.z
= surfaceVelocity
;
2658 // Update altitude that would be used when executing RTH
2659 if (estimateValid
) {
2660 updateDesiredRTHAltitude();
2662 // If we acquired new surface reference - changing from NONE/USABLE -> TRUSTED
2663 if ((surfaceStatus
== EST_TRUSTED
) && (posControl
.flags
.estAglStatus
!= EST_TRUSTED
)) {
2664 // If we are in terrain-following modes - signal that we should update the surface tracking setpoint
2665 // NONE/USABLE means that we were flying blind, now we should lock to surface
2666 //updateSurfaceTrackingSetpoint();
2669 posControl
.flags
.estAglStatus
= surfaceStatus
; // Could be TRUSTED or USABLE
2670 posControl
.flags
.estAltStatus
= EST_TRUSTED
;
2671 posControl
.flags
.verticalPositionDataNew
= true;
2672 posControl
.lastValidAltitudeTimeMs
= millis();
2673 /* flag set if mismatch between relative GPS and estimated altitude exceeds 20m */
2674 posControl
.flags
.gpsCfEstimatedAltitudeMismatch
= fabsf(gpsCfEstimatedAltitudeError
) > 2000;
2677 posControl
.flags
.estAltStatus
= EST_NONE
;
2678 posControl
.flags
.estAglStatus
= EST_NONE
;
2679 posControl
.flags
.verticalPositionDataNew
= false;
2680 posControl
.flags
.gpsCfEstimatedAltitudeMismatch
= false;
2683 if (ARMING_FLAG(ARMED
)) {
2684 if ((posControl
.flags
.estAglStatus
== EST_TRUSTED
) && surfaceDistance
> 0) {
2685 if (posControl
.actualState
.surfaceMin
> 0) {
2686 posControl
.actualState
.surfaceMin
= MIN(posControl
.actualState
.surfaceMin
, surfaceDistance
);
2689 posControl
.actualState
.surfaceMin
= surfaceDistance
;
2694 posControl
.actualState
.surfaceMin
= -1;
2697 //Update blackbox data
2698 navLatestActualPosition
[Z
] = navGetCurrentActualPositionAndVelocity()->pos
.z
;
2699 navActualVelocity
[Z
] = constrain(navGetCurrentActualPositionAndVelocity()->vel
.z
, -32678, 32767);
2702 /*-----------------------------------------------------------
2703 * Processes an update to estimated heading
2704 *-----------------------------------------------------------*/
2705 void updateActualHeading(bool headingValid
, int32_t newHeading
, int32_t newGroundCourse
)
2707 /* Update heading. Check if we're acquiring a valid heading for the
2708 * first time and update home heading accordingly.
2711 navigationEstimateStatus_e newEstHeading
= headingValid
? EST_TRUSTED
: EST_NONE
;
2713 #ifdef USE_DEV_TOOLS
2714 if (systemConfig()->groundTestMode
&& STATE(AIRPLANE
)) {
2715 newEstHeading
= EST_TRUSTED
;
2718 if (newEstHeading
>= EST_USABLE
&& posControl
.flags
.estHeadingStatus
< EST_USABLE
&&
2719 (posControl
.rthState
.homeFlags
& (NAV_HOME_VALID_XY
| NAV_HOME_VALID_Z
)) &&
2720 (posControl
.rthState
.homeFlags
& NAV_HOME_VALID_HEADING
) == 0) {
2722 // Home was stored using the fake heading (assuming boot as 0deg). Calculate
2723 // the offset from the fake to the actual yaw and apply the same rotation
2724 // to the home point.
2725 int32_t fakeToRealYawOffset
= newHeading
- posControl
.actualState
.yaw
;
2726 posControl
.rthState
.homePosition
.heading
+= fakeToRealYawOffset
;
2727 posControl
.rthState
.homePosition
.heading
= wrap_36000(posControl
.rthState
.homePosition
.heading
);
2729 posControl
.rthState
.homeFlags
|= NAV_HOME_VALID_HEADING
;
2732 posControl
.actualState
.yaw
= newHeading
;
2733 posControl
.actualState
.cog
= newGroundCourse
;
2734 posControl
.flags
.estHeadingStatus
= newEstHeading
;
2736 /* Precompute sin/cos of yaw angle */
2737 posControl
.actualState
.sinYaw
= sin_approx(CENTIDEGREES_TO_RADIANS(newHeading
));
2738 posControl
.actualState
.cosYaw
= cos_approx(CENTIDEGREES_TO_RADIANS(newHeading
));
2741 /*-----------------------------------------------------------
2742 * Returns pointer to currently used position (ABS or AGL) depending on surface tracking status
2743 *-----------------------------------------------------------*/
2744 const navEstimatedPosVel_t
* navGetCurrentActualPositionAndVelocity(void)
2746 return posControl
.flags
.isTerrainFollowEnabled
? &posControl
.actualState
.agl
: &posControl
.actualState
.abs
;
2749 /*-----------------------------------------------------------
2750 * Calculates distance and bearing to destination point
2751 *-----------------------------------------------------------*/
2752 static uint32_t calculateDistanceFromDelta(float deltaX
, float deltaY
)
2754 return calc_length_pythagorean_2D(deltaX
, deltaY
);
2757 static int32_t calculateBearingFromDelta(float deltaX
, float deltaY
)
2759 return wrap_36000(RADIANS_TO_CENTIDEGREES(atan2_approx(deltaY
, deltaX
)));
2762 uint32_t calculateDistanceToDestination(const fpVector3_t
* destinationPos
)
2764 const navEstimatedPosVel_t
*posvel
= navGetCurrentActualPositionAndVelocity();
2765 const float deltaX
= destinationPos
->x
- posvel
->pos
.x
;
2766 const float deltaY
= destinationPos
->y
- posvel
->pos
.y
;
2768 return calculateDistanceFromDelta(deltaX
, deltaY
);
2771 int32_t calculateBearingToDestination(const fpVector3_t
* destinationPos
)
2773 const navEstimatedPosVel_t
*posvel
= navGetCurrentActualPositionAndVelocity();
2774 const float deltaX
= destinationPos
->x
- posvel
->pos
.x
;
2775 const float deltaY
= destinationPos
->y
- posvel
->pos
.y
;
2777 return calculateBearingFromDelta(deltaX
, deltaY
);
2780 int32_t calculateBearingBetweenLocalPositions(const fpVector3_t
* startPos
, const fpVector3_t
* endPos
)
2782 const float deltaX
= endPos
->x
- startPos
->x
;
2783 const float deltaY
= endPos
->y
- startPos
->y
;
2785 return calculateBearingFromDelta(deltaX
, deltaY
);
2788 bool navCalculatePathToDestination(navDestinationPath_t
*result
, const fpVector3_t
* destinationPos
) // NOT USED ANYWHERE
2790 if (posControl
.flags
.estPosStatus
== EST_NONE
||
2791 posControl
.flags
.estHeadingStatus
== EST_NONE
) {
2796 const navEstimatedPosVel_t
*posvel
= navGetCurrentActualPositionAndVelocity();
2797 const float deltaX
= destinationPos
->x
- posvel
->pos
.x
;
2798 const float deltaY
= destinationPos
->y
- posvel
->pos
.y
;
2800 result
->distance
= calculateDistanceFromDelta(deltaX
, deltaY
);
2801 result
->bearing
= calculateBearingFromDelta(deltaX
, deltaY
);
2805 static bool getLocalPosNextWaypoint(fpVector3_t
* nextWpPos
)
2807 // Only for WP Mode not Trackback. Ignore non geo waypoints except RTH and JUMP.
2808 if (navGetStateFlags(posControl
.navState
) & NAV_AUTO_WP
&& !isLastMissionWaypoint()) {
2809 navWaypointActions_e nextWpAction
= posControl
.waypointList
[posControl
.activeWaypointIndex
+ 1].action
;
2811 if (!(nextWpAction
== NAV_WP_ACTION_SET_POI
|| nextWpAction
== NAV_WP_ACTION_SET_HEAD
)) {
2812 uint8_t nextWpIndex
= posControl
.activeWaypointIndex
+ 1;
2813 if (nextWpAction
== NAV_WP_ACTION_JUMP
) {
2814 if (posControl
.waypointList
[posControl
.activeWaypointIndex
+ 1].p3
!= 0 ||
2815 posControl
.waypointList
[posControl
.activeWaypointIndex
+ 1].p2
== -1) {
2816 nextWpIndex
= posControl
.waypointList
[posControl
.activeWaypointIndex
+ 1].p1
+ posControl
.startWpIndex
;
2817 } else if (posControl
.activeWaypointIndex
+ 2 <= posControl
.startWpIndex
+ posControl
.waypointCount
- 1) {
2818 if (posControl
.waypointList
[posControl
.activeWaypointIndex
+ 2].action
!= NAV_WP_ACTION_JUMP
) {
2821 return false; // give up - too complicated
2825 mapWaypointToLocalPosition(nextWpPos
, &posControl
.waypointList
[nextWpIndex
], 0);
2830 return false; // no position available
2833 /*-----------------------------------------------------------
2834 * Check if waypoint is/was reached.
2835 * waypointBearing stores initial bearing to waypoint
2836 *-----------------------------------------------------------*/
2837 static bool isWaypointReached(const fpVector3_t
* waypointPos
, const int32_t * waypointBearing
)
2839 posControl
.wpDistance
= calculateDistanceToDestination(waypointPos
);
2841 // Airplane will do a circular loiter at hold waypoints and might never approach them closer than waypoint_radius
2842 // Check within 10% margin of circular loiter radius
2843 if (STATE(AIRPLANE
) && isNavHoldPositionActive() && posControl
.wpDistance
<= (navConfig()->fw
.loiter_radius
* 1.10f
)) {
2847 if (navGetStateFlags(posControl
.navState
) & NAV_AUTO_WP
|| posControl
.flags
.rthTrackbackActive
) {
2848 // If WP turn smoothing CUT option used WP is reached when start of turn is initiated
2849 if (navConfig()->fw
.wp_turn_smoothing
== WP_TURN_SMOOTHING_CUT
&& posControl
.flags
.wpTurnSmoothingActive
) {
2850 posControl
.flags
.wpTurnSmoothingActive
= false;
2853 // Check if waypoint was missed based on bearing to WP exceeding 100 degrees relative to waypoint Yaw
2854 // Same method for turn smoothing option but relative bearing set at 60 degrees
2855 uint16_t relativeBearing
= posControl
.flags
.wpTurnSmoothingActive
? 6000 : 10000;
2856 if (ABS(wrap_18000(calculateBearingToDestination(waypointPos
) - *waypointBearing
)) > relativeBearing
) {
2861 return posControl
.wpDistance
<= (navConfig()->general
.waypoint_radius
);
2864 bool isWaypointAltitudeReached(void)
2866 return ABS(navGetCurrentActualPositionAndVelocity()->pos
.z
- posControl
.activeWaypoint
.pos
.z
) < navConfig()->general
.waypoint_enforce_altitude
;
2869 static void updateHomePositionCompatibility(void)
2871 geoConvertLocalToGeodetic(&GPS_home
, &posControl
.gpsOrigin
, &posControl
.rthState
.homePosition
.pos
);
2872 GPS_distanceToHome
= posControl
.homeDistance
* 0.01f
;
2873 GPS_directionToHome
= posControl
.homeDirection
* 0.01f
;
2876 // Backdoor for RTH estimator
2877 float getFinalRTHAltitude(void)
2879 return posControl
.rthState
.rthFinalAltitude
;
2882 /*-----------------------------------------------------------
2883 * Update the RTH Altitudes
2884 *-----------------------------------------------------------*/
2885 static void updateDesiredRTHAltitude(void)
2887 if (ARMING_FLAG(ARMED
)) {
2888 if (!((navGetStateFlags(posControl
.navState
) & NAV_AUTO_RTH
)
2889 || ((navGetStateFlags(posControl
.navState
) & NAV_AUTO_WP
) && posControl
.waypointList
[posControl
.activeWaypointIndex
].action
== NAV_WP_ACTION_RTH
))) {
2890 switch (navConfig()->general
.flags
.rth_climb_first_stage_mode
) {
2891 case NAV_RTH_CLIMB_STAGE_AT_LEAST
:
2892 posControl
.rthState
.rthClimbStageAltitude
= posControl
.rthState
.homePosition
.pos
.z
+ navConfig()->general
.rth_climb_first_stage_altitude
;
2894 case NAV_RTH_CLIMB_STAGE_EXTRA
:
2895 posControl
.rthState
.rthClimbStageAltitude
= posControl
.actualState
.abs
.pos
.z
+ navConfig()->general
.rth_climb_first_stage_altitude
;
2899 switch (navConfig()->general
.flags
.rth_alt_control_mode
) {
2900 case NAV_RTH_NO_ALT
:
2901 posControl
.rthState
.rthInitialAltitude
= posControl
.actualState
.abs
.pos
.z
;
2902 posControl
.rthState
.rthFinalAltitude
= posControl
.rthState
.rthInitialAltitude
;
2905 case NAV_RTH_EXTRA_ALT
: // Maintain current altitude + predefined safety margin
2906 posControl
.rthState
.rthInitialAltitude
= posControl
.actualState
.abs
.pos
.z
+ navConfig()->general
.rth_altitude
;
2907 posControl
.rthState
.rthFinalAltitude
= posControl
.rthState
.rthInitialAltitude
;
2910 case NAV_RTH_MAX_ALT
:
2911 posControl
.rthState
.rthInitialAltitude
= MAX(posControl
.rthState
.rthInitialAltitude
, posControl
.actualState
.abs
.pos
.z
);
2912 if (navConfig()->general
.rth_altitude
> 0) {
2913 posControl
.rthState
.rthInitialAltitude
= MAX(posControl
.rthState
.rthInitialAltitude
, posControl
.rthState
.homePosition
.pos
.z
+ navConfig()->general
.rth_altitude
);
2915 posControl
.rthState
.rthFinalAltitude
= posControl
.rthState
.rthInitialAltitude
;
2918 case NAV_RTH_AT_LEAST_ALT
: // Climb to at least some predefined altitude above home
2919 posControl
.rthState
.rthInitialAltitude
= MAX(posControl
.rthState
.homePosition
.pos
.z
+ navConfig()->general
.rth_altitude
, posControl
.actualState
.abs
.pos
.z
);
2920 posControl
.rthState
.rthFinalAltitude
= posControl
.rthState
.rthInitialAltitude
;
2923 case NAV_RTH_CONST_ALT
: // Climb/descend to predefined altitude above home
2925 posControl
.rthState
.rthInitialAltitude
= posControl
.rthState
.homePosition
.pos
.z
+ navConfig()->general
.rth_altitude
;
2926 posControl
.rthState
.rthFinalAltitude
= posControl
.rthState
.rthInitialAltitude
;
2929 if ((navConfig()->general
.flags
.rth_use_linear_descent
) && (navConfig()->general
.rth_home_altitude
> 0) && (navConfig()->general
.rth_linear_descent_start_distance
== 0) ) {
2930 posControl
.rthState
.rthFinalAltitude
= posControl
.rthState
.homePosition
.pos
.z
+ navConfig()->general
.rth_home_altitude
;
2934 posControl
.rthState
.rthClimbStageAltitude
= posControl
.actualState
.abs
.pos
.z
;
2935 posControl
.rthState
.rthInitialAltitude
= posControl
.actualState
.abs
.pos
.z
;
2936 posControl
.rthState
.rthFinalAltitude
= posControl
.actualState
.abs
.pos
.z
;
2940 /*-----------------------------------------------------------
2941 * RTH sanity test logic
2942 *-----------------------------------------------------------*/
2943 void initializeRTHSanityChecker(void)
2945 const timeMs_t currentTimeMs
= millis();
2947 posControl
.rthSanityChecker
.lastCheckTime
= currentTimeMs
;
2948 posControl
.rthSanityChecker
.rthSanityOK
= true;
2949 posControl
.rthSanityChecker
.minimalDistanceToHome
= calculateDistanceToDestination(&posControl
.rthState
.homePosition
.pos
);
2952 bool validateRTHSanityChecker(void)
2954 const timeMs_t currentTimeMs
= millis();
2956 // Ability to disable sanity checker
2957 if (navConfig()->general
.rth_abort_threshold
== 0) {
2961 // Check at 10Hz rate
2962 if ((currentTimeMs
- posControl
.rthSanityChecker
.lastCheckTime
) > 100) {
2963 const float currentDistanceToHome
= calculateDistanceToDestination(&posControl
.rthState
.homePosition
.pos
);
2964 posControl
.rthSanityChecker
.lastCheckTime
= currentTimeMs
;
2966 if (currentDistanceToHome
< posControl
.rthSanityChecker
.minimalDistanceToHome
) {
2967 posControl
.rthSanityChecker
.minimalDistanceToHome
= currentDistanceToHome
;
2969 // If while doing RTH we got even farther away from home - RTH is doing something crazy
2970 posControl
.rthSanityChecker
.rthSanityOK
= (currentDistanceToHome
- posControl
.rthSanityChecker
.minimalDistanceToHome
) < navConfig()->general
.rth_abort_threshold
;
2974 return posControl
.rthSanityChecker
.rthSanityOK
;
2977 /*-----------------------------------------------------------
2978 * Reset home position to current position
2979 *-----------------------------------------------------------*/
2980 void setHomePosition(const fpVector3_t
* pos
, int32_t heading
, navSetWaypointFlags_t useMask
, navigationHomeFlags_t homeFlags
)
2983 if ((useMask
& NAV_POS_UPDATE_XY
) != 0) {
2984 posControl
.rthState
.homePosition
.pos
.x
= pos
->x
;
2985 posControl
.rthState
.homePosition
.pos
.y
= pos
->y
;
2986 if (homeFlags
& NAV_HOME_VALID_XY
) {
2987 posControl
.rthState
.homeFlags
|= NAV_HOME_VALID_XY
;
2989 posControl
.rthState
.homeFlags
&= ~NAV_HOME_VALID_XY
;
2994 if ((useMask
& NAV_POS_UPDATE_Z
) != 0) {
2995 posControl
.rthState
.homePosition
.pos
.z
= pos
->z
;
2996 if (homeFlags
& NAV_HOME_VALID_Z
) {
2997 posControl
.rthState
.homeFlags
|= NAV_HOME_VALID_Z
;
2999 posControl
.rthState
.homeFlags
&= ~NAV_HOME_VALID_Z
;
3004 if ((useMask
& NAV_POS_UPDATE_HEADING
) != 0) {
3006 posControl
.rthState
.homePosition
.heading
= heading
;
3007 if (homeFlags
& NAV_HOME_VALID_HEADING
) {
3008 posControl
.rthState
.homeFlags
|= NAV_HOME_VALID_HEADING
;
3010 posControl
.rthState
.homeFlags
&= ~NAV_HOME_VALID_HEADING
;
3014 posControl
.homeDistance
= 0;
3015 posControl
.homeDirection
= 0;
3017 // Update target RTH altitude as a waypoint above home
3018 updateDesiredRTHAltitude();
3020 // Reset RTH sanity checker for new home position if RTH active
3021 if (FLIGHT_MODE(NAV_RTH_MODE
)) {
3022 initializeRTHSanityChecker();
3025 updateHomePositionCompatibility();
3026 ENABLE_STATE(GPS_FIX_HOME
);
3029 static navigationHomeFlags_t
navigationActualStateHomeValidity(void)
3031 navigationHomeFlags_t flags
= 0;
3033 if (posControl
.flags
.estPosStatus
>= EST_USABLE
) {
3034 flags
|= NAV_HOME_VALID_XY
| NAV_HOME_VALID_Z
;
3037 if (posControl
.flags
.estHeadingStatus
>= EST_USABLE
) {
3038 flags
|= NAV_HOME_VALID_HEADING
;
3044 #if defined(USE_SAFE_HOME)
3045 void checkSafeHomeState(bool shouldBeEnabled
)
3047 bool safehomeNotApplicable
= navConfig()->general
.flags
.safehome_usage_mode
== SAFEHOME_USAGE_OFF
|| posControl
.flags
.rthTrackbackActive
||
3048 (!posControl
.safehomeState
.isApplied
&& posControl
.homeDistance
< navConfig()->general
.min_rth_distance
);
3049 #ifdef USE_MULTI_FUNCTIONS
3050 safehomeNotApplicable
= safehomeNotApplicable
|| (MULTI_FUNC_FLAG(MF_SUSPEND_SAFEHOMES
) && !posControl
.flags
.forcedRTHActivated
);
3053 if (safehomeNotApplicable
) {
3054 shouldBeEnabled
= false;
3055 } else if (navConfig()->general
.flags
.safehome_usage_mode
== SAFEHOME_USAGE_RTH_FS
&& shouldBeEnabled
) {
3056 // if safehomes are only used with failsafe and we're trying to enable safehome
3057 // then enable the safehome only with failsafe
3058 shouldBeEnabled
= posControl
.flags
.forcedRTHActivated
;
3060 // no safe homes found when arming or safehome feature in the correct state, then we don't need to do anything
3061 if (posControl
.safehomeState
.distance
== 0 || posControl
.safehomeState
.isApplied
== shouldBeEnabled
) {
3064 if (shouldBeEnabled
) {
3065 // set home to safehome
3066 setHomePosition(&posControl
.safehomeState
.nearestSafeHome
, 0, NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_Z
| NAV_POS_UPDATE_HEADING
, navigationActualStateHomeValidity());
3067 posControl
.safehomeState
.isApplied
= true;
3069 // set home to original arming point
3070 setHomePosition(&posControl
.rthState
.originalHomePosition
, 0, NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_Z
| NAV_POS_UPDATE_HEADING
, navigationActualStateHomeValidity());
3071 posControl
.safehomeState
.isApplied
= false;
3073 // if we've changed the home position, update the distance and direction
3074 updateHomePosition();
3077 /***********************************************************
3078 * See if there are any safehomes near where we are arming.
3079 * If so, save the nearest one in case we need it later for RTH.
3080 **********************************************************/
3081 bool findNearestSafeHome(void)
3083 posControl
.safehomeState
.index
= -1;
3084 uint32_t nearest_safehome_distance
= navConfig()->general
.safehome_max_distance
+ 1;
3085 uint32_t distance_to_current
;
3086 fpVector3_t currentSafeHome
;
3087 gpsLocation_t shLLH
;
3089 for (uint8_t i
= 0; i
< MAX_SAFE_HOMES
; i
++) {
3090 if (!safeHomeConfig(i
)->enabled
)
3093 shLLH
.lat
= safeHomeConfig(i
)->lat
;
3094 shLLH
.lon
= safeHomeConfig(i
)->lon
;
3095 geoConvertGeodeticToLocal(¤tSafeHome
, &posControl
.gpsOrigin
, &shLLH
, GEO_ALT_RELATIVE
);
3096 distance_to_current
= calculateDistanceToDestination(¤tSafeHome
);
3097 if (distance_to_current
< nearest_safehome_distance
) {
3098 // this safehome is the nearest so far - keep track of it.
3099 posControl
.safehomeState
.index
= i
;
3100 nearest_safehome_distance
= distance_to_current
;
3101 posControl
.safehomeState
.nearestSafeHome
= currentSafeHome
;
3104 if (posControl
.safehomeState
.index
>= 0) {
3105 posControl
.safehomeState
.distance
= nearest_safehome_distance
;
3107 posControl
.safehomeState
.distance
= 0;
3109 return posControl
.safehomeState
.distance
> 0;
3113 /*-----------------------------------------------------------
3114 * Update home position, calculate distance and bearing to home
3115 *-----------------------------------------------------------*/
3116 void updateHomePosition(void)
3118 // Disarmed and have a valid position, constantly update home before first arm (depending on setting)
3119 // Update immediately after arming thereafter if reset on each arm (required to avoid home reset after emerg in flight rearm)
3120 static bool setHome
= false;
3121 navSetWaypointFlags_t homeUpdateFlags
= NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_Z
| NAV_POS_UPDATE_HEADING
;
3123 if (!ARMING_FLAG(ARMED
)) {
3124 if (posControl
.flags
.estPosStatus
>= EST_USABLE
) {
3125 const navigationHomeFlags_t validHomeFlags
= NAV_HOME_VALID_XY
| NAV_HOME_VALID_Z
;
3126 setHome
= (posControl
.rthState
.homeFlags
& validHomeFlags
) != validHomeFlags
;
3127 switch ((nav_reset_type_e
)positionEstimationConfig()->reset_home_type
) {
3128 case NAV_RESET_NEVER
:
3130 case NAV_RESET_ON_FIRST_ARM
:
3131 setHome
|= !ARMING_FLAG(WAS_EVER_ARMED
);
3133 case NAV_RESET_ON_EACH_ARM
:
3140 static bool isHomeResetAllowed
= false;
3141 // If pilot so desires he may reset home position to current position
3142 if (IS_RC_MODE_ACTIVE(BOXHOMERESET
)) {
3143 if (isHomeResetAllowed
&& !FLIGHT_MODE(FAILSAFE_MODE
) && !FLIGHT_MODE(NAV_RTH_MODE
) && !FLIGHT_MODE(NAV_WP_MODE
) && (posControl
.flags
.estPosStatus
>= EST_USABLE
)) {
3144 homeUpdateFlags
= 0;
3145 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
);
3147 isHomeResetAllowed
= false;
3151 isHomeResetAllowed
= true;
3154 // Update distance and direction to home if armed (home is not updated when armed)
3155 if (STATE(GPS_FIX_HOME
)) {
3156 fpVector3_t
* tmpHomePos
= rthGetHomeTargetPosition(RTH_HOME_FINAL_LAND
);
3157 posControl
.homeDistance
= calculateDistanceToDestination(tmpHomePos
);
3158 posControl
.homeDirection
= calculateBearingToDestination(tmpHomePos
);
3159 updateHomePositionCompatibility();
3162 setHome
&= !STATE(IN_FLIGHT_EMERG_REARM
); // prevent reset following emerg in flight rearm
3165 if (setHome
&& (!ARMING_FLAG(WAS_EVER_ARMED
) || ARMING_FLAG(ARMED
))) {
3166 #if defined(USE_SAFE_HOME)
3167 findNearestSafeHome();
3169 setHomePosition(&posControl
.actualState
.abs
.pos
, posControl
.actualState
.yaw
, homeUpdateFlags
, navigationActualStateHomeValidity());
3171 if (ARMING_FLAG(ARMED
) && positionEstimationConfig()->reset_altitude_type
== NAV_RESET_ON_EACH_ARM
) {
3172 posControl
.rthState
.homePosition
.pos
.z
= 0; // force to 0 if reference altitude also reset every arm
3174 // save the current location in case it is replaced by a safehome or HOME_RESET
3175 posControl
.rthState
.originalHomePosition
= posControl
.rthState
.homePosition
.pos
;
3180 /* -----------------------------------------------------------
3181 * Override RTH preset altitude and Climb First option
3182 * using Pitch/Roll stick held for > 1 seconds
3183 * Climb First override limited to Fixed Wing only
3184 * Roll also cancels RTH trackback on Fixed Wing and Multirotor
3185 *-----------------------------------------------------------*/
3186 static bool rthAltControlStickOverrideCheck(unsigned axis
)
3188 if (!navConfig()->general
.flags
.rth_alt_control_override
|| posControl
.flags
.forcedRTHActivated
||
3189 (axis
== ROLL
&& STATE(MULTIROTOR
) && !posControl
.flags
.rthTrackbackActive
)) {
3192 static timeMs_t rthOverrideStickHoldStartTime
[2];
3194 if (rxGetChannelValue(axis
) > rxConfig()->maxcheck
) {
3195 timeDelta_t holdTime
= millis() - rthOverrideStickHoldStartTime
[axis
];
3197 if (!rthOverrideStickHoldStartTime
[axis
]) {
3198 rthOverrideStickHoldStartTime
[axis
] = millis();
3199 } else if (ABS(1500 - holdTime
) < 500) { // 1s delay to activate, activation duration limited to 1 sec
3200 if (axis
== PITCH
) { // PITCH down to override preset altitude reset to current altitude
3201 posControl
.rthState
.rthInitialAltitude
= posControl
.actualState
.abs
.pos
.z
;
3202 posControl
.rthState
.rthFinalAltitude
= posControl
.rthState
.rthInitialAltitude
;
3204 } else if (axis
== ROLL
) { // ROLL right to override climb first
3209 rthOverrideStickHoldStartTime
[axis
] = 0;
3215 /* ---------------------------------------------------
3216 * If climb stage is being used, see if it is time to
3217 * transiton in to turn.
3218 * Limited to fixed wing only.
3219 * --------------------------------------------------- */
3220 bool rthClimbStageActiveAndComplete(void) {
3221 if ((STATE(FIXED_WING_LEGACY
) || STATE(AIRPLANE
)) && (navConfig()->general
.rth_climb_first_stage_altitude
> 0)) {
3222 if (posControl
.actualState
.abs
.pos
.z
>= posControl
.rthState
.rthClimbStageAltitude
) {
3230 /* --------------------------------------------------------------------------------
3231 * == RTH Trackback ==
3232 * Saves track during flight which is used during RTH to back track
3233 * along arrival route rather than immediately heading directly toward home.
3234 * Max desired trackback distance set by user or limited by number of available points.
3235 * Reverts to normal RTH heading direct to home when end of track reached.
3236 * Trackpoints logged with precedence for course/altitude changes. Distance based changes
3237 * only logged if no course/altitude changes logged over an extended distance.
3238 * Tracking suspended during fixed wing loiter (PosHold and WP Mode timed hold).
3239 * --------------------------------------------------------------------------------- */
3240 static void updateRthTrackback(bool forceSaveTrackPoint
)
3242 static bool suspendTracking
= false;
3243 bool fwLoiterIsActive
= STATE(AIRPLANE
) && (NAV_Status
.state
== MW_NAV_STATE_HOLD_TIMED
|| FLIGHT_MODE(NAV_POSHOLD_MODE
));
3244 if (!fwLoiterIsActive
&& suspendTracking
) {
3245 suspendTracking
= false;
3248 if (navConfig()->general
.flags
.rth_trackback_mode
== RTH_TRACKBACK_OFF
|| FLIGHT_MODE(NAV_RTH_MODE
) || !ARMING_FLAG(ARMED
) || suspendTracking
) {
3252 // Record trackback points based on significant change in course/altitude until
3253 // points limit reached. Overwrite older points from then on.
3254 if (posControl
.flags
.estPosStatus
>= EST_USABLE
&& posControl
.flags
.estAltStatus
>= EST_USABLE
) {
3255 static int32_t previousTBTripDist
; // cm
3256 static int16_t previousTBCourse
; // degrees
3257 static int16_t previousTBAltitude
; // meters
3258 static uint8_t distanceCounter
= 0;
3259 bool saveTrackpoint
= forceSaveTrackPoint
;
3260 bool GPSCourseIsValid
= isGPSHeadingValid();
3262 // start recording when some distance from home, 50m seems reasonable.
3263 if (posControl
.activeRthTBPointIndex
< 0) {
3264 saveTrackpoint
= posControl
.homeDistance
> METERS_TO_CENTIMETERS(50);
3266 previousTBCourse
= CENTIDEGREES_TO_DEGREES(posControl
.actualState
.cog
);
3267 previousTBTripDist
= posControl
.totalTripDistance
;
3269 // Minimum distance increment between course change track points when GPS course valid - set to 10m
3270 const bool distanceIncrement
= posControl
.totalTripDistance
- previousTBTripDist
> METERS_TO_CENTIMETERS(10);
3273 if (ABS(previousTBAltitude
- CENTIMETERS_TO_METERS(posControl
.actualState
.abs
.pos
.z
)) > 10) { // meters
3274 saveTrackpoint
= true;
3275 } else if (distanceIncrement
&& GPSCourseIsValid
) {
3276 // Course change - set to 45 degrees
3277 if (ABS(wrap_18000(DEGREES_TO_CENTIDEGREES(DECIDEGREES_TO_DEGREES(gpsSol
.groundCourse
) - previousTBCourse
))) > DEGREES_TO_CENTIDEGREES(45)) {
3278 saveTrackpoint
= true;
3279 } else if (distanceCounter
>= 9) {
3280 // Distance based trackpoint logged if at least 10 distance increments occur without altitude or course change
3281 // and deviation from projected course path > 20m
3282 float distToPrevPoint
= calculateDistanceToDestination(&posControl
.rthTBPointsList
[posControl
.activeRthTBPointIndex
]);
3284 fpVector3_t virtualCoursePoint
;
3285 virtualCoursePoint
.x
= posControl
.rthTBPointsList
[posControl
.activeRthTBPointIndex
].x
+ distToPrevPoint
* cos_approx(DEGREES_TO_RADIANS(previousTBCourse
));
3286 virtualCoursePoint
.y
= posControl
.rthTBPointsList
[posControl
.activeRthTBPointIndex
].y
+ distToPrevPoint
* sin_approx(DEGREES_TO_RADIANS(previousTBCourse
));
3288 saveTrackpoint
= calculateDistanceToDestination(&virtualCoursePoint
) > METERS_TO_CENTIMETERS(20);
3291 previousTBTripDist
= posControl
.totalTripDistance
;
3292 } else if (!GPSCourseIsValid
) {
3293 // if no reliable course revert to basic distance logging based on direct distance from last point - set to 20m
3294 saveTrackpoint
= calculateDistanceToDestination(&posControl
.rthTBPointsList
[posControl
.activeRthTBPointIndex
]) > METERS_TO_CENTIMETERS(20);
3295 previousTBTripDist
= posControl
.totalTripDistance
;
3298 // Suspend tracking during loiter on fixed wing. Save trackpoint at start of loiter.
3299 if (distanceCounter
&& fwLoiterIsActive
) {
3300 saveTrackpoint
= suspendTracking
= true;
3304 // when trackpoint store full, overwrite from start of store using 'rthTBWrapAroundCounter' to track overwrite position
3305 if (saveTrackpoint
) {
3306 if (posControl
.activeRthTBPointIndex
== (NAV_RTH_TRACKBACK_POINTS
- 1)) { // wraparound to start
3307 posControl
.rthTBWrapAroundCounter
= posControl
.activeRthTBPointIndex
= 0;
3309 posControl
.activeRthTBPointIndex
++;
3310 if (posControl
.rthTBWrapAroundCounter
> -1) { // track wraparound overwrite position after store first filled
3311 posControl
.rthTBWrapAroundCounter
= posControl
.activeRthTBPointIndex
;
3314 posControl
.rthTBPointsList
[posControl
.activeRthTBPointIndex
] = posControl
.actualState
.abs
.pos
;
3316 posControl
.rthTBLastSavedIndex
= posControl
.activeRthTBPointIndex
;
3317 previousTBAltitude
= CENTIMETERS_TO_METERS(posControl
.actualState
.abs
.pos
.z
);
3318 previousTBCourse
= GPSCourseIsValid
? DECIDEGREES_TO_DEGREES(gpsSol
.groundCourse
) : previousTBCourse
;
3319 distanceCounter
= 0;
3324 static fpVector3_t
* rthGetTrackbackPos(void)
3326 // ensure trackback altitude never lower than altitude of start point
3327 if (posControl
.rthTBPointsList
[posControl
.activeRthTBPointIndex
].z
< posControl
.rthTBPointsList
[posControl
.rthTBLastSavedIndex
].z
) {
3328 posControl
.rthTBPointsList
[posControl
.activeRthTBPointIndex
].z
= posControl
.rthTBPointsList
[posControl
.rthTBLastSavedIndex
].z
;
3331 return &posControl
.rthTBPointsList
[posControl
.activeRthTBPointIndex
];
3334 /*-----------------------------------------------------------
3335 * Update flight statistics
3336 *-----------------------------------------------------------*/
3337 static void updateNavigationFlightStatistics(void)
3339 static timeMs_t previousTimeMs
= 0;
3340 const timeMs_t currentTimeMs
= millis();
3341 const timeDelta_t timeDeltaMs
= currentTimeMs
- previousTimeMs
;
3342 previousTimeMs
= currentTimeMs
;
3344 if (ARMING_FLAG(ARMED
)) {
3345 posControl
.totalTripDistance
+= posControl
.actualState
.velXY
* MS2S(timeDeltaMs
);
3350 * Total travel distance in cm
3352 uint32_t getTotalTravelDistance(void)
3354 return lrintf(posControl
.totalTripDistance
);
3357 /*-----------------------------------------------------------
3358 * Calculate platform-specific hold position (account for deceleration)
3359 *-----------------------------------------------------------*/
3360 void calculateInitialHoldPosition(fpVector3_t
* pos
)
3362 if (STATE(FIXED_WING_LEGACY
)) { // FIXED_WING_LEGACY
3363 calculateFixedWingInitialHoldPosition(pos
);
3366 calculateMulticopterInitialHoldPosition(pos
);
3370 /*-----------------------------------------------------------
3371 * Set active XYZ-target and desired heading
3372 *-----------------------------------------------------------*/
3373 void setDesiredPosition(const fpVector3_t
* pos
, int32_t yaw
, navSetWaypointFlags_t useMask
)
3375 // XY-position update is allowed only when not braking in NAV_CRUISE_BRAKING
3376 if ((useMask
& NAV_POS_UPDATE_XY
) != 0 && !STATE(NAV_CRUISE_BRAKING
)) {
3377 posControl
.desiredState
.pos
.x
= pos
->x
;
3378 posControl
.desiredState
.pos
.y
= pos
->y
;
3382 if ((useMask
& NAV_POS_UPDATE_Z
) != 0) {
3383 updateClimbRateToAltitudeController(0, 0, ROC_TO_ALT_RESET
); // Reset RoC/RoD -> altitude controller
3384 posControl
.desiredState
.pos
.z
= pos
->z
;
3388 if ((useMask
& NAV_POS_UPDATE_HEADING
) != 0) {
3390 posControl
.desiredState
.yaw
= yaw
;
3392 else if ((useMask
& NAV_POS_UPDATE_BEARING
) != 0) {
3393 posControl
.desiredState
.yaw
= calculateBearingToDestination(pos
);
3395 else if ((useMask
& NAV_POS_UPDATE_BEARING_TAIL_FIRST
) != 0) {
3396 posControl
.desiredState
.yaw
= wrap_36000(calculateBearingToDestination(pos
) - 18000);
3400 void calculateFarAwayPos(fpVector3_t
*farAwayPos
, const fpVector3_t
*start
, int32_t bearing
, int32_t distance
)
3402 farAwayPos
->x
= start
->x
+ distance
* cos_approx(CENTIDEGREES_TO_RADIANS(bearing
));
3403 farAwayPos
->y
= start
->y
+ distance
* sin_approx(CENTIDEGREES_TO_RADIANS(bearing
));
3404 farAwayPos
->z
= start
->z
;
3407 void calculateFarAwayTarget(fpVector3_t
* farAwayPos
, int32_t bearing
, int32_t distance
)
3409 calculateFarAwayPos(farAwayPos
, &navGetCurrentActualPositionAndVelocity()->pos
, bearing
, distance
);
3412 /*-----------------------------------------------------------
3414 *-----------------------------------------------------------*/
3415 void updateLandingStatus(timeMs_t currentTimeMs
)
3417 if (STATE(AIRPLANE
) && !navConfig()->general
.flags
.disarm_on_landing
) {
3418 return; // no point using this with a fixed wing if not set to disarm
3421 static timeMs_t lastUpdateTimeMs
= 0;
3422 if ((currentTimeMs
- lastUpdateTimeMs
) <= HZ2MS(100)) { // limit update to 100Hz
3425 lastUpdateTimeMs
= currentTimeMs
;
3427 DEBUG_SET(DEBUG_LANDING
, 0, landingDetectorIsActive
);
3428 DEBUG_SET(DEBUG_LANDING
, 1, STATE(LANDING_DETECTED
));
3430 if (!ARMING_FLAG(ARMED
)) {
3431 if (STATE(LANDING_DETECTED
)) {
3432 landingDetectorIsActive
= false;
3434 resetLandingDetector();
3436 if (!IS_RC_MODE_ACTIVE(BOXARM
)) {
3437 DISABLE_ARMING_FLAG(ARMING_DISABLED_LANDING_DETECTED
);
3442 if (!landingDetectorIsActive
) {
3443 if (isFlightDetected()) {
3444 landingDetectorIsActive
= true;
3445 resetLandingDetector();
3447 } else if (STATE(LANDING_DETECTED
)) {
3448 pidResetErrorAccumulators();
3449 if (navConfig()->general
.flags
.disarm_on_landing
&& !FLIGHT_MODE(FAILSAFE_MODE
)) {
3450 ENABLE_ARMING_FLAG(ARMING_DISABLED_LANDING_DETECTED
);
3451 disarm(DISARM_LANDING
);
3452 } else if (!navigationInAutomaticThrottleMode()) {
3453 // for multirotor only - reactivate landing detector without disarm when throttle raised toward hover throttle
3454 landingDetectorIsActive
= rxGetChannelValue(THROTTLE
) < (0.5 * (currentBatteryProfile
->nav
.mc
.hover_throttle
+ getThrottleIdleValue()));
3456 } else if (isLandingDetected()) {
3457 ENABLE_STATE(LANDING_DETECTED
);
3461 bool isLandingDetected(void)
3463 return STATE(AIRPLANE
) ? isFixedWingLandingDetected() : isMulticopterLandingDetected();
3466 void resetLandingDetector(void)
3468 DISABLE_STATE(LANDING_DETECTED
);
3469 posControl
.flags
.resetLandingDetector
= true;
3472 void resetLandingDetectorActiveState(void)
3474 landingDetectorIsActive
= false;
3477 bool isFlightDetected(void)
3479 return STATE(AIRPLANE
) ? isFixedWingFlying() : isMulticopterFlying();
3482 bool isProbablyStillFlying(void)
3484 bool inFlightSanityCheck
;
3485 if (STATE(MULTIROTOR
)) {
3486 inFlightSanityCheck
= posControl
.actualState
.velXY
> MC_LAND_CHECK_VEL_XY_MOVING
|| averageAbsGyroRates() > 4.0f
;
3488 inFlightSanityCheck
= isGPSHeadingValid();
3491 return landingDetectorIsActive
&& inFlightSanityCheck
;
3494 /*-----------------------------------------------------------
3495 * Z-position controller
3496 *-----------------------------------------------------------*/
3497 void updateClimbRateToAltitudeController(float desiredClimbRate
, float targetAltitude
, climbRateToAltitudeControllerMode_e mode
)
3499 #define MIN_TARGET_CLIMB_RATE 100.0f // cm/s
3501 static timeUs_t lastUpdateTimeUs
;
3502 timeUs_t currentTimeUs
= micros();
3504 // Terrain following uses different altitude measurement
3505 const float altitudeToUse
= navGetCurrentActualPositionAndVelocity()->pos
.z
;
3507 if (mode
!= ROC_TO_ALT_RESET
&& desiredClimbRate
) {
3508 /* ROC_TO_ALT_CONSTANT - constant climb rate
3509 * ROC_TO_ALT_TARGET - constant climb rate until close to target altitude reducing to min rate when altitude reached
3510 * Rate reduction starts at distance from target altitude of 5 x climb rate */
3512 if (mode
== ROC_TO_ALT_TARGET
&& fabsf(desiredClimbRate
) > MIN_TARGET_CLIMB_RATE
) {
3513 const int8_t direction
= desiredClimbRate
> 0 ? 1 : -1;
3514 const float absClimbRate
= fabsf(desiredClimbRate
);
3515 const uint16_t maxRateCutoffAlt
= absClimbRate
* 5;
3516 const float verticalVelScaled
= scaleRangef(navGetCurrentActualPositionAndVelocity()->pos
.z
- targetAltitude
,
3517 0.0f
, -maxRateCutoffAlt
* direction
, MIN_TARGET_CLIMB_RATE
, absClimbRate
);
3519 desiredClimbRate
= direction
* constrainf(verticalVelScaled
, MIN_TARGET_CLIMB_RATE
, absClimbRate
);
3523 * If max altitude is set, reset climb rate if altitude is reached and climb rate is > 0
3524 * In other words, when altitude is reached, allow it only to shrink
3526 if (navConfig()->general
.max_altitude
> 0 && altitudeToUse
>= navConfig()->general
.max_altitude
&& desiredClimbRate
> 0) {
3527 desiredClimbRate
= 0;
3530 if (STATE(FIXED_WING_LEGACY
)) {
3531 // Fixed wing climb rate controller is open-loop. We simply move the known altitude target
3532 float timeDelta
= US2S(currentTimeUs
- lastUpdateTimeUs
);
3533 static bool targetHoldActive
= false;
3535 if (timeDelta
<= HZ2S(MIN_POSITION_UPDATE_RATE_HZ
) && desiredClimbRate
) {
3536 // Update target altitude only if actual altitude moving in same direction and lagging by < 5 m, otherwise hold target
3537 if (navGetCurrentActualPositionAndVelocity()->vel
.z
* desiredClimbRate
>= 0 && fabsf(posControl
.desiredState
.pos
.z
- altitudeToUse
) < 500) {
3538 posControl
.desiredState
.pos
.z
+= desiredClimbRate
* timeDelta
;
3539 targetHoldActive
= false;
3540 } else if (!targetHoldActive
) { // Reset and hold target to actual + climb rate boost until actual catches up
3541 posControl
.desiredState
.pos
.z
= altitudeToUse
+ desiredClimbRate
;
3542 targetHoldActive
= true;
3545 targetHoldActive
= false;
3549 // Multicopter climb-rate control is closed-loop, it's possible to directly calculate desired altitude setpoint to yield the required RoC/RoD
3550 posControl
.desiredState
.pos
.z
= altitudeToUse
+ (desiredClimbRate
/ posControl
.pids
.pos
[Z
].param
.kP
);
3552 } else { // ROC_TO_ALT_RESET or zero desired climbrate
3553 posControl
.desiredState
.pos
.z
= altitudeToUse
;
3556 lastUpdateTimeUs
= currentTimeUs
;
3559 static void resetAltitudeController(bool useTerrainFollowing
)
3561 // Set terrain following flag
3562 posControl
.flags
.isTerrainFollowEnabled
= useTerrainFollowing
;
3564 if (STATE(FIXED_WING_LEGACY
)) {
3565 resetFixedWingAltitudeController();
3568 resetMulticopterAltitudeController();
3572 static void setupAltitudeController(void)
3574 if (STATE(FIXED_WING_LEGACY
)) {
3575 setupFixedWingAltitudeController();
3578 setupMulticopterAltitudeController();
3582 static bool adjustAltitudeFromRCInput(void)
3584 if (STATE(FIXED_WING_LEGACY
)) {
3585 return adjustFixedWingAltitudeFromRCInput();
3588 return adjustMulticopterAltitudeFromRCInput();
3592 /*-----------------------------------------------------------
3593 * Jump Counter support functions
3594 *-----------------------------------------------------------*/
3595 static void setupJumpCounters(void)
3597 for (uint8_t wp
= posControl
.startWpIndex
; wp
< posControl
.waypointCount
+ posControl
.startWpIndex
; wp
++) {
3598 if (posControl
.waypointList
[wp
].action
== NAV_WP_ACTION_JUMP
){
3599 posControl
.waypointList
[wp
].p3
= posControl
.waypointList
[wp
].p2
;
3604 static void resetJumpCounter(void)
3606 // reset the volatile counter from the set / static value
3607 posControl
.waypointList
[posControl
.activeWaypointIndex
].p3
= posControl
.waypointList
[posControl
.activeWaypointIndex
].p2
;
3610 static void clearJumpCounters(void)
3612 for (uint8_t wp
= posControl
.startWpIndex
; wp
< posControl
.waypointCount
+ posControl
.startWpIndex
; wp
++) {
3613 if (posControl
.waypointList
[wp
].action
== NAV_WP_ACTION_JUMP
) {
3614 posControl
.waypointList
[wp
].p3
= 0;
3621 /*-----------------------------------------------------------
3622 * Heading controller (pass-through to MAG mode)
3623 *-----------------------------------------------------------*/
3624 static void resetHeadingController(void)
3626 if (STATE(FIXED_WING_LEGACY
)) {
3627 resetFixedWingHeadingController();
3630 resetMulticopterHeadingController();
3634 static bool adjustHeadingFromRCInput(void)
3636 if (STATE(FIXED_WING_LEGACY
)) {
3637 return adjustFixedWingHeadingFromRCInput();
3640 return adjustMulticopterHeadingFromRCInput();
3644 /*-----------------------------------------------------------
3645 * XY Position controller
3646 *-----------------------------------------------------------*/
3647 static void resetPositionController(void)
3649 if (STATE(FIXED_WING_LEGACY
)) {
3650 resetFixedWingPositionController();
3653 resetMulticopterPositionController();
3654 resetMulticopterBrakingMode();
3658 static bool adjustPositionFromRCInput(void)
3662 if (STATE(FIXED_WING_LEGACY
)) {
3663 retValue
= adjustFixedWingPositionFromRCInput();
3667 const int16_t rcPitchAdjustment
= applyDeadbandRescaled(rcCommand
[PITCH
], rcControlsConfig()->pos_hold_deadband
, -500, 500);
3668 const int16_t rcRollAdjustment
= applyDeadbandRescaled(rcCommand
[ROLL
], rcControlsConfig()->pos_hold_deadband
, -500, 500);
3670 retValue
= adjustMulticopterPositionFromRCInput(rcPitchAdjustment
, rcRollAdjustment
);
3676 /*-----------------------------------------------------------
3678 *-----------------------------------------------------------*/
3679 void resetGCSFlags(void)
3681 posControl
.flags
.isGCSAssistedNavigationReset
= false;
3682 posControl
.flags
.isGCSAssistedNavigationEnabled
= false;
3685 void getWaypoint(uint8_t wpNumber
, navWaypoint_t
* wpData
)
3687 /* Default waypoint to send */
3688 wpData
->action
= NAV_WP_ACTION_RTH
;
3695 wpData
->flag
= NAV_WP_FLAG_LAST
;
3697 // WP #0 - special waypoint - HOME
3698 if (wpNumber
== 0) {
3699 if (STATE(GPS_FIX_HOME
)) {
3700 wpData
->lat
= GPS_home
.lat
;
3701 wpData
->lon
= GPS_home
.lon
;
3702 wpData
->alt
= GPS_home
.alt
;
3705 // WP #255 - special waypoint - directly get actualPosition
3706 else if (wpNumber
== 255) {
3707 gpsLocation_t wpLLH
;
3709 geoConvertLocalToGeodetic(&wpLLH
, &posControl
.gpsOrigin
, &navGetCurrentActualPositionAndVelocity()->pos
);
3711 wpData
->lat
= wpLLH
.lat
;
3712 wpData
->lon
= wpLLH
.lon
;
3713 wpData
->alt
= wpLLH
.alt
;
3715 // WP #254 - special waypoint - get desiredPosition that was set by ground control station if in 3D-guided mode
3716 else if (wpNumber
== 254) {
3717 navigationFSMStateFlags_t navStateFlags
= navGetStateFlags(posControl
.navState
);
3719 if ((posControl
.gpsOrigin
.valid
) && (navStateFlags
& NAV_CTL_ALT
) && (navStateFlags
& NAV_CTL_POS
)) {
3720 gpsLocation_t wpLLH
;
3722 geoConvertLocalToGeodetic(&wpLLH
, &posControl
.gpsOrigin
, &posControl
.desiredState
.pos
);
3724 wpData
->lat
= wpLLH
.lat
;
3725 wpData
->lon
= wpLLH
.lon
;
3726 wpData
->alt
= wpLLH
.alt
;
3729 // WP #1 - #60 - common waypoints - pre-programmed mission
3730 else if ((wpNumber
>= 1) && (wpNumber
<= NAV_MAX_WAYPOINTS
)) {
3731 if (wpNumber
<= getWaypointCount()) {
3732 *wpData
= posControl
.waypointList
[wpNumber
- 1 + (ARMING_FLAG(ARMED
) ? posControl
.startWpIndex
: 0)];
3733 if(wpData
->action
== NAV_WP_ACTION_JUMP
) {
3734 wpData
->p1
+= 1; // make WP # (vice index)
3740 void setWaypoint(uint8_t wpNumber
, const navWaypoint_t
* wpData
)
3742 gpsLocation_t wpLLH
;
3743 navWaypointPosition_t wpPos
;
3745 // Pre-fill structure to convert to local coordinates
3746 wpLLH
.lat
= wpData
->lat
;
3747 wpLLH
.lon
= wpData
->lon
;
3748 wpLLH
.alt
= wpData
->alt
;
3750 // WP #0 - special waypoint - HOME
3751 if ((wpNumber
== 0) && ARMING_FLAG(ARMED
) && (posControl
.flags
.estPosStatus
>= EST_USABLE
) && posControl
.gpsOrigin
.valid
&& posControl
.flags
.isGCSAssistedNavigationEnabled
) {
3752 // Forcibly set home position. Note that this is only valid if already armed, otherwise home will be reset instantly
3753 geoConvertGeodeticToLocal(&wpPos
.pos
, &posControl
.gpsOrigin
, &wpLLH
, GEO_ALT_RELATIVE
);
3754 setHomePosition(&wpPos
.pos
, 0, NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_Z
| NAV_POS_UPDATE_HEADING
, NAV_HOME_VALID_ALL
);
3756 // WP #255 - special waypoint - directly set desiredPosition
3757 // Only valid when armed and in poshold mode
3758 else if ((wpNumber
== 255) && (wpData
->action
== NAV_WP_ACTION_WAYPOINT
) &&
3759 ARMING_FLAG(ARMED
) && (posControl
.flags
.estPosStatus
== EST_TRUSTED
) && posControl
.gpsOrigin
.valid
&& posControl
.flags
.isGCSAssistedNavigationEnabled
&&
3760 (posControl
.navState
== NAV_STATE_POSHOLD_3D_IN_PROGRESS
)) {
3761 // Convert to local coordinates
3762 geoConvertGeodeticToLocal(&wpPos
.pos
, &posControl
.gpsOrigin
, &wpLLH
, GEO_ALT_RELATIVE
);
3764 navSetWaypointFlags_t waypointUpdateFlags
= NAV_POS_UPDATE_XY
;
3766 // If we received global altitude == 0, use current altitude
3767 if (wpData
->alt
!= 0) {
3768 waypointUpdateFlags
|= NAV_POS_UPDATE_Z
;
3771 if (wpData
->p1
> 0 && wpData
->p1
< 360) {
3772 waypointUpdateFlags
|= NAV_POS_UPDATE_HEADING
;
3775 setDesiredPosition(&wpPos
.pos
, DEGREES_TO_CENTIDEGREES(wpData
->p1
), waypointUpdateFlags
);
3777 // WP #1 - #NAV_MAX_WAYPOINTS - common waypoints - pre-programmed mission
3778 else if ((wpNumber
>= 1) && (wpNumber
<= NAV_MAX_WAYPOINTS
) && !ARMING_FLAG(ARMED
)) {
3779 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
) {
3780 // Only allow upload next waypoint (continue upload mission) or first waypoint (new mission)
3781 static int8_t nonGeoWaypointCount
= 0;
3783 if (wpNumber
== (posControl
.waypointCount
+ 1) || wpNumber
== 1) {
3784 if (wpNumber
== 1) {
3785 resetWaypointList();
3787 posControl
.waypointList
[wpNumber
- 1] = *wpData
;
3788 if(wpData
->action
== NAV_WP_ACTION_SET_POI
|| wpData
->action
== NAV_WP_ACTION_SET_HEAD
|| wpData
->action
== NAV_WP_ACTION_JUMP
) {
3789 nonGeoWaypointCount
+= 1;
3790 if(wpData
->action
== NAV_WP_ACTION_JUMP
) {
3791 posControl
.waypointList
[wpNumber
- 1].p1
-= 1; // make index (vice WP #)
3795 posControl
.waypointCount
= wpNumber
;
3796 posControl
.waypointListValid
= (wpData
->flag
== NAV_WP_FLAG_LAST
);
3797 posControl
.geoWaypointCount
= posControl
.waypointCount
- nonGeoWaypointCount
;
3798 if (posControl
.waypointListValid
) {
3799 nonGeoWaypointCount
= 0;
3806 void resetWaypointList(void)
3808 posControl
.waypointCount
= 0;
3809 posControl
.waypointListValid
= false;
3810 posControl
.geoWaypointCount
= 0;
3811 posControl
.startWpIndex
= 0;
3812 #ifdef USE_MULTI_MISSION
3813 posControl
.totalMultiMissionWpCount
= 0;
3814 posControl
.loadedMultiMissionIndex
= 0;
3815 posControl
.multiMissionCount
= 0;
3819 bool isWaypointListValid(void)
3821 return posControl
.waypointListValid
;
3824 int getWaypointCount(void)
3826 uint8_t waypointCount
= posControl
.waypointCount
;
3827 #ifdef USE_MULTI_MISSION
3828 if (!ARMING_FLAG(ARMED
) && posControl
.totalMultiMissionWpCount
) {
3829 waypointCount
= posControl
.totalMultiMissionWpCount
;
3832 return waypointCount
;
3835 #ifdef USE_MULTI_MISSION
3836 void selectMultiMissionIndex(int8_t increment
)
3838 if (posControl
.multiMissionCount
> 1) { // stick selection only active when multi mission loaded
3839 navConfigMutable()->general
.waypoint_multi_mission_index
= constrain(navConfigMutable()->general
.waypoint_multi_mission_index
+ increment
, 1, posControl
.multiMissionCount
);
3843 void loadSelectedMultiMission(uint8_t missionIndex
)
3845 uint8_t missionCount
= 1;
3846 posControl
.waypointCount
= 0;
3847 posControl
.geoWaypointCount
= 0;
3849 for (int i
= 0; i
< NAV_MAX_WAYPOINTS
; i
++) {
3850 if (missionCount
== missionIndex
) {
3851 /* store details of selected mission: start wp index, mission wp count, geo wp count */
3852 if (!(posControl
.waypointList
[i
].action
== NAV_WP_ACTION_SET_POI
||
3853 posControl
.waypointList
[i
].action
== NAV_WP_ACTION_SET_HEAD
||
3854 posControl
.waypointList
[i
].action
== NAV_WP_ACTION_JUMP
)) {
3855 posControl
.geoWaypointCount
++;
3858 if (posControl
.waypointCount
== 0) {
3859 posControl
.waypointCount
= 1; // start marker only, value unimportant (but not 0)
3860 posControl
.startWpIndex
= i
;
3863 if (posControl
.waypointList
[i
].flag
== NAV_WP_FLAG_LAST
) {
3864 posControl
.waypointCount
= i
- posControl
.startWpIndex
+ 1;
3867 } else if (posControl
.waypointList
[i
].flag
== NAV_WP_FLAG_LAST
) {
3872 posControl
.loadedMultiMissionIndex
= posControl
.multiMissionCount
? missionIndex
: 0;
3873 posControl
.activeWaypointIndex
= posControl
.startWpIndex
;
3876 bool updateWpMissionChange(void)
3878 /* Function only called when ARMED */
3880 if (posControl
.multiMissionCount
< 2 || posControl
.wpPlannerActiveWPIndex
|| FLIGHT_MODE(NAV_WP_MODE
)) {
3884 uint8_t setMissionIndex
= navConfig()->general
.waypoint_multi_mission_index
;
3885 if (!(IS_RC_MODE_ACTIVE(BOXCHANGEMISSION
) || isAdjustmentFunctionSelected(ADJUSTMENT_NAV_WP_MULTI_MISSION_INDEX
))) {
3886 /* reload mission if mission index changed */
3887 if (posControl
.loadedMultiMissionIndex
!= setMissionIndex
) {
3888 loadSelectedMultiMission(setMissionIndex
);
3893 static bool toggleFlag
= false;
3894 if (IS_RC_MODE_ACTIVE(BOXNAVWP
) && toggleFlag
) {
3895 if (setMissionIndex
== posControl
.multiMissionCount
) {
3896 navConfigMutable()->general
.waypoint_multi_mission_index
= 1;
3898 selectMultiMissionIndex(1);
3901 } else if (!IS_RC_MODE_ACTIVE(BOXNAVWP
)) {
3904 return false; // block WP mode while changing mission when armed
3907 bool checkMissionCount(int8_t waypoint
)
3909 if (nonVolatileWaypointList(waypoint
)->flag
== NAV_WP_FLAG_LAST
) {
3910 posControl
.multiMissionCount
+= 1; // count up no missions in multi mission WP file
3911 if (waypoint
!= NAV_MAX_WAYPOINTS
- 1) {
3912 return (nonVolatileWaypointList(waypoint
+ 1)->flag
== NAV_WP_FLAG_LAST
&&
3913 nonVolatileWaypointList(waypoint
+ 1)->action
==NAV_WP_ACTION_RTH
);
3914 // end of multi mission file if successive NAV_WP_FLAG_LAST and default action (RTH)
3919 #endif // multi mission
3920 #ifdef NAV_NON_VOLATILE_WAYPOINT_STORAGE
3921 bool loadNonVolatileWaypointList(bool clearIfLoaded
)
3923 /* Don't load if armed or mission planner active */
3924 if (ARMING_FLAG(ARMED
) || posControl
.wpPlannerActiveWPIndex
) {
3928 // if forced and waypoints are already loaded, just unload them.
3929 if (clearIfLoaded
&& posControl
.waypointCount
> 0) {
3930 resetWaypointList();
3933 #ifdef USE_MULTI_MISSION
3934 /* Reset multi mission index to 1 if exceeds number of available missions */
3935 if (navConfig()->general
.waypoint_multi_mission_index
> posControl
.multiMissionCount
) {
3936 navConfigMutable()->general
.waypoint_multi_mission_index
= 1;
3939 for (int i
= 0; i
< NAV_MAX_WAYPOINTS
; i
++) {
3940 setWaypoint(i
+ 1, nonVolatileWaypointList(i
));
3941 #ifdef USE_MULTI_MISSION
3942 /* count up number of missions and exit after last multi mission */
3943 if (checkMissionCount(i
)) {
3947 posControl
.totalMultiMissionWpCount
= posControl
.waypointCount
;
3948 loadSelectedMultiMission(navConfig()->general
.waypoint_multi_mission_index
);
3950 /* Mission sanity check failed - reset the list
3951 * Also reset if no selected mission loaded (shouldn't happen) */
3952 if (!posControl
.waypointListValid
|| !posControl
.waypointCount
) {
3954 // check this is the last waypoint
3955 if (nonVolatileWaypointList(i
)->flag
== NAV_WP_FLAG_LAST
) {
3960 // Mission sanity check failed - reset the list
3961 if (!posControl
.waypointListValid
) {
3963 resetWaypointList();
3966 return posControl
.waypointListValid
;
3969 bool saveNonVolatileWaypointList(void)
3971 if (ARMING_FLAG(ARMED
) || !posControl
.waypointListValid
)
3974 for (int i
= 0; i
< NAV_MAX_WAYPOINTS
; i
++) {
3975 getWaypoint(i
+ 1, nonVolatileWaypointListMutable(i
));
3977 #ifdef USE_MULTI_MISSION
3978 navConfigMutable()->general
.waypoint_multi_mission_index
= 1; // reset selected mission to 1 when new entries saved
3980 saveConfigAndNotify();
3986 #if defined(USE_SAFE_HOME)
3988 void resetSafeHomes(void)
3990 memset(safeHomeConfigMutable(0), 0, sizeof(navSafeHome_t
) * MAX_SAFE_HOMES
);
3994 static void mapWaypointToLocalPosition(fpVector3_t
* localPos
, const navWaypoint_t
* waypoint
, geoAltitudeConversionMode_e altConv
)
3996 gpsLocation_t wpLLH
;
3998 /* Default to home position if lat & lon = 0 or HOME flag set
3999 * Applicable to WAYPOINT, HOLD_TIME & LANDING WP types */
4000 if ((waypoint
->lat
== 0 && waypoint
->lon
== 0) || waypoint
->flag
== NAV_WP_FLAG_HOME
) {
4001 wpLLH
.lat
= GPS_home
.lat
;
4002 wpLLH
.lon
= GPS_home
.lon
;
4004 wpLLH
.lat
= waypoint
->lat
;
4005 wpLLH
.lon
= waypoint
->lon
;
4007 wpLLH
.alt
= waypoint
->alt
;
4009 geoConvertGeodeticToLocal(localPos
, &posControl
.gpsOrigin
, &wpLLH
, altConv
);
4012 static void calculateAndSetActiveWaypointToLocalPosition(const fpVector3_t
* pos
)
4014 // Calculate bearing towards waypoint and store it in waypoint bearing parameter (this will further be used to detect missed waypoints)
4015 if (isWaypointNavTrackingActive() && !(posControl
.activeWaypoint
.pos
.x
== pos
->x
&& posControl
.activeWaypoint
.pos
.y
== pos
->y
)) {
4016 posControl
.activeWaypoint
.bearing
= calculateBearingBetweenLocalPositions(&posControl
.activeWaypoint
.pos
, pos
);
4018 posControl
.activeWaypoint
.bearing
= calculateBearingToDestination(pos
);
4020 posControl
.activeWaypoint
.nextTurnAngle
= -1; // no turn angle set (-1), will be set by WP mode as required
4022 posControl
.activeWaypoint
.pos
= *pos
;
4024 // Set desired position to next waypoint (XYZ-controller)
4025 setDesiredPosition(&posControl
.activeWaypoint
.pos
, posControl
.activeWaypoint
.bearing
, NAV_POS_UPDATE_XY
| NAV_POS_UPDATE_Z
| NAV_POS_UPDATE_HEADING
);
4028 geoAltitudeConversionMode_e
waypointMissionAltConvMode(geoAltitudeDatumFlag_e datumFlag
)
4030 return ((datumFlag
& NAV_WP_MSL_DATUM
) == NAV_WP_MSL_DATUM
) ? GEO_ALT_ABSOLUTE
: GEO_ALT_RELATIVE
;
4033 static void calculateAndSetActiveWaypoint(const navWaypoint_t
* waypoint
)
4035 fpVector3_t localPos
;
4036 mapWaypointToLocalPosition(&localPos
, waypoint
, waypointMissionAltConvMode(waypoint
->p3
));
4037 calculateAndSetActiveWaypointToLocalPosition(&localPos
);
4039 if (navConfig()->fw
.wp_turn_smoothing
) {
4040 fpVector3_t posNextWp
;
4041 if (getLocalPosNextWaypoint(&posNextWp
)) {
4042 int32_t bearingToNextWp
= calculateBearingBetweenLocalPositions(&posControl
.activeWaypoint
.pos
, &posNextWp
);
4043 posControl
.activeWaypoint
.nextTurnAngle
= wrap_18000(bearingToNextWp
- posControl
.activeWaypoint
.bearing
);
4048 /* Checks if active waypoint is last in mission */
4049 bool isLastMissionWaypoint(void)
4051 return FLIGHT_MODE(NAV_WP_MODE
) && (posControl
.activeWaypointIndex
>= (posControl
.startWpIndex
+ posControl
.waypointCount
- 1) ||
4052 (posControl
.waypointList
[posControl
.activeWaypointIndex
].flag
== NAV_WP_FLAG_LAST
));
4055 /* Checks if Nav hold position is active */
4056 bool isNavHoldPositionActive(void)
4058 // WP mode last WP hold and Timed hold positions
4059 if (FLIGHT_MODE(NAV_WP_MODE
)) {
4060 return isLastMissionWaypoint() || NAV_Status
.state
== MW_NAV_STATE_HOLD_TIMED
;
4062 // RTH mode (spiral climb and Home positions but excluding RTH Trackback point positions) and POSHOLD mode
4063 // Also hold position during emergency landing if position valid
4064 return (FLIGHT_MODE(NAV_RTH_MODE
) && !posControl
.flags
.rthTrackbackActive
) ||
4065 FLIGHT_MODE(NAV_POSHOLD_MODE
) ||
4066 navigationIsExecutingAnEmergencyLanding();
4069 float getActiveSpeed(void)
4071 /* Currently only applicable for multicopter */
4073 // Speed limit for modes where speed manually controlled
4074 if (posControl
.flags
.isAdjustingPosition
|| FLIGHT_MODE(NAV_COURSE_HOLD_MODE
)) {
4075 return navConfig()->general
.max_manual_speed
;
4078 uint16_t waypointSpeed
= navConfig()->general
.auto_speed
;
4080 if (navGetStateFlags(posControl
.navState
) & NAV_AUTO_WP
) {
4081 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
)) {
4082 float wpSpecificSpeed
= 0.0f
;
4083 if(posControl
.waypointList
[posControl
.activeWaypointIndex
].action
== NAV_WP_ACTION_HOLD_TIME
)
4084 wpSpecificSpeed
= posControl
.waypointList
[posControl
.activeWaypointIndex
].p2
; // P1 is hold time
4086 wpSpecificSpeed
= posControl
.waypointList
[posControl
.activeWaypointIndex
].p1
; // default case
4088 if (wpSpecificSpeed
>= 50.0f
&& wpSpecificSpeed
<= navConfig()->general
.max_auto_speed
) {
4089 waypointSpeed
= wpSpecificSpeed
;
4090 } else if (wpSpecificSpeed
> navConfig()->general
.max_auto_speed
) {
4091 waypointSpeed
= navConfig()->general
.max_auto_speed
;
4096 return waypointSpeed
;
4099 bool isWaypointNavTrackingActive(void)
4101 // NAV_WP_MODE flag used rather than state flag NAV_AUTO_WP to ensure heading to initial waypoint
4102 // is set from current position not previous WP. Works for WP Restart intermediate WP as well as first mission WP.
4103 // (NAV_WP_MODE flag isn't set until WP initialisation is finished, i.e. after calculateAndSetActiveWaypoint called)
4105 return FLIGHT_MODE(NAV_WP_MODE
) || (posControl
.flags
.rthTrackbackActive
&& posControl
.activeRthTBPointIndex
!= posControl
.rthTBLastSavedIndex
);
4108 /*-----------------------------------------------------------
4109 * Process adjustments to alt, pos and yaw controllers
4110 *-----------------------------------------------------------*/
4111 static void processNavigationRCAdjustments(void)
4113 /* Process pilot's RC input. Disable all pilot's input when in FAILSAFE_MODE */
4114 navigationFSMStateFlags_t navStateFlags
= navGetStateFlags(posControl
.navState
);
4116 if (FLIGHT_MODE(FAILSAFE_MODE
)) {
4117 if (STATE(MULTIROTOR
) && navStateFlags
& NAV_RC_POS
) {
4118 resetMulticopterBrakingMode();
4120 posControl
.flags
.isAdjustingAltitude
= false;
4121 posControl
.flags
.isAdjustingPosition
= false;
4122 posControl
.flags
.isAdjustingHeading
= false;
4127 posControl
.flags
.isAdjustingAltitude
= (navStateFlags
& NAV_RC_ALT
) && adjustAltitudeFromRCInput();
4128 posControl
.flags
.isAdjustingPosition
= (navStateFlags
& NAV_RC_POS
) && adjustPositionFromRCInput();
4129 posControl
.flags
.isAdjustingHeading
= (navStateFlags
& NAV_RC_YAW
) && adjustHeadingFromRCInput();
4132 /*-----------------------------------------------------------
4133 * A main function to call position controllers at loop rate
4134 *-----------------------------------------------------------*/
4135 void applyWaypointNavigationAndAltitudeHold(void)
4137 const timeUs_t currentTimeUs
= micros();
4139 //Updata blackbox data
4141 if (posControl
.flags
.estAltStatus
== EST_TRUSTED
) navFlags
|= (1 << 0);
4142 if (posControl
.flags
.estAglStatus
== EST_TRUSTED
) navFlags
|= (1 << 1);
4143 if (posControl
.flags
.estPosStatus
== EST_TRUSTED
) navFlags
|= (1 << 2);
4144 if (posControl
.flags
.isTerrainFollowEnabled
) navFlags
|= (1 << 3);
4145 #if defined(NAV_GPS_GLITCH_DETECTION)
4146 if (isGPSGlitchDetected()) navFlags
|= (1 << 4);
4148 if (posControl
.flags
.estHeadingStatus
== EST_TRUSTED
) navFlags
|= (1 << 5);
4150 // Reset all navigation requests - NAV controllers will set them if necessary
4151 DISABLE_STATE(NAV_MOTOR_STOP_OR_IDLE
);
4153 // No navigation when disarmed
4154 if (!ARMING_FLAG(ARMED
)) {
4155 // If we are disarmed, abort forced RTH or Emergency Landing
4156 posControl
.flags
.forcedRTHActivated
= false;
4157 posControl
.flags
.forcedEmergLandingActivated
= false;
4158 posControl
.flags
.manualEmergLandActive
= false;
4159 // ensure WP missions always restart from first waypoint after disarm
4160 posControl
.activeWaypointIndex
= posControl
.startWpIndex
;
4161 // Reset RTH trackback
4162 posControl
.activeRthTBPointIndex
= -1;
4163 posControl
.flags
.rthTrackbackActive
= false;
4164 posControl
.rthTBWrapAroundCounter
= -1;
4170 posControl
.flags
.horizontalPositionDataConsumed
= false;
4171 posControl
.flags
.verticalPositionDataConsumed
= false;
4173 #ifdef USE_FW_AUTOLAND
4174 if (!isFwLandInProgess()) {
4175 posControl
.fwLandState
.landState
= FW_AUTOLAND_STATE_IDLE
;
4179 /* Process controllers */
4180 navigationFSMStateFlags_t navStateFlags
= navGetStateFlags(posControl
.navState
);
4181 if (STATE(ROVER
) || STATE(BOAT
)) {
4182 applyRoverBoatNavigationController(navStateFlags
, currentTimeUs
);
4183 } else if (STATE(FIXED_WING_LEGACY
)) {
4184 applyFixedWingNavigationController(navStateFlags
, currentTimeUs
);
4187 applyMulticopterNavigationController(navStateFlags
, currentTimeUs
);
4190 /* Consume position data */
4191 if (posControl
.flags
.horizontalPositionDataConsumed
)
4192 posControl
.flags
.horizontalPositionDataNew
= false;
4194 if (posControl
.flags
.verticalPositionDataConsumed
)
4195 posControl
.flags
.verticalPositionDataNew
= false;
4197 //Update blackbox data
4198 if (posControl
.flags
.isAdjustingPosition
) navFlags
|= (1 << 6);
4199 if (posControl
.flags
.isAdjustingAltitude
) navFlags
|= (1 << 7);
4200 if (posControl
.flags
.isAdjustingHeading
) navFlags
|= (1 << 8);
4202 navTargetPosition
[X
] = lrintf(posControl
.desiredState
.pos
.x
);
4203 navTargetPosition
[Y
] = lrintf(posControl
.desiredState
.pos
.y
);
4204 navTargetPosition
[Z
] = lrintf(posControl
.desiredState
.pos
.z
);
4206 navDesiredHeading
= wrap_36000(posControl
.desiredState
.yaw
);
4209 /*-----------------------------------------------------------
4210 * Set CF's FLIGHT_MODE from current NAV_MODE
4211 *-----------------------------------------------------------*/
4212 void switchNavigationFlightModes(void)
4214 const flightModeFlags_e enabledNavFlightModes
= navGetMappedFlightModes(posControl
.navState
);
4215 const flightModeFlags_e disabledFlightModes
= (NAV_ALTHOLD_MODE
| NAV_RTH_MODE
| NAV_POSHOLD_MODE
| NAV_WP_MODE
| NAV_LAUNCH_MODE
| NAV_COURSE_HOLD_MODE
) & (~enabledNavFlightModes
);
4216 DISABLE_FLIGHT_MODE(disabledFlightModes
);
4217 ENABLE_FLIGHT_MODE(enabledNavFlightModes
);
4220 /*-----------------------------------------------------------
4221 * desired NAV_MODE from combination of FLIGHT_MODE flags
4222 *-----------------------------------------------------------*/
4223 static bool canActivateAltHoldMode(void)
4225 return (posControl
.flags
.estAltStatus
>= EST_USABLE
);
4228 static bool canActivatePosHoldMode(void)
4230 return (posControl
.flags
.estPosStatus
>= EST_USABLE
) && (posControl
.flags
.estVelStatus
== EST_TRUSTED
) && (posControl
.flags
.estHeadingStatus
>= EST_USABLE
);
4233 static bool canActivateNavigationModes(void)
4235 return (posControl
.flags
.estPosStatus
== EST_TRUSTED
) && (posControl
.flags
.estVelStatus
== EST_TRUSTED
) && (posControl
.flags
.estHeadingStatus
>= EST_USABLE
);
4238 static bool isWaypointMissionValid(void)
4240 return posControl
.waypointListValid
&& (posControl
.waypointCount
> 0);
4243 void checkManualEmergencyLandingControl(bool forcedActivation
)
4245 static timeMs_t timeout
= 0;
4246 static int8_t counter
= 0;
4248 timeMs_t currentTimeMs
= millis();
4250 if (timeout
&& currentTimeMs
> timeout
) {
4252 counter
-= counter
? 1 : 0;
4257 if (IS_RC_MODE_ACTIVE(BOXNAVPOSHOLD
)) {
4258 if (!timeout
&& toggle
) {
4259 timeout
= currentTimeMs
+ 4000;
4267 // Emergency landing toggled ON or OFF after 5 cycles of Poshold mode @ 1Hz minimum rate
4268 if (counter
>= 5 || forcedActivation
) {
4270 posControl
.flags
.manualEmergLandActive
= !posControl
.flags
.manualEmergLandActive
;
4272 if (!posControl
.flags
.manualEmergLandActive
) {
4273 navProcessFSMEvents(NAV_FSM_EVENT_SWITCH_TO_IDLE
);
4278 static navigationFSMEvent_t
selectNavEventFromBoxModeInput(void)
4280 static bool canActivateLaunchMode
= false;
4282 //We can switch modes only when ARMED
4283 if (ARMING_FLAG(ARMED
)) {
4284 // Ask failsafe system if we can use navigation system
4285 if (failsafeBypassNavigation()) {
4286 return NAV_FSM_EVENT_SWITCH_TO_IDLE
;
4289 // Flags if we can activate certain nav modes (check if we have required sensors and they provide valid data)
4290 const bool canActivateAltHold
= canActivateAltHoldMode();
4291 const bool canActivatePosHold
= canActivatePosHoldMode();
4292 const bool canActivateNavigation
= canActivateNavigationModes();
4293 const bool isExecutingRTH
= navGetStateFlags(posControl
.navState
) & NAV_AUTO_RTH
;
4294 #ifdef USE_SAFE_HOME
4295 checkSafeHomeState(isExecutingRTH
|| posControl
.flags
.forcedRTHActivated
);
4297 // deactivate rth trackback if RTH not active
4298 if (posControl
.flags
.rthTrackbackActive
) {
4299 posControl
.flags
.rthTrackbackActive
= isExecutingRTH
;
4302 /* Emergency landing controlled manually by rapid switching of Poshold mode.
4303 * Landing toggled ON or OFF for each Poshold activation sequence */
4304 checkManualEmergencyLandingControl(false);
4306 /* Emergency landing triggered by failsafe Landing or manually initiated */
4307 if (posControl
.flags
.forcedEmergLandingActivated
|| posControl
.flags
.manualEmergLandActive
) {
4308 return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
;
4311 /* Keep Emergency landing mode active once triggered.
4312 * If caused by sensor failure - landing auto cancelled if sensors working again or when WP and RTH deselected or if Althold selected.
4313 * If caused by RTH Sanity Checking - landing cancelled if RTH deselected.
4314 * Remains active if failsafe active regardless of mode selections */
4315 if (navigationIsExecutingAnEmergencyLanding()) {
4316 bool autonomousNavIsPossible
= canActivateNavigation
&& canActivateAltHold
&& STATE(GPS_FIX_HOME
);
4317 bool emergLandingCancel
= (!autonomousNavIsPossible
&&
4318 ((IS_RC_MODE_ACTIVE(BOXNAVALTHOLD
) && canActivateAltHold
) || !(IS_RC_MODE_ACTIVE(BOXNAVWP
) || IS_RC_MODE_ACTIVE(BOXNAVRTH
)))) ||
4319 (autonomousNavIsPossible
&& !IS_RC_MODE_ACTIVE(BOXNAVRTH
));
4321 if ((!posControl
.rthSanityChecker
.rthSanityOK
|| !autonomousNavIsPossible
) && (!emergLandingCancel
|| FLIGHT_MODE(FAILSAFE_MODE
))) {
4322 return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING
;
4325 posControl
.rthSanityChecker
.rthSanityOK
= true;
4327 /* WP mission activation control:
4328 * canActivateWaypoint & waypointWasActivated are used to prevent WP mission
4329 * auto restarting after interruption by Manual or RTH modes.
4330 * WP mode must be deselected before it can be reactivated again. */
4331 static bool waypointWasActivated
= false;
4332 const bool isWpMissionLoaded
= isWaypointMissionValid();
4333 bool canActivateWaypoint
= isWpMissionLoaded
&& !posControl
.flags
.wpMissionPlannerActive
; // Block activation if using WP Mission Planner
4335 if (waypointWasActivated
&& !FLIGHT_MODE(NAV_WP_MODE
)) {
4336 canActivateWaypoint
= false;
4337 if (!IS_RC_MODE_ACTIVE(BOXNAVWP
)) {
4338 canActivateWaypoint
= true;
4339 waypointWasActivated
= false;
4343 /* Airplane specific modes */
4344 if (STATE(AIRPLANE
)) {
4345 // LAUNCH mode has priority over any other NAV mode
4346 if (isNavLaunchEnabled()) { // FIXME: Only available for fixed wing aircrafts now
4347 if (canActivateLaunchMode
) {
4348 canActivateLaunchMode
= false;
4349 return NAV_FSM_EVENT_SWITCH_TO_LAUNCH
;
4351 else if FLIGHT_MODE(NAV_LAUNCH_MODE
) {
4352 // Make sure we don't bail out to IDLE
4353 return NAV_FSM_EVENT_NONE
;
4357 // If we were in LAUNCH mode - force switch to IDLE only if the throttle is low or throttle stick < launch idle throttle
4358 if (FLIGHT_MODE(NAV_LAUNCH_MODE
)) {
4359 if (abortLaunchAllowed()) {
4360 return NAV_FSM_EVENT_SWITCH_TO_IDLE
;
4362 return NAV_FSM_EVENT_NONE
;
4367 /* Soaring mode, disables altitude control in Position hold and Course hold modes.
4368 * Pitch allowed to freefloat within defined Angle mode deadband */
4369 if (IS_RC_MODE_ACTIVE(BOXSOARING
) && (FLIGHT_MODE(NAV_POSHOLD_MODE
) || FLIGHT_MODE(NAV_COURSE_HOLD_MODE
))) {
4370 ENABLE_FLIGHT_MODE(SOARING_MODE
);
4372 DISABLE_FLIGHT_MODE(SOARING_MODE
);
4376 /* If we request forced RTH - attempt to activate it no matter what
4377 * This might switch to emergency landing controller if GPS is unavailable */
4378 if (posControl
.flags
.forcedRTHActivated
) {
4379 return NAV_FSM_EVENT_SWITCH_TO_RTH
;
4382 /* Pilot-triggered RTH, also fall-back for WP if there is no mission loaded.
4383 * WP prevented from falling back to RTH if WP mission planner is active */
4384 const bool wpRthFallbackIsActive
= IS_RC_MODE_ACTIVE(BOXNAVWP
) && !isWpMissionLoaded
&& !posControl
.flags
.wpMissionPlannerActive
;
4385 if (IS_RC_MODE_ACTIVE(BOXNAVRTH
) || wpRthFallbackIsActive
) {
4386 // Check for isExecutingRTH to prevent switching our from RTH in case of a brief GPS loss
4387 // Without this loss of any of the canActivateNavigation && canActivateAltHold
4388 // will kick us out of RTH state machine via NAV_FSM_EVENT_SWITCH_TO_IDLE and will prevent any of the fall-back
4389 // logic kicking in (waiting for GPS on airplanes, switch to emergency landing etc)
4390 if (isExecutingRTH
|| (canActivateNavigation
&& canActivateAltHold
&& STATE(GPS_FIX_HOME
))) {
4391 return NAV_FSM_EVENT_SWITCH_TO_RTH
;
4395 // MANUAL mode has priority over WP/PH/AH
4396 if (IS_RC_MODE_ACTIVE(BOXMANUAL
)) {
4397 return NAV_FSM_EVENT_SWITCH_TO_IDLE
;
4400 // Pilot-activated waypoint mission. Fall-back to RTH if no mission loaded.
4401 // Also check multimission mission change status before activating WP mode.
4402 #ifdef USE_MULTI_MISSION
4403 if (updateWpMissionChange() && IS_RC_MODE_ACTIVE(BOXNAVWP
) && canActivateWaypoint
) {
4405 if (IS_RC_MODE_ACTIVE(BOXNAVWP
) && canActivateWaypoint
) {
4407 if (FLIGHT_MODE(NAV_WP_MODE
) || (canActivateNavigation
&& canActivateAltHold
&& STATE(GPS_FIX_HOME
))) {
4408 waypointWasActivated
= true;
4409 return NAV_FSM_EVENT_SWITCH_TO_WAYPOINT
;
4413 if (IS_RC_MODE_ACTIVE(BOXNAVPOSHOLD
)) {
4414 if (FLIGHT_MODE(NAV_POSHOLD_MODE
) || (canActivatePosHold
&& canActivateAltHold
))
4415 return NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D
;
4418 // CRUISE has priority over COURSE_HOLD and AH
4419 if (IS_RC_MODE_ACTIVE(BOXNAVCRUISE
)) {
4420 if ((FLIGHT_MODE(NAV_COURSE_HOLD_MODE
) && FLIGHT_MODE(NAV_ALTHOLD_MODE
)) || (canActivatePosHold
&& canActivateAltHold
))
4421 return NAV_FSM_EVENT_SWITCH_TO_CRUISE
;
4424 // PH has priority over COURSE_HOLD
4425 // CRUISE has priority on AH
4426 if (IS_RC_MODE_ACTIVE(BOXNAVCOURSEHOLD
)) {
4427 if (IS_RC_MODE_ACTIVE(BOXNAVALTHOLD
) && ((FLIGHT_MODE(NAV_COURSE_HOLD_MODE
) && FLIGHT_MODE(NAV_ALTHOLD_MODE
)) || (canActivatePosHold
&& canActivateAltHold
))) {
4428 return NAV_FSM_EVENT_SWITCH_TO_CRUISE
;
4431 if (FLIGHT_MODE(NAV_COURSE_HOLD_MODE
) || (canActivatePosHold
)) {
4432 return NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD
;
4436 if (IS_RC_MODE_ACTIVE(BOXNAVALTHOLD
)) {
4437 if ((FLIGHT_MODE(NAV_ALTHOLD_MODE
)) || (canActivateAltHold
))
4438 return NAV_FSM_EVENT_SWITCH_TO_ALTHOLD
;
4441 // 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)
4442 canActivateLaunchMode
= isNavLaunchEnabled() && (!sensors(SENSOR_GPS
) || (sensors(SENSOR_GPS
) && !isGPSHeadingValid()));
4445 return NAV_FSM_EVENT_SWITCH_TO_IDLE
;
4448 /*-----------------------------------------------------------
4449 * An indicator that throttle tilt compensation is forced
4450 *-----------------------------------------------------------*/
4451 bool navigationRequiresThrottleTiltCompensation(void)
4453 return !STATE(FIXED_WING_LEGACY
) && (navGetStateFlags(posControl
.navState
) & NAV_REQUIRE_THRTILT
);
4456 /*-----------------------------------------------------------
4457 * An indicator that ANGLE mode must be forced per NAV requirement
4458 *-----------------------------------------------------------*/
4459 bool navigationRequiresAngleMode(void)
4461 const navigationFSMStateFlags_t currentState
= navGetStateFlags(posControl
.navState
);
4462 return (currentState
& NAV_REQUIRE_ANGLE
) || ((currentState
& NAV_REQUIRE_ANGLE_FW
) && STATE(FIXED_WING_LEGACY
));
4465 /*-----------------------------------------------------------
4466 * An indicator that TURN ASSISTANCE is required for navigation
4467 *-----------------------------------------------------------*/
4468 bool navigationRequiresTurnAssistance(void)
4470 const navigationFSMStateFlags_t currentState
= navGetStateFlags(posControl
.navState
);
4471 if (STATE(FIXED_WING_LEGACY
)) {
4472 // For airplanes turn assistant is always required when controlling position
4473 return (currentState
& (NAV_CTL_POS
| NAV_CTL_ALT
));
4480 * An indicator that NAV is in charge of heading control (a signal to disable other heading controllers)
4482 int8_t navigationGetHeadingControlState(void)
4484 // For airplanes report as manual heading control
4485 if (STATE(FIXED_WING_LEGACY
)) {
4486 return NAV_HEADING_CONTROL_MANUAL
;
4489 // For multirotors it depends on navigation system mode
4490 // Course hold requires Auto Control to update heading hold target whilst RC adjustment active
4491 if (navGetStateFlags(posControl
.navState
) & NAV_REQUIRE_MAGHOLD
) {
4492 if (posControl
.flags
.isAdjustingHeading
&& !FLIGHT_MODE(NAV_COURSE_HOLD_MODE
)) {
4493 return NAV_HEADING_CONTROL_MANUAL
;
4496 return NAV_HEADING_CONTROL_AUTO
;
4499 return NAV_HEADING_CONTROL_NONE
;
4502 bool navigationTerrainFollowingEnabled(void)
4504 return posControl
.flags
.isTerrainFollowEnabled
;
4507 uint32_t distanceToFirstWP(void)
4509 fpVector3_t startingWaypointPos
;
4510 mapWaypointToLocalPosition(&startingWaypointPos
, &posControl
.waypointList
[posControl
.startWpIndex
], GEO_ALT_RELATIVE
);
4511 return calculateDistanceToDestination(&startingWaypointPos
);
4514 bool navigationPositionEstimateIsHealthy(void)
4516 return (posControl
.flags
.estPosStatus
>= EST_USABLE
) && STATE(GPS_FIX_HOME
);
4519 navArmingBlocker_e
navigationIsBlockingArming(bool *usedBypass
)
4521 const bool navBoxModesEnabled
= IS_RC_MODE_ACTIVE(BOXNAVRTH
) || IS_RC_MODE_ACTIVE(BOXNAVWP
) || IS_RC_MODE_ACTIVE(BOXNAVCOURSEHOLD
) ||
4522 IS_RC_MODE_ACTIVE(BOXNAVCRUISE
) || IS_RC_MODE_ACTIVE(BOXNAVPOSHOLD
) || (STATE(FIXED_WING_LEGACY
) && IS_RC_MODE_ACTIVE(BOXNAVALTHOLD
));
4525 *usedBypass
= false;
4528 // Apply extra arming safety only if pilot has any of GPS modes configured
4529 if ((isUsingNavigationModes() || failsafeMayRequireNavigationMode()) && !navigationPositionEstimateIsHealthy()) {
4530 if (navConfig()->general
.flags
.extra_arming_safety
== NAV_EXTRA_ARMING_SAFETY_ALLOW_BYPASS
&&
4531 (STATE(NAV_EXTRA_ARMING_SAFETY_BYPASSED
) || checkStickPosition(YAW_HI
))) {
4535 return NAV_ARMING_BLOCKER_NONE
;
4537 return NAV_ARMING_BLOCKER_MISSING_GPS_FIX
;
4540 // Don't allow arming if any of NAV modes is active
4541 if (!ARMING_FLAG(ARMED
) && navBoxModesEnabled
) {
4542 return NAV_ARMING_BLOCKER_NAV_IS_ALREADY_ACTIVE
;
4545 // Don't allow arming if first waypoint is farther than configured safe distance
4546 if ((posControl
.waypointCount
> 0) && (navConfig()->general
.waypoint_safe_distance
!= 0)) {
4547 if (distanceToFirstWP() > METERS_TO_CENTIMETERS(navConfig()->general
.waypoint_safe_distance
) && !checkStickPosition(YAW_HI
)) {
4548 return NAV_ARMING_BLOCKER_FIRST_WAYPOINT_TOO_FAR
;
4553 * Don't allow arming if any of JUMP waypoint has invalid settings
4554 * First WP can't be JUMP
4555 * Can't jump to immediately adjacent WPs (pointless)
4556 * Can't jump beyond WP list
4557 * Only jump to geo-referenced WP types
4559 if (posControl
.waypointCount
) {
4560 for (uint8_t wp
= posControl
.startWpIndex
; wp
< posControl
.waypointCount
+ posControl
.startWpIndex
; wp
++){
4561 if (posControl
.waypointList
[wp
].action
== NAV_WP_ACTION_JUMP
){
4562 if (wp
== posControl
.startWpIndex
|| posControl
.waypointList
[wp
].p1
>= posControl
.waypointCount
||
4563 (posControl
.waypointList
[wp
].p1
> (wp
- posControl
.startWpIndex
- 2) && posControl
.waypointList
[wp
].p1
< (wp
- posControl
.startWpIndex
+ 2)) || posControl
.waypointList
[wp
].p2
< -1) {
4564 return NAV_ARMING_BLOCKER_JUMP_WAYPOINT_ERROR
;
4567 /* check for target geo-ref sanity */
4568 uint16_t target
= posControl
.waypointList
[wp
].p1
+ posControl
.startWpIndex
;
4569 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
)) {
4570 return NAV_ARMING_BLOCKER_JUMP_WAYPOINT_ERROR
;
4576 return NAV_ARMING_BLOCKER_NONE
;
4580 * Indicate ready/not ready status
4582 static void updateReadyStatus(void)
4584 static bool posReadyBeepDone
= false;
4586 /* Beep out READY_BEEP once when position lock is firstly acquired and HOME set */
4587 if (navigationPositionEstimateIsHealthy() && !posReadyBeepDone
) {
4588 beeper(BEEPER_READY_BEEP
);
4589 posReadyBeepDone
= true;
4593 void updateFlightBehaviorModifiers(void)
4595 if (posControl
.flags
.isGCSAssistedNavigationEnabled
&& !IS_RC_MODE_ACTIVE(BOXGCSNAV
)) {
4596 posControl
.flags
.isGCSAssistedNavigationReset
= true;
4599 posControl
.flags
.isGCSAssistedNavigationEnabled
= IS_RC_MODE_ACTIVE(BOXGCSNAV
);
4602 /* On the fly WP mission planner mode allows WP missions to be setup during navigation.
4603 * Uses the WP mode switch to save WP at current location (WP mode disabled when active)
4604 * Mission can be flown after mission planner mode switched off and saved after disarm. */
4606 void updateWpMissionPlanner(void)
4608 static timeMs_t resetTimerStart
= 0;
4609 if (IS_RC_MODE_ACTIVE(BOXPLANWPMISSION
) && !(FLIGHT_MODE(NAV_WP_MODE
) || isWaypointMissionRTHActive())) {
4610 const bool positionTrusted
= posControl
.flags
.estAltStatus
== EST_TRUSTED
&& posControl
.flags
.estPosStatus
== EST_TRUSTED
&& STATE(GPS_FIX
);
4612 posControl
.flags
.wpMissionPlannerActive
= true;
4613 if (millis() - resetTimerStart
< 1000 && navConfig()->general
.flags
.mission_planner_reset
) {
4614 posControl
.waypointCount
= posControl
.wpPlannerActiveWPIndex
= 0;
4615 posControl
.waypointListValid
= false;
4616 posControl
.wpMissionPlannerStatus
= WP_PLAN_WAIT
;
4618 if (positionTrusted
&& posControl
.wpMissionPlannerStatus
!= WP_PLAN_FULL
) {
4619 missionPlannerSetWaypoint();
4621 posControl
.wpMissionPlannerStatus
= posControl
.wpMissionPlannerStatus
== WP_PLAN_FULL
? WP_PLAN_FULL
: WP_PLAN_WAIT
;
4623 } else if (posControl
.flags
.wpMissionPlannerActive
) {
4624 posControl
.flags
.wpMissionPlannerActive
= false;
4625 posControl
.activeWaypointIndex
= 0;
4626 resetTimerStart
= millis();
4630 void missionPlannerSetWaypoint(void)
4632 static bool boxWPModeIsReset
= true;
4634 boxWPModeIsReset
= !boxWPModeIsReset
? !IS_RC_MODE_ACTIVE(BOXNAVWP
) : boxWPModeIsReset
; // only able to save new WP when WP mode reset
4635 posControl
.wpMissionPlannerStatus
= boxWPModeIsReset
? boxWPModeIsReset
: posControl
.wpMissionPlannerStatus
; // hold save status until WP mode reset
4637 if (!boxWPModeIsReset
|| !IS_RC_MODE_ACTIVE(BOXNAVWP
)) {
4641 if (!posControl
.wpPlannerActiveWPIndex
) { // reset existing mission data before adding first WP
4642 resetWaypointList();
4645 gpsLocation_t wpLLH
;
4646 geoConvertLocalToGeodetic(&wpLLH
, &posControl
.gpsOrigin
, &navGetCurrentActualPositionAndVelocity()->pos
);
4648 posControl
.waypointList
[posControl
.wpPlannerActiveWPIndex
].action
= 1;
4649 posControl
.waypointList
[posControl
.wpPlannerActiveWPIndex
].lat
= wpLLH
.lat
;
4650 posControl
.waypointList
[posControl
.wpPlannerActiveWPIndex
].lon
= wpLLH
.lon
;
4651 posControl
.waypointList
[posControl
.wpPlannerActiveWPIndex
].alt
= wpLLH
.alt
;
4652 posControl
.waypointList
[posControl
.wpPlannerActiveWPIndex
].p1
= posControl
.waypointList
[posControl
.wpPlannerActiveWPIndex
].p2
= 0;
4653 posControl
.waypointList
[posControl
.wpPlannerActiveWPIndex
].p3
|= NAV_WP_ALTMODE
; // use absolute altitude datum
4654 posControl
.waypointList
[posControl
.wpPlannerActiveWPIndex
].flag
= NAV_WP_FLAG_LAST
;
4655 posControl
.waypointListValid
= true;
4657 if (posControl
.wpPlannerActiveWPIndex
) {
4658 posControl
.waypointList
[posControl
.wpPlannerActiveWPIndex
- 1].flag
= 0; // rollling reset of previous end of mission flag when new WP added
4661 posControl
.wpPlannerActiveWPIndex
+= 1;
4662 posControl
.waypointCount
= posControl
.geoWaypointCount
= posControl
.wpPlannerActiveWPIndex
;
4663 posControl
.wpMissionPlannerStatus
= posControl
.waypointCount
== NAV_MAX_WAYPOINTS
? WP_PLAN_FULL
: WP_PLAN_OK
;
4664 boxWPModeIsReset
= false;
4668 * Process NAV mode transition and WP/RTH state machine
4669 * Update rate: RX (data driven or 50Hz)
4671 void updateWaypointsAndNavigationMode(void)
4673 /* Initiate home position update */
4674 updateHomePosition();
4676 /* Update flight statistics */
4677 updateNavigationFlightStatistics();
4679 /* Update NAV ready status */
4680 updateReadyStatus();
4682 // Update flight behaviour modifiers
4683 updateFlightBehaviorModifiers();
4685 // Process switch to a different navigation mode (if needed)
4686 navProcessFSMEvents(selectNavEventFromBoxModeInput());
4688 // Process pilot's RC input to adjust behaviour
4689 processNavigationRCAdjustments();
4691 // Map navMode back to enabled flight modes
4692 switchNavigationFlightModes();
4694 // Update WP mission planner
4695 updateWpMissionPlanner();
4697 // Update RTH trackback
4698 updateRthTrackback(false);
4700 //Update Blackbox data
4701 navCurrentState
= (int16_t)posControl
.navPersistentId
;
4704 /*-----------------------------------------------------------
4705 * NAV main control functions
4706 *-----------------------------------------------------------*/
4707 void navigationUsePIDs(void)
4709 /** Multicopter PIDs */
4710 // Brake time parameter
4711 posControl
.posDecelerationTime
= (float)navConfig()->mc
.posDecelerationTime
/ 100.0f
;
4713 // Position controller expo (taret vel expo for MC)
4714 posControl
.posResponseExpo
= constrainf((float)navConfig()->mc
.posResponseExpo
/ 100.0f
, 0.0f
, 1.0f
);
4716 // Initialize position hold P-controller
4717 for (int axis
= 0; axis
< 2; axis
++) {
4719 &posControl
.pids
.pos
[axis
],
4720 (float)pidProfile()->bank_mc
.pid
[PID_POS_XY
].P
/ 100.0f
,
4728 navPidInit(&posControl
.pids
.vel
[axis
], (float)pidProfile()->bank_mc
.pid
[PID_VEL_XY
].P
/ 20.0f
,
4729 (float)pidProfile()->bank_mc
.pid
[PID_VEL_XY
].I
/ 100.0f
,
4730 (float)pidProfile()->bank_mc
.pid
[PID_VEL_XY
].D
/ 100.0f
,
4731 (float)pidProfile()->bank_mc
.pid
[PID_VEL_XY
].FF
/ 100.0f
,
4732 pidProfile()->navVelXyDTermLpfHz
,
4738 * Set coefficients used in MC VEL_XY
4740 multicopterPosXyCoefficients
.dTermAttenuation
= pidProfile()->navVelXyDtermAttenuation
/ 100.0f
;
4741 multicopterPosXyCoefficients
.dTermAttenuationStart
= pidProfile()->navVelXyDtermAttenuationStart
/ 100.0f
;
4742 multicopterPosXyCoefficients
.dTermAttenuationEnd
= pidProfile()->navVelXyDtermAttenuationEnd
/ 100.0f
;
4744 #ifdef USE_MR_BRAKING_MODE
4745 multicopterPosXyCoefficients
.breakingBoostFactor
= (float) navConfig()->mc
.braking_boost_factor
/ 100.0f
;
4748 // Initialize altitude hold PID-controllers (pos_z, vel_z, acc_z
4750 &posControl
.pids
.pos
[Z
],
4751 (float)pidProfile()->bank_mc
.pid
[PID_POS_Z
].P
/ 100.0f
,
4759 navPidInit(&posControl
.pids
.vel
[Z
], (float)pidProfile()->bank_mc
.pid
[PID_VEL_Z
].P
/ 66.7f
,
4760 (float)pidProfile()->bank_mc
.pid
[PID_VEL_Z
].I
/ 20.0f
,
4761 (float)pidProfile()->bank_mc
.pid
[PID_VEL_Z
].D
/ 100.0f
,
4763 NAV_VEL_Z_DERIVATIVE_CUT_HZ
,
4764 NAV_VEL_Z_ERROR_CUT_HZ
4767 // Initialize surface tracking PID
4768 navPidInit(&posControl
.pids
.surface
, 2.0f
,
4776 /** Airplane PIDs */
4777 // Initialize fixed wing PID controllers
4778 navPidInit(&posControl
.pids
.fw_nav
, (float)pidProfile()->bank_fw
.pid
[PID_POS_XY
].P
/ 100.0f
,
4779 (float)pidProfile()->bank_fw
.pid
[PID_POS_XY
].I
/ 100.0f
,
4780 (float)pidProfile()->bank_fw
.pid
[PID_POS_XY
].D
/ 100.0f
,
4786 navPidInit(&posControl
.pids
.fw_alt
, (float)pidProfile()->bank_fw
.pid
[PID_POS_Z
].P
/ 10.0f
,
4787 (float)pidProfile()->bank_fw
.pid
[PID_POS_Z
].I
/ 10.0f
,
4788 (float)pidProfile()->bank_fw
.pid
[PID_POS_Z
].D
/ 10.0f
,
4794 navPidInit(&posControl
.pids
.fw_heading
, (float)pidProfile()->bank_fw
.pid
[PID_POS_HEADING
].P
/ 10.0f
,
4795 (float)pidProfile()->bank_fw
.pid
[PID_POS_HEADING
].I
/ 10.0f
,
4796 (float)pidProfile()->bank_fw
.pid
[PID_POS_HEADING
].D
/ 100.0f
,
4803 void navigationInit(void)
4806 posControl
.navState
= NAV_STATE_IDLE
;
4808 posControl
.flags
.horizontalPositionDataNew
= false;
4809 posControl
.flags
.verticalPositionDataNew
= false;
4811 posControl
.flags
.estAltStatus
= EST_NONE
;
4812 posControl
.flags
.estPosStatus
= EST_NONE
;
4813 posControl
.flags
.estVelStatus
= EST_NONE
;
4814 posControl
.flags
.estHeadingStatus
= EST_NONE
;
4815 posControl
.flags
.estAglStatus
= EST_NONE
;
4817 posControl
.flags
.forcedRTHActivated
= false;
4818 posControl
.flags
.forcedEmergLandingActivated
= false;
4819 posControl
.waypointCount
= 0;
4820 posControl
.activeWaypointIndex
= 0;
4821 posControl
.waypointListValid
= false;
4822 posControl
.wpPlannerActiveWPIndex
= 0;
4823 posControl
.flags
.wpMissionPlannerActive
= false;
4824 posControl
.startWpIndex
= 0;
4825 posControl
.safehomeState
.isApplied
= false;
4826 #ifdef USE_MULTI_MISSION
4827 posControl
.multiMissionCount
= 0;
4829 /* Set initial surface invalid */
4830 posControl
.actualState
.surfaceMin
= -1.0f
;
4832 /* Reset statistics */
4833 posControl
.totalTripDistance
= 0.0f
;
4835 /* Use system config */
4836 navigationUsePIDs();
4838 #if defined(NAV_NON_VOLATILE_WAYPOINT_STORAGE)
4839 /* configure WP missions at boot */
4840 #ifdef USE_MULTI_MISSION
4841 for (int8_t i
= 0; i
< NAV_MAX_WAYPOINTS
; i
++) { // check number missions in NVM
4842 if (checkMissionCount(i
)) {
4846 /* set index to 1 if saved mission index > available missions */
4847 if (navConfig()->general
.waypoint_multi_mission_index
> posControl
.multiMissionCount
) {
4848 navConfigMutable()->general
.waypoint_multi_mission_index
= 1;
4851 /* load mission on boot */
4852 if (navConfig()->general
.waypoint_load_on_boot
) {
4853 loadNonVolatileWaypointList(false);
4858 /*-----------------------------------------------------------
4859 * Access to estimated position/velocity data
4860 *-----------------------------------------------------------*/
4861 float getEstimatedActualVelocity(int axis
)
4863 return navGetCurrentActualPositionAndVelocity()->vel
.v
[axis
];
4866 float getEstimatedActualPosition(int axis
)
4868 return navGetCurrentActualPositionAndVelocity()->pos
.v
[axis
];
4871 /*-----------------------------------------------------------
4872 * Ability to execute RTH on external event
4873 *-----------------------------------------------------------*/
4874 void activateForcedRTH(void)
4876 abortFixedWingLaunch();
4877 posControl
.flags
.forcedRTHActivated
= true;
4878 #ifdef USE_SAFE_HOME
4879 checkSafeHomeState(true);
4881 navProcessFSMEvents(selectNavEventFromBoxModeInput());
4884 void abortForcedRTH(void)
4886 // Disable failsafe RTH and make sure we back out of navigation mode to IDLE
4887 // If any navigation mode was active prior to RTH it will be re-enabled with next RX update
4888 posControl
.flags
.forcedRTHActivated
= false;
4889 #ifdef USE_SAFE_HOME
4890 checkSafeHomeState(false);
4892 navProcessFSMEvents(NAV_FSM_EVENT_SWITCH_TO_IDLE
);
4895 rthState_e
getStateOfForcedRTH(void)
4897 /* If forced RTH activated and in AUTO_RTH or EMERG state */
4898 if (posControl
.flags
.forcedRTHActivated
&& (navGetStateFlags(posControl
.navState
) & (NAV_AUTO_RTH
| NAV_CTL_EMERG
| NAV_MIXERAT
))) {
4899 if (posControl
.navState
== NAV_STATE_RTH_FINISHED
|| posControl
.navState
== NAV_STATE_EMERGENCY_LANDING_FINISHED
) {
4900 return RTH_HAS_LANDED
;
4903 return RTH_IN_PROGRESS
;
4911 /*-----------------------------------------------------------
4912 * Ability to execute Emergency Landing on external event
4913 *-----------------------------------------------------------*/
4914 void activateForcedEmergLanding(void)
4916 abortFixedWingLaunch();
4917 posControl
.flags
.forcedEmergLandingActivated
= true;
4918 navProcessFSMEvents(selectNavEventFromBoxModeInput());
4921 void abortForcedEmergLanding(void)
4923 // Disable emergency landing and make sure we back out of navigation mode to IDLE
4924 // If any navigation mode was active prior to emergency landing it will be re-enabled with next RX update
4925 posControl
.flags
.forcedEmergLandingActivated
= false;
4926 navProcessFSMEvents(NAV_FSM_EVENT_SWITCH_TO_IDLE
);
4929 emergLandState_e
getStateOfForcedEmergLanding(void)
4931 /* If forced emergency landing activated and in EMERG state */
4932 if (posControl
.flags
.forcedEmergLandingActivated
&& (navGetStateFlags(posControl
.navState
) & NAV_CTL_EMERG
)) {
4933 if (posControl
.navState
== NAV_STATE_EMERGENCY_LANDING_FINISHED
) {
4934 return EMERG_LAND_HAS_LANDED
;
4936 return EMERG_LAND_IN_PROGRESS
;
4939 return EMERG_LAND_IDLE
;
4943 bool isWaypointMissionRTHActive(void)
4945 return (navGetStateFlags(posControl
.navState
) & NAV_AUTO_RTH
) && IS_RC_MODE_ACTIVE(BOXNAVWP
) &&
4946 !(IS_RC_MODE_ACTIVE(BOXNAVRTH
) || posControl
.flags
.forcedRTHActivated
);
4949 bool navigationIsExecutingAnEmergencyLanding(void)
4951 return navGetCurrentStateFlags() & NAV_CTL_EMERG
;
4954 bool navigationInAutomaticThrottleMode(void)
4956 navigationFSMStateFlags_t stateFlags
= navGetCurrentStateFlags();
4957 return (stateFlags
& (NAV_CTL_ALT
| NAV_CTL_EMERG
| NAV_CTL_LAND
)) ||
4958 ((stateFlags
& NAV_CTL_LAUNCH
) && !navConfig()->fw
.launch_manual_throttle
);
4961 bool navigationIsControllingThrottle(void)
4963 // Note that this makes a detour into mixer code to evaluate actual motor status
4964 return navigationInAutomaticThrottleMode() && getMotorStatus() != MOTOR_STOPPED_USER
&& !FLIGHT_MODE(SOARING_MODE
);
4967 bool navigationIsControllingAltitude(void) {
4968 navigationFSMStateFlags_t stateFlags
= navGetCurrentStateFlags();
4969 return (stateFlags
& NAV_CTL_ALT
);
4972 bool navigationIsFlyingAutonomousMode(void)
4974 navigationFSMStateFlags_t stateFlags
= navGetCurrentStateFlags();
4975 return (stateFlags
& (NAV_AUTO_RTH
| NAV_AUTO_WP
));
4978 bool navigationRTHAllowsLanding(void)
4980 // WP mission RTH landing setting
4981 if (isWaypointMissionRTHActive() && isWaypointMissionValid()) {
4982 return posControl
.waypointList
[posControl
.startWpIndex
+ posControl
.waypointCount
- 1].p1
> 0;
4985 // normal RTH landing setting
4986 navRTHAllowLanding_e allow
= navConfig()->general
.flags
.rth_allow_landing
;
4987 return allow
== NAV_RTH_ALLOW_LANDING_ALWAYS
||
4988 (allow
== NAV_RTH_ALLOW_LANDING_FS_ONLY
&& FLIGHT_MODE(FAILSAFE_MODE
));
4991 bool isNavLaunchEnabled(void)
4993 return (IS_RC_MODE_ACTIVE(BOXNAVLAUNCH
) || feature(FEATURE_FW_LAUNCH
)) && STATE(AIRPLANE
);
4996 bool abortLaunchAllowed(void)
4998 // allow NAV_LAUNCH_MODE to be aborted if throttle is low or throttle stick position is < launch idle throttle setting
4999 return throttleStickIsLow() || throttleStickMixedValue() < currentBatteryProfile
->nav
.fw
.launch_idle_throttle
;
5002 int32_t navigationGetHomeHeading(void)
5004 return posControl
.rthState
.homePosition
.heading
;
5008 float calculateAverageSpeed(void) {
5009 float flightTime
= getFlightTime();
5010 if (flightTime
== 0.0f
) return 0;
5011 return (float)getTotalTravelDistance() / (flightTime
* 100);
5014 const navigationPIDControllers_t
* getNavigationPIDControllers(void) {
5015 return &posControl
.pids
;
5018 bool isAdjustingPosition(void) {
5019 return posControl
.flags
.isAdjustingPosition
;
5022 bool isAdjustingHeading(void) {
5023 return posControl
.flags
.isAdjustingHeading
;
5026 int32_t getCruiseHeadingAdjustment(void) {
5027 return wrap_18000(posControl
.cruise
.course
- posControl
.cruise
.previousCourse
);
5030 int32_t navigationGetHeadingError(void)
5032 return wrap_18000(posControl
.desiredState
.yaw
- posControl
.actualState
.cog
);
5035 int8_t navCheckActiveAngleHoldAxis(void)
5037 int8_t activeAxis
= -1;
5039 if (IS_RC_MODE_ACTIVE(BOXANGLEHOLD
)) {
5040 navigationFSMStateFlags_t stateFlags
= navGetCurrentStateFlags();
5041 bool altholdActive
= stateFlags
& NAV_REQUIRE_ANGLE_FW
&& !(stateFlags
& NAV_REQUIRE_ANGLE
);
5043 if (FLIGHT_MODE(NAV_COURSE_HOLD_MODE
) && !FLIGHT_MODE(NAV_ALTHOLD_MODE
)) {
5044 activeAxis
= FD_PITCH
;
5045 } else if (altholdActive
) {
5046 activeAxis
= FD_ROLL
;
5053 uint8_t getActiveWpNumber(void)
5055 return NAV_Status
.activeWpNumber
;
5058 #ifdef USE_FW_AUTOLAND
5060 static void resetFwAutoland(void)
5062 posControl
.fwLandState
.landAltAgl
= 0;
5063 posControl
.fwLandState
.landAproachAltAgl
= 0;
5064 posControl
.fwLandState
.loiterStartTime
= 0;
5065 posControl
.fwLandState
.approachSettingIdx
= 0;
5066 posControl
.fwLandState
.landPosHeading
= -1;
5067 posControl
.fwLandState
.landState
= FW_AUTOLAND_STATE_IDLE
;
5068 posControl
.fwLandState
.landWp
= false;
5071 static int32_t calcFinalApproachHeading(int32_t approachHeading
, int32_t windAngle
)
5073 if (approachHeading
== 0) {
5077 int32_t windRelToRunway
= wrap_36000(windAngle
- ABS(approachHeading
));
5079 if (windRelToRunway
>= 27000 || windRelToRunway
<= 9000) {
5080 return wrap_36000(ABS(approachHeading
));
5083 if (approachHeading
> 0) {
5084 return wrap_36000(approachHeading
+ 18000);
5090 static float getLandAltitude(void)
5092 float altitude
= -1;
5093 #ifdef USE_RANGEFINDER
5094 if (rangefinderIsHealthy() && rangefinderGetLatestAltitude() > RANGEFINDER_OUT_OF_RANGE
) {
5095 altitude
= rangefinderGetLatestAltitude();
5099 if (posControl
.flags
.estAglStatus
>= EST_USABLE
) {
5100 altitude
= posControl
.actualState
.agl
.pos
.z
;
5102 altitude
= posControl
.actualState
.abs
.pos
.z
;
5107 static int32_t calcWindDiff(int32_t heading
, int32_t windHeading
)
5109 return ABS(wrap_18000(windHeading
- heading
));
5112 static void setLandWaypoint(const fpVector3_t
*pos
, const fpVector3_t
*nextWpPos
)
5114 calculateAndSetActiveWaypointToLocalPosition(pos
);
5116 if (navConfig()->fw
.wp_turn_smoothing
&& nextWpPos
!= NULL
) {
5117 int32_t bearingToNextWp
= calculateBearingBetweenLocalPositions(&posControl
.activeWaypoint
.pos
, nextWpPos
);
5118 posControl
.activeWaypoint
.nextTurnAngle
= wrap_18000(bearingToNextWp
- posControl
.activeWaypoint
.bearing
);
5120 posControl
.activeWaypoint
.nextTurnAngle
= -1;
5123 posControl
.wpInitialDistance
= calculateDistanceToDestination(&posControl
.activeWaypoint
.pos
);
5124 posControl
.wpInitialAltitude
= posControl
.actualState
.abs
.pos
.z
;
5125 posControl
.wpAltitudeReached
= false;
5128 void resetFwAutolandApproach(int8_t idx
)
5130 if (idx
>= 0 && idx
< MAX_FW_LAND_APPOACH_SETTINGS
) {
5131 memset(fwAutolandApproachConfigMutable(idx
), 0, sizeof(navFwAutolandApproach_t
));
5133 memset(fwAutolandApproachConfigMutable(0), 0, sizeof(navFwAutolandApproach_t
) * MAX_FW_LAND_APPOACH_SETTINGS
);
5137 bool isFwLandInProgess(void)
5139 return posControl
.navState
== NAV_STATE_FW_LANDING_CLIMB_TO_LOITER
5140 || posControl
.navState
== NAV_STATE_FW_LANDING_LOITER
5141 || posControl
.navState
== NAV_STATE_FW_LANDING_APPROACH
5142 || posControl
.navState
== NAV_STATE_FW_LANDING_GLIDE
5143 || posControl
.navState
== NAV_STATE_FW_LANDING_FLARE
;
5146 bool canFwLandCanceld(void)
5148 return posControl
.navState
== NAV_STATE_FW_LANDING_CLIMB_TO_LOITER
5149 || posControl
.navState
== NAV_STATE_FW_LANDING_LOITER
5150 || posControl
.navState
== NAV_STATE_FW_LANDING_APPROACH
5151 || posControl
.navState
== NAV_STATE_FW_LANDING_GLIDE
;