LCOV - code coverage report
Current view: top level - home/pbarker/rc/ardupilot/ArduCopter - mode_auto.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 774 1105 70.0 %
Date: 2026-09-09 11:02:08 Functions: 70 94 74.5 %

          Line data    Source code
       1             : #include "Copter.h"
       2             : 
       3             : #if MODE_AUTO_ENABLED
       4             : 
       5             : /*
       6             :  * Init and run calls for auto flight mode
       7             :  *
       8             :  * This file contains the implementation for Land, Waypoint navigation and Takeoff from Auto mode
       9             :  * Command execution code (i.e. command_logic.pde) should:
      10             :  *      a) switch to Auto flight mode with set_mode() function.  This will cause auto_init to be called
      11             :  *      b) call one of the three auto initialisation functions: auto_wp_start(), auto_takeoff_start(), auto_land_start()
      12             :  *      c) call one of the verify functions auto_wp_verify(), auto_takeoff_verify, auto_land_verify repeated to check if the command has completed
      13             :  * The main loop (i.e. fast loop) will call update_flight_modes() which will in turn call auto_run() which, based upon the auto_mode variable will call
      14             :  *      correct auto_wp_run, auto_takeoff_run or auto_land_run to actually implement the feature
      15             :  */
      16             : 
      17             : /*
      18             :  *  While in the auto flight mode, navigation or do/now commands can be run.
      19             :  *  Code in this file implements the navigation commands
      20             :  */
      21             : 
      22             : // auto_init - initialise auto controller
      23          98 : bool ModeAuto::init(bool ignore_checks)
      24             : {
      25          98 :     auto_RTL = false;
      26          98 :     if (mission.present() || ignore_checks) {
      27             :         // reject switching to auto mode if landed with motors armed but first command is not a takeoff (reduce chance of flips)
      28          98 :         if (motors->armed() && copter.ap.land_complete && !mission.starts_with_takeoff_cmd()) {
      29           0 :             gcs().send_text(MAV_SEVERITY_CRITICAL, "Auto: Missing Takeoff Cmd");
      30           0 :             return false;
      31             :         }
      32             : 
      33          98 :         _mode = SubMode::LOITER;
      34             : 
      35             :         // stop ROI from carrying over from previous runs of the mission
      36             :         // To-Do: reset the yaw as part of auto_wp_start when the previous command was not a wp command to remove the need for this special ROI check
      37          98 :         if (auto_yaw.mode() == AutoYaw::Mode::ROI) {
      38           0 :             auto_yaw.set_mode(AutoYaw::Mode::HOLD);
      39             :         }
      40             : 
      41             :         // initialise waypoint and spline controller
      42          98 :         wp_nav->wp_and_spline_init_m();
      43             : 
      44             :         // initialise desired speed overrides
      45          98 :         desired_speed_override_ms = {0, 0, 0};
      46             : 
      47             :         // initialise LOITER_TURNS circle state
      48          98 :         circle = {};
      49             : 
      50             :         // set flag to start mission
      51          98 :         waiting_to_start = true;
      52             : 
      53             :         // initialise mission change check (ignore results)
      54          98 :         IGNORE_RETURN(mis_change_detector.check_for_mission_change());
      55             : 
      56             :         // clear guided limits
      57          98 :         copter.mode_guided.limit_clear();
      58             : 
      59             :         // reset flag indicating if pilot has applied roll or pitch inputs during landing
      60          98 :         copter.ap.land_repo_active = false;
      61             : 
      62             : #if AC_PRECLAND_ENABLED
      63             :         // initialise precland state machine
      64          98 :         copter.precland_statemachine.init();
      65             : #endif
      66             : 
      67          98 :         return true;
      68             :     } else {
      69           0 :         return false;
      70             :     }
      71             : }
      72             : 
      73             : // stop mission when we leave auto mode
      74          80 : void ModeAuto::exit()
      75             : {
      76          80 :     if (copter.mode_auto.mission.state() == AP_Mission::MISSION_RUNNING) {
      77          43 :         copter.mode_auto.mission.stop();
      78             :     }
      79             : #if HAL_MOUNT_ENABLED
      80          80 :     copter.camera_mount.set_mode_to_default();
      81             : #endif  // HAL_MOUNT_ENABLED
      82             : 
      83          80 :     auto_RTL = false;
      84          80 : }
      85             : 
      86             : // auto_run - runs the auto controller
      87             : //      should be called at 100hz or more
      88     3482367 : void ModeAuto::run()
      89             : {
      90             :     // start or update mission
      91     3482367 :     if (waiting_to_start) {
      92             :         // don't start the mission until we have an origin
      93       44563 :         Location loc;
      94       44563 :         if (copter.ahrs.get_origin(loc)) {
      95             :             // start/resume the mission (based on MIS_RESTART parameter)
      96          98 :             mission.start_or_resume();
      97          98 :             waiting_to_start = false;
      98             : 
      99             :             // initialise mission change check (ignore results)
     100          98 :             IGNORE_RETURN(mis_change_detector.check_for_mission_change());
     101             :         }
     102             :     } else {
     103             :         // check for mission changes
     104     3437804 :         if (mis_change_detector.check_for_mission_change()) {
     105             :             // if mission is running restart the current command if it is a waypoint or spline command
     106          29 :             if ((mission.state() == AP_Mission::MISSION_RUNNING) && (_mode == SubMode::WP)) {
     107           1 :                 if (mission.restart_current_nav_cmd()) {
     108           0 :                     gcs().send_text(MAV_SEVERITY_CRITICAL, "Auto mission changed, restarted command");
     109             :                 } else {
     110             :                     // failed to restart mission for some reason
     111           1 :                     gcs().send_text(MAV_SEVERITY_CRITICAL, "Auto mission changed but failed to restart command");
     112             :                 }
     113             :             }
     114             :         }
     115             : 
     116     3437804 :         mission.update();
     117             :     }
     118             : 
     119             :     // call the correct auto controller
     120     3482367 :     switch (_mode) {
     121             : 
     122      685267 :     case SubMode::TAKEOFF:
     123      685267 :         takeoff_run();
     124      685267 :         break;
     125             : 
     126     1501079 :     case SubMode::WP:
     127             :     case SubMode::CIRCLE_MOVE_TO_EDGE:
     128             :     case SubMode::CIRCLE:
     129             :         // the circle orbit is flown as an S-curve waypoint leg
     130     1501079 :         wp_run();
     131     1501079 :         break;
     132             : 
     133      151476 :     case SubMode::LAND:
     134      151476 :         land_run();
     135      151476 :         break;
     136             : 
     137     1014590 :     case SubMode::RTL:
     138     1014590 :         rtl_run();
     139     1014590 :         break;
     140             : 
     141           0 :     case SubMode::NAVGUIDED:
     142             :     case SubMode::NAV_SCRIPT_TIME:
     143             : #if AC_NAV_GUIDED || AP_SCRIPTING_ENABLED
     144           0 :         nav_guided_run();
     145             : #endif
     146           0 :         break;
     147             : 
     148       91648 :     case SubMode::LOITER:
     149       91648 :         loiter_run();
     150       91648 :         break;
     151             : 
     152       14519 :     case SubMode::LOITER_TO_ALT:
     153       14519 :         loiter_to_alt_run();
     154       14519 :         break;
     155             : 
     156             : #if AP_MISSION_NAV_PAYLOAD_PLACE_ENABLED && AC_PAYLOAD_PLACE_ENABLED
     157       23788 :     case SubMode::NAV_PAYLOAD_PLACE:
     158       23788 :         payload_place.run();
     159       23788 :         break;
     160             : #endif
     161             : 
     162           0 :     case SubMode::NAV_ATTITUDE_TIME:
     163           0 :         nav_attitude_time_run();
     164           0 :         break;
     165             :     }
     166             : 
     167             :     // only pretend to be in auto RTL so long as mission still thinks its in a landing sequence or the mission has completed
     168     3482367 :     const bool auto_rtl_active = mission.get_in_landing_sequence_flag() || mission.get_in_return_path_flag() || mission.state() == AP_Mission::mission_state::MISSION_COMPLETE;
     169     3482367 :     if (auto_RTL && !auto_rtl_active) {
     170           4 :         auto_RTL = false;
     171             :         // log exit from Auto RTL
     172             : #if HAL_LOGGING_ENABLED
     173           4 :         copter.logger.Write_Mode((uint8_t)copter.flightmode->mode_number(), ModeReason::AUTO_RTL_EXIT);
     174             : #endif
     175             :     }
     176     3482367 : }
     177             : 
     178             : // return true if a position estimate is required
     179      175799 : bool ModeAuto::requires_position() const
     180             : {
     181             :     // position estimate is required in all sub modes except attitude control
     182      175799 :     return _mode != SubMode::NAV_ATTITUDE_TIME;
     183             : }
     184             : 
     185             : // set submode.  This may re-trigger the vehicle's EKF failsafe if the new submode requires a position estimate
     186         416 : void ModeAuto::set_submode(SubMode new_submode)
     187             : {
     188             :     // return immediately if the submode has not been changed
     189         416 :     if (new_submode == _mode) {
     190         175 :         return;
     191             :     }
     192             : 
     193             :     // backup old mode
     194         241 :     SubMode old_submode = _mode;
     195             : 
     196             :     // set mode
     197         241 :     _mode = new_submode;
     198             : 
     199             :     // if changing out of the nav-attitude-time submode, recheck the EKF failsafe
     200             :     // this may trigger a flight mode change if the EKF failsafe is active
     201         241 :     if (old_submode == SubMode::NAV_ATTITUDE_TIME) {
     202           0 :         copter.failsafe_ekf_recheck();
     203             :     }
     204             : }
     205             : 
     206     5527316 : bool ModeAuto::option_is_enabled(Option option) const
     207             : {
     208     5527316 :     return ((copter.g2.auto_options & (uint32_t)option) != 0);
     209             : }
     210             : 
     211          53 : bool ModeAuto::allows_arming(AP_Arming::Method method) const
     212             : {
     213          53 :     if (auto_RTL) {
     214           0 :         return false;
     215             :     }
     216          53 :     return option_is_enabled(Option::AllowArming);
     217             : }
     218             : 
     219             : #if WEATHERVANE_ENABLED
     220     2958721 : bool ModeAuto::allows_weathervaning() const
     221             : {
     222     2958721 :     return option_is_enabled(Option::AllowWeatherVaning);
     223             : }
     224             : #endif
     225             : 
     226             : // determine EKF reset handling method based on Guide submode
     227     3482367 : bool ModeAuto::move_vehicle_on_ekf_reset() const
     228             : {
     229             :         // call the correct auto controller
     230     3482367 :     switch (_mode) {
     231     1981320 :     case SubMode::TAKEOFF:
     232             :     case SubMode::LAND:
     233             :     case SubMode::RTL:
     234             :     case SubMode::NAVGUIDED:
     235             :     case SubMode::LOITER:
     236             :     case SubMode::LOITER_TO_ALT:
     237             : #if AP_MISSION_NAV_PAYLOAD_PLACE_ENABLED && AC_PAYLOAD_PLACE_ENABLED
     238             :     case SubMode::NAV_PAYLOAD_PLACE:
     239             : #endif
     240             :     case SubMode::NAV_SCRIPT_TIME:
     241             :     case SubMode::NAV_ATTITUDE_TIME:
     242             :         // these submodes reset their targets so the vehicle does not physically move
     243     1981320 :         return false;
     244     1501047 :     case SubMode::WP:
     245             :     case SubMode::CIRCLE_MOVE_TO_EDGE:
     246             :     case SubMode::CIRCLE:
     247             :         // these submodes smoothly move to maintain an absolute position
     248             :         // (the circle orbit and the move-to-edge leg are S-curve waypoint legs, like WP)
     249     1501047 :         return true;
     250             :     }
     251             : 
     252             :     // should never reach here but just in case
     253           0 :     return true;
     254             : }
     255             : 
     256             : // Go straight to landing sequence via DO_LAND_START, if succeeds pretend to be Auto RTL mode
     257           5 : bool ModeAuto::jump_to_landing_sequence_auto_RTL(ModeReason reason)
     258             : {
     259           5 :     if (!mission.jump_to_landing_sequence(get_stopping_point())) {
     260           1 :         LOGGER_WRITE_ERROR(LogErrorSubsystem::FLIGHT_MODE, LogErrorCode(Number::AUTO_RTL));
     261             :         // make sad noise
     262           1 :         if (copter.ap.initialised) {
     263           1 :             AP_Notify::events.user_mode_change_failed = 1;
     264             :         }
     265           1 :         gcs().send_text(MAV_SEVERITY_WARNING, "Mode change to AUTO RTL failed: No landing sequence found");
     266           1 :         return false;
     267             :     }
     268             : 
     269           4 :     return enter_auto_rtl(reason);
     270             : }
     271             : 
     272             : // Join mission after DO_RETURN_PATH_START waypoint, if succeeds pretend to be Auto RTL mode
     273           5 : bool ModeAuto::return_path_start_auto_RTL(ModeReason reason)
     274             : {
     275           5 :     if (!mission.jump_to_closest_mission_leg(get_stopping_point())) {
     276           2 :         LOGGER_WRITE_ERROR(LogErrorSubsystem::FLIGHT_MODE, LogErrorCode(Number::AUTO_RTL));
     277             :         // make sad noise
     278           2 :         if (copter.ap.initialised) {
     279           2 :             AP_Notify::events.user_mode_change_failed = 1;
     280             :         }
     281           2 :         gcs().send_text(MAV_SEVERITY_WARNING, "Mode change to AUTO RTL failed: No return path found");
     282           2 :         return false;
     283             :     }
     284             : 
     285           3 :     return enter_auto_rtl(reason);
     286             : }
     287             : 
     288             : // Try join return path else do land start
     289          13 : bool ModeAuto::return_path_or_jump_to_landing_sequence_auto_RTL(ModeReason reason)
     290             : {
     291          13 :     const Location stopping_point = get_stopping_point();
     292          13 :     if (!mission.jump_to_closest_mission_leg(stopping_point) && !mission.jump_to_landing_sequence(stopping_point)) {
     293          11 :         LOGGER_WRITE_ERROR(LogErrorSubsystem::FLIGHT_MODE, LogErrorCode(Number::AUTO_RTL));
     294             :         // make sad noise
     295          11 :         if (copter.ap.initialised) {
     296          11 :             AP_Notify::events.user_mode_change_failed = 1;
     297             :         }
     298          11 :         gcs().send_text(MAV_SEVERITY_WARNING, "Mode change to AUTO RTL failed: No return path or landing sequence found");
     299          11 :         return false;
     300             :     }
     301             : 
     302           2 :     return enter_auto_rtl(reason);
     303             : }
     304             : 
     305             : // Enter auto rtl pseudo mode
     306           9 : bool ModeAuto::enter_auto_rtl(ModeReason reason) 
     307             : {
     308           9 :     mission.set_force_resume(true);
     309             : 
     310             :     // if not already in auto switch to auto
     311           9 :     if ((copter.flightmode == this) || set_mode(Mode::Number::AUTO, reason)) {
     312           9 :         auto_RTL = true;
     313             : #if HAL_LOGGING_ENABLED
     314             :         // log entry into AUTO RTL
     315           9 :         copter.logger.Write_Mode((uint8_t)copter.flightmode->mode_number(), reason);
     316             : #endif
     317             : 
     318             :         // make happy noise
     319           9 :         if (copter.ap.initialised) {
     320           9 :             AP_Notify::events.user_mode_change = 1;
     321             :         }
     322           9 :         return true;
     323             :     }
     324             : 
     325             :     // mode change failed, revert force resume flag
     326           0 :     mission.set_force_resume(false);
     327             : 
     328           0 :     LOGGER_WRITE_ERROR(LogErrorSubsystem::FLIGHT_MODE, LogErrorCode(Number::AUTO_RTL));
     329             :     // make sad noise
     330           0 :     if (copter.ap.initialised) {
     331           0 :         AP_Notify::events.user_mode_change_failed = 1;
     332             :     }
     333           0 :     return false;
     334             : }
     335             : 
     336             : // lua scripts use this to retrieve the contents of the active command
     337           0 : bool ModeAuto::nav_script_time(uint16_t &id, uint8_t &cmd, float &arg1, float &arg2, int16_t &arg3, int16_t &arg4)
     338             : {
     339             : #if AP_SCRIPTING_ENABLED
     340           0 :     if (_mode == SubMode::NAV_SCRIPT_TIME) {
     341           0 :         id = nav_scripting.id;
     342           0 :         cmd = nav_scripting.command;
     343           0 :         arg1 = nav_scripting.arg1;
     344           0 :         arg2 = nav_scripting.arg2;
     345           0 :         arg3 = nav_scripting.arg3;
     346           0 :         arg4 = nav_scripting.arg4;
     347           0 :         return true;
     348             :     }
     349             : #endif
     350           0 :     return false;
     351             : }
     352             : 
     353             : // lua scripts use this to indicate when they have complete the command
     354           0 : void ModeAuto::nav_script_time_done(uint16_t id)
     355             : {
     356             : #if AP_SCRIPTING_ENABLED
     357           0 :     if ((_mode == SubMode::NAV_SCRIPT_TIME) && (id == nav_scripting.id)) {
     358           0 :         nav_scripting.done = true;
     359             :     }
     360             : #endif
     361           0 : }
     362             : 
     363             : // auto_loiter_start - initialises loitering in auto mode
     364             : //  returns success/failure because this can be called by exit_mission
     365           3 : bool ModeAuto::loiter_start()
     366             : {
     367             :     // return failure if GPS is bad
     368           3 :     if (!copter.position_ok()) {
     369           0 :         return false;
     370             :     }
     371           3 :     _mode = SubMode::LOITER;
     372             : 
     373             :     // calculate stopping point
     374           3 :     Vector3p stopping_point_ned_m;
     375           3 :     wp_nav->get_wp_stopping_point_NED_m(stopping_point_ned_m);
     376             : 
     377             :     // initialise waypoint controller target to stopping point
     378           3 :     wp_nav->set_wp_destination_NED_m(stopping_point_ned_m);
     379             : 
     380             :     // hold yaw at current heading
     381           3 :     auto_yaw.set_mode(AutoYaw::Mode::HOLD);
     382             : 
     383           3 :     return true;
     384             : }
     385             : 
     386             : // auto_rtl_start - initialises RTL in AUTO flight mode
     387          46 : void ModeAuto::rtl_start()
     388             : {
     389             :     // call regular rtl flight mode initialisation and ask it to ignore checks
     390          46 :     if (copter.mode_rtl.init(true)) {
     391          46 :         set_submode(SubMode::RTL);
     392             :     } else {
     393             :         // this should never happen because RTL never fails init if argument is true
     394           0 :         INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
     395             :     }
     396          46 : }
     397             : 
     398             : // initialise waypoint controller to implement take-off
     399          92 : void ModeAuto::takeoff_start(const Location& dest_loc)
     400             : {
     401          92 :     if (!copter.current_loc.initialised()) {
     402             :         // this should never happen because mission commands are not executed until
     403             :         // the AHRS/EKF origin is set by which time current_loc should also have been set
     404           0 :         INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
     405           0 :         return;
     406             :     }
     407             : 
     408             :     // calculate current and target altitudes
     409             :     // by default current_alt_m and alt_target_m are alt-above-EKF-origin
     410             :     float alt_target_m;
     411          92 :     bool alt_target_terrain = false;
     412          92 :     float current_alt_m = pos_control->get_pos_estimate_U_m();
     413             :     float terrain_u_m;   // terrain's altitude in m above the ekf origin
     414          92 :     if ((dest_loc.get_alt_frame() == Location::AltFrame::ABOVE_TERRAIN) && wp_nav->get_terrain_U_m(terrain_u_m)) {
     415             :         // subtract terrain offset to convert vehicle's alt-above-ekf-origin to alt-above-terrain
     416           0 :         current_alt_m -= terrain_u_m;
     417             : 
     418             :         // specify alt_target_m as alt-above-terrain
     419           0 :         alt_target_m = dest_loc.alt * 0.01;
     420           0 :         alt_target_terrain = true;
     421             :     } else {
     422             :         // set horizontal target
     423          92 :         Location dest(dest_loc);
     424          92 :         dest.lat = copter.current_loc.lat;
     425          92 :         dest.lng = copter.current_loc.lng;
     426             : 
     427             :         // get altitude target above EKF origin
     428          92 :         if (!dest.get_alt_m(Location::AltFrame::ABOVE_ORIGIN, alt_target_m)) {
     429             :             // this failure could only happen if take-off alt was specified as an alt-above terrain and we have no terrain data
     430           9 :             LOGGER_WRITE_ERROR(LogErrorSubsystem::TERRAIN, LogErrorCode::MISSING_TERRAIN_DATA);
     431             :             // fall back to altitude above current altitude
     432           9 :             alt_target_m = current_alt_m + dest.alt * 0.01;
     433             :         }
     434             :     }
     435             : 
     436             :     // sanity check target
     437          92 :     float alt_target_min_m = current_alt_m + (copter.ap.land_complete ? 1.0 : 0.0);
     438          92 :     alt_target_m = MAX(alt_target_m, alt_target_min_m);
     439             : 
     440             :     // initialise yaw
     441          92 :     auto_yaw.set_mode(AutoYaw::Mode::HOLD);
     442             : 
     443             :     // clear i term when we're taking off
     444          92 :     pos_control->D_init_controller();
     445             : 
     446             :     // initialise alt for WP_NAVALT_MIN and set completion alt
     447          92 :     auto_takeoff.start_m(alt_target_m, alt_target_terrain);
     448             : 
     449             :     // set submode
     450          92 :     set_submode(SubMode::TAKEOFF);
     451             : }
     452             : 
     453             : // auto_wp_start - initialises waypoint controller to implement flying to a particular destination
     454         230 : bool ModeAuto::wp_start(const Location& dest_loc)
     455             : {
     456             :     // init wpnav and set origin if transitioning from takeoff
     457         230 :     if (!wp_nav->is_active()) {
     458          68 :         Vector3p stopping_point_ned_m;
     459          68 :         if (_mode == SubMode::TAKEOFF) {
     460          58 :             Vector3p takeoff_complete_pos_ned_m;
     461          58 :             if (auto_takeoff.get_completion_pos_ned_m(takeoff_complete_pos_ned_m)) {
     462          57 :                 stopping_point_ned_m = takeoff_complete_pos_ned_m;
     463             :             }
     464             :         }
     465          68 :         float des_speed_xy_ms = is_positive(desired_speed_override_ms.xy) ? desired_speed_override_ms.xy : 0;
     466          68 :         wp_nav->wp_and_spline_init_m(des_speed_xy_ms, stopping_point_ned_m);
     467             : 
     468             :         // override speeds up and down if necessary
     469          68 :         if (is_positive(desired_speed_override_ms.up)) {
     470           0 :             wp_nav->set_speed_up_ms(desired_speed_override_ms.up);
     471             :         }
     472          68 :         if (is_positive(desired_speed_override_ms.down)) {
     473           0 :             wp_nav->set_speed_down_ms(desired_speed_override_ms.down);
     474             :         }
     475             :     }
     476             : 
     477         230 :     if (!wp_nav->set_wp_destination_loc(dest_loc)) {
     478           0 :         return false;
     479             :     }
     480             : 
     481             :     // initialise yaw
     482             :     // To-Do: reset the yaw only when the previous navigation command is not a WP.  this would allow removing the special check for ROI
     483         230 :     if (auto_yaw.mode() != AutoYaw::Mode::ROI && !(auto_yaw.mode() == AutoYaw::Mode::FIXED && copter.g.wp_yaw_behavior == Copter::WPYawBehavior::NONE)) {
     484         230 :         auto_yaw.set_mode_to_default(false);
     485             :     }
     486             : 
     487             :     // set submode
     488         230 :     set_submode(SubMode::WP);
     489             : 
     490         230 :     return true;
     491             : }
     492             : 
     493             : // auto_land_start - initialises controller to implement a landing
     494          10 : void ModeAuto::land_start()
     495             : {
     496             :     // set horizontal speed and acceleration limits
     497          10 :     pos_control->NE_set_max_speed_accel_m(wp_nav->get_default_speed_NE_ms(), wp_nav->get_wp_acceleration_mss());
     498          10 :     pos_control->NE_set_correction_speed_accel_m(wp_nav->get_default_speed_NE_ms(), wp_nav->get_wp_acceleration_mss());
     499             : 
     500             :     // initialise the vertical position controller
     501          10 :     if (!pos_control->NE_is_active()) {
     502           0 :         pos_control->NE_init_controller();
     503             :     }
     504             : 
     505             :     // set vertical speed and acceleration limits
     506          10 :     pos_control->D_set_max_speed_accel_m(wp_nav->get_default_speed_down_ms(), wp_nav->get_default_speed_up_ms(), wp_nav->get_accel_D_mss());
     507          10 :     pos_control->D_set_correction_speed_accel_m(wp_nav->get_default_speed_down_ms(), wp_nav->get_default_speed_up_ms(), wp_nav->get_accel_D_mss());
     508             : 
     509             :     // initialise the vertical position controller
     510          10 :     if (!pos_control->D_is_active()) {
     511           0 :         pos_control->D_init_controller();
     512             :     }
     513             : 
     514             :     // initialise yaw
     515          10 :     auto_yaw.set_mode(AutoYaw::Mode::HOLD);
     516             : 
     517             : #if AP_LANDINGGEAR_ENABLED
     518             :     // optionally deploy landing gear
     519          10 :     copter.landinggear.deploy_for_landing();
     520             : #endif
     521             : 
     522             :     // reset flag indicating if pilot has applied roll or pitch inputs during landing
     523          10 :     copter.ap.land_repo_active = false;
     524             : 
     525             :     // this will be set true if prec land is later active
     526          10 :     copter.ap.prec_land_active = false;
     527             : 
     528             :     // set submode
     529          10 :     set_submode(SubMode::LAND);
     530          10 : }
     531             : 
     532             : // circle_movetoedge_start - initialise waypoint controller to move to edge of a circle with it's center at the specified location
     533             : //  we assume the caller has performed all required GPS_ok checks
     534           5 : void ModeAuto::circle_movetoedge_start(const Location &circle_center, float radius_m)
     535             : {
     536             :     // set circle center
     537           5 :     copter.circle_nav->set_center(circle_center);
     538             : 
     539             :     // set circle radius. The radius is read back so circle.radius_m carries AC_Circle's
     540             :     // clamp, and a zero (panorama) radius is stored explicitly because get_radius_m() falls
     541             :     // back to the CIRCLE_RADIUS parameter when the commanded radius is zero
     542           5 :     copter.circle_nav->set_radius_m(radius_m);
     543           5 :     circle.radius_m = is_positive(radius_m) ? copter.circle_nav->get_radius_m() : 0.0f;
     544             : 
     545             :     // Always fly to the exact edge of the circle first: circle_start() derives the S-curve
     546             :     // orbit's radius purely from wp_nav's current destination, with no closed-loop correction
     547             :     // across the orbit, so that destination must land exactly on the circle at zero speed.
     548             :     // If the vehicle is already there this leg is short and completes within a tick or two,
     549             :     // so this costs nothing in the common case.
     550           5 :     Vector3p circle_edge_ned_m;
     551             :     float dist_to_edge_m;
     552           5 :     copter.circle_nav->get_closest_point_on_circle_NED_m(circle_edge_ned_m, dist_to_edge_m);
     553             : 
     554             :     // initialise wpnav to move to edge of circle.  set_center() has already resolved the
     555             :     // command's altitude and frame into the center, and the edge point carries the center's
     556             :     // altitude, so the point goes straight through in NED rather than round-tripping via a
     557             :     // Location and back
     558           5 :     if (!wp_nav->set_wp_destination_NED_m(circle_edge_ned_m, copter.circle_nav->center_is_terrain_alt())) {
     559             :         // failure to set destination can only be because of missing terrain data
     560           0 :         copter.failsafe_terrain_on_event();
     561             :     }
     562             : 
     563             :     // if we are outside the circle, point at the edge, otherwise hold yaw
     564           5 :     const float dist_to_center_m = get_horizontal_distance(pos_control->get_pos_estimate_NED_m().xy().tofloat(), copter.circle_nav->get_center_NED_m().xy().tofloat());
     565             :     // initialise yaw
     566             :     // To-Do: reset the yaw only when the previous navigation command is not a WP.  this would allow removing the special check for ROI
     567           5 :     if (auto_yaw.mode() != AutoYaw::Mode::ROI) {
     568           5 :         if (dist_to_center_m > circle.radius_m && dist_to_center_m > 5.0) {
     569           2 :             auto_yaw.set_mode_to_default(false);
     570             :         } else {
     571             :             // vehicle is within circle so hold yaw to avoid spinning as we move to edge of circle
     572           3 :             auto_yaw.set_mode(AutoYaw::Mode::HOLD);
     573             :         }
     574             :     }
     575             : 
     576             :     // set the submode to move to the edge of the circle
     577           5 :     set_submode(SubMode::CIRCLE_MOVE_TO_EDGE);
     578           5 : }
     579             : 
     580             : // auto_circle_start - begin flying a circular orbit as an S-curve waypoint leg
     581             : //   circle_movetoedge_start has already configured circle_nav with the center, radius and
     582             : //   altitude frame; those are used here only as a parameter store.  CIRCLE_RATE is read only
     583             : //   for the radius-0 panorama spin.
     584           5 : void ModeAuto::circle_start()
     585             : {
     586             :     // radius-0 panorama: hold the current wp_nav destination (the circle center, already
     587             :     // reached by the move-to-edge leg) and spin yaw at the circle rate.  No orbit leg is built,
     588             :     // rather than relying on calculate_circle_track()'s degenerate-arc guard to reject a
     589             :     // zero-radius orbit, so the hold does not depend on the origin landing exactly on the center.
     590           5 :     if (!is_positive(circle.radius_m)) {
     591             :         // a zero radius cannot encode a direction in the mission item (loiter_ccw comes from
     592             :         // param3 < 0), so CIRCLE_RATE supplies both the rate and the spin direction
     593           1 :         const float rate_degs = copter.circle_nav->get_rate_degs();
     594             :         // use the rate set_fixed_yaw_rad() will actually fly, which is the yaw slew limit for a
     595             :         // zero rate and is clamped to that limit above it.  One expression feeds both the yaw
     596             :         // command and the duration below so the two cannot disagree
     597           1 :         const float slew_rate_max_rads = attitude_control->get_slew_yaw_max_rads();
     598           1 :         const float rate_rads = is_positive(fabsf(rate_degs)) ? MIN(radians(fabsf(rate_degs)), slew_rate_max_rads)
     599           1 :                                                               : slew_rate_max_rads;
     600           1 :         if (auto_yaw.mode() != AutoYaw::Mode::ROI) {
     601             :             // slew the yaw target through the requested turns at the circle rate.  FIXED is an
     602             :             // angle target, so unlike a rate command it cannot under-rotate against the yaw slew
     603             :             // limit, and it needs no teardown when the command ends
     604           1 :             auto_yaw.set_fixed_yaw_rad(fabsf(circle.turns_signed) * M_2PI, rate_rads,
     605           1 :                                        is_negative(rate_degs) ? -1 : 1, true);
     606             :         }
     607             : 
     608             :         // the panorama runs for as long as the commanded turns take at that rate, the same way
     609             :         // AC_Circle's angle counter measured it
     610           1 :         circle.panorama_start_ms = millis();
     611           1 :         circle.panorama_rate_rads = rate_rads;
     612             : 
     613           1 :         GCS_SEND_TEXT(MAV_SEVERITY_INFO, "Mission: circling %.1f turns", (double)fabsf(circle.turns_signed));
     614             : 
     615             :         // set submode to circle
     616           1 :         set_submode(SubMode::CIRCLE);
     617           1 :         return;
     618             :     }
     619             : 
     620             :     // no whole turn has been announced yet
     621           4 :     circle.turns_reported = 0;
     622             : 
     623           4 :     const Vector3p center_ned_m = copter.circle_nav->get_center_NED_m();
     624           4 :     const bool is_terrain_alt = copter.circle_nav->center_is_terrain_alt();
     625             : 
     626             :     // start the S-curve orbit leg (origin is the current wp_nav destination, on the circle edge).
     627             :     // The orbit is flown at the waypoint speed like any other leg, limited by the corner
     628             :     // acceleration through the arc radius
     629           4 :     if (!wp_nav->set_circle_destination_NED_m(center_ned_m.xy().tofloat(), circle.turns_signed, center_ned_m.z, is_terrain_alt)) {
     630             :         // failure to set destination can only be because of missing terrain data
     631           0 :         copter.failsafe_terrain_on_event();
     632           0 :         return;
     633             :     }
     634             : 
     635           4 :     GCS_SEND_TEXT(MAV_SEVERITY_INFO, "Mission: circling %.1f turns", (double)fabsf(circle.turns_signed));
     636             : 
     637           4 :     if (auto_yaw.mode() != AutoYaw::Mode::ROI) {
     638           4 :         auto_yaw.set_mode(AutoYaw::Mode::CIRCLE);
     639             :     }
     640             : 
     641             :     // set submode to circle
     642           4 :     set_submode(SubMode::CIRCLE);
     643             : }
     644             : 
     645             : #if AC_NAV_GUIDED
     646             : // auto_nav_guided_start - hand over control to external navigation controller in AUTO mode
     647           0 : void ModeAuto::nav_guided_start()
     648             : {
     649             :     // call regular guided flight mode initialisation
     650           0 :     if (!copter.mode_guided.init(true)) {
     651             :         // this should never happen because guided mode never fails to init
     652           0 :         INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
     653           0 :         return;
     654             :     }
     655             : 
     656             :     // initialise guided start time and position as reference for limit checking
     657           0 :     copter.mode_guided.limit_init_time_and_pos();
     658             : 
     659             :     // set submode
     660           0 :     set_submode(SubMode::NAVGUIDED);
     661             : }
     662             : #endif //AC_NAV_GUIDED
     663             : 
     664     1531481 : bool ModeAuto::is_landing() const
     665             : {
     666     1531481 :     switch(_mode) {
     667      123084 :     case SubMode::LAND:
     668      123084 :         return true;
     669      358464 :     case SubMode::RTL:
     670      358464 :         return copter.mode_rtl.is_landing();
     671     1049933 :     default:
     672     1049933 :         return false;
     673             :     }
     674             :     return false;
     675             : }
     676             : 
     677      586906 : bool ModeAuto::is_taking_off() const
     678             : {
     679      586906 :     return ((_mode == SubMode::TAKEOFF) && !auto_takeoff.complete);
     680             : }
     681             : 
     682             : #if AC_PAYLOAD_PLACE_ENABLED
     683             : // auto_payload_place_start - initialises controller to implement a placing
     684           1 : void PayloadPlace::start_descent()
     685             : {
     686           1 :     auto *pos_control = copter.pos_control;
     687           1 :     auto *wp_nav = copter.wp_nav;
     688             : 
     689             :     // set horizontal speed and acceleration limits
     690           1 :     pos_control->NE_set_max_speed_accel_m(wp_nav->get_default_speed_NE_ms(), wp_nav->get_wp_acceleration_mss());
     691           1 :     pos_control->NE_set_correction_speed_accel_m(wp_nav->get_default_speed_NE_ms(), wp_nav->get_wp_acceleration_mss());
     692             : 
     693             :     // initialise the vertical position controller
     694           1 :     if (!pos_control->NE_is_active()) {
     695           0 :         pos_control->NE_init_controller();
     696             :     }
     697             : 
     698             :     // set vertical speed and acceleration limits
     699           1 :     pos_control->D_set_max_speed_accel_m(wp_nav->get_default_speed_down_ms(), wp_nav->get_default_speed_up_ms(), wp_nav->get_accel_D_mss());
     700           1 :     pos_control->D_set_correction_speed_accel_m(wp_nav->get_default_speed_down_ms(), wp_nav->get_default_speed_up_ms(), wp_nav->get_accel_D_mss());
     701             : 
     702             :     // initialise the vertical position controller
     703           1 :     if (!pos_control->D_is_active()) {
     704           0 :         pos_control->D_init_controller();
     705             :     }
     706             : 
     707             :     // initialise yaw
     708           1 :     copter.flightmode->auto_yaw.set_mode(Mode::AutoYaw::Mode::HOLD);
     709             : 
     710           1 :     state = PayloadPlace::State::Descent_Start;
     711           1 : }
     712             : #endif
     713             : 
     714             : // returns true if pilot's yaw input should be used to adjust vehicle's heading
     715     2936403 : bool ModeAuto::use_pilot_yaw(void) const
     716             : {
     717             :     // use option bit except during land and RTL
     718     2936403 :     switch (_mode) {
     719      134457 :         case SubMode::LAND:
     720      134457 :             return copter.mode_land.use_pilot_yaw();
     721      918671 :         case SubMode::RTL:
     722      918671 :             return copter.mode_rtl.use_pilot_yaw();
     723             : #if AC_NAV_GUIDED
     724           0 :         case SubMode::NAVGUIDED:
     725           0 :             return copter.mode_guided.use_pilot_yaw();
     726             : #endif
     727     1883275 :         default:
     728     1883275 :             return !option_is_enabled(Option::IgnorePilotYaw);
     729             :     }
     730             : }
     731             : 
     732         233 : bool ModeAuto::set_speed_NE_ms(float speed_ne_ms)
     733             : {
     734         233 :     copter.wp_nav->set_speed_NE_ms(speed_ne_ms);
     735         233 :     desired_speed_override_ms.xy = speed_ne_ms;
     736         233 :     return true;
     737             : }
     738             : 
     739           0 : bool ModeAuto::set_speed_up_ms(float speed_up_ms)
     740             : {
     741           0 :     copter.wp_nav->set_speed_up_ms(speed_up_ms);
     742           0 :     desired_speed_override_ms.up = speed_up_ms;
     743           0 :     return true;
     744             : }
     745             : 
     746           0 : bool ModeAuto::set_speed_down_ms(float speed_down_ms)
     747             : {
     748           0 :     copter.wp_nav->set_speed_down_ms(speed_down_ms);
     749           0 :     desired_speed_override_ms.down = speed_down_ms;
     750           0 :     return true;
     751             : }
     752             : 
     753             : // start_command - this function will be called when the ap_mission lib wishes to start a new command
     754         426 : bool ModeAuto::start_command(const AP_Mission::Mission_Command& cmd)
     755             : {
     756         426 :     switch(cmd.id) {
     757             : 
     758             :     ///
     759             :     /// navigation commands
     760             :     ///
     761          92 :     case MAV_CMD_NAV_VTOL_TAKEOFF:
     762             :     case MAV_CMD_NAV_TAKEOFF:                   // 22
     763          92 :         do_takeoff(cmd);
     764          92 :         break;
     765             : 
     766         215 :     case MAV_CMD_NAV_WAYPOINT:                  // 16  Navigate to Waypoint
     767             :     case MAV_CMD_NAV_ARC_WAYPOINT:              // 36 Navigate to waypoint via an arc
     768         215 :         do_nav_wp(cmd);
     769         215 :         break;
     770             : 
     771          11 :     case MAV_CMD_NAV_VTOL_LAND:
     772             :     case MAV_CMD_NAV_LAND:              // 21 LAND to Waypoint
     773          11 :         do_land(cmd);
     774          11 :         break;
     775             : 
     776           0 :     case MAV_CMD_NAV_LOITER_UNLIM:              // 17 Loiter indefinitely
     777           0 :         do_loiter_unlimited(cmd);
     778           0 :         break;
     779             : 
     780           5 :     case MAV_CMD_NAV_LOITER_TURNS:              //18 Loiter N Times
     781           5 :         do_circle(cmd);
     782           5 :         break;
     783             : 
     784           5 :     case MAV_CMD_NAV_LOITER_TIME:              // 19
     785           5 :         do_loiter_time(cmd);
     786           5 :         break;
     787             : 
     788           2 :     case MAV_CMD_NAV_LOITER_TO_ALT:
     789           2 :         do_loiter_to_alt(cmd);
     790           2 :         break;
     791             : 
     792          46 :     case MAV_CMD_NAV_RETURN_TO_LAUNCH:             //20
     793          46 :         do_RTL();
     794          46 :         break;
     795             : 
     796          24 :     case MAV_CMD_NAV_SPLINE_WAYPOINT:           // 82  Navigate to Waypoint using spline
     797          24 :         do_spline_wp(cmd);
     798          24 :         break;
     799             : 
     800             : #if AC_NAV_GUIDED
     801           0 :     case MAV_CMD_NAV_GUIDED_ENABLE:             // 92  accept navigation commands from external nav computer
     802           0 :         do_nav_guided_enable(cmd);
     803           0 :         break;
     804             : #endif
     805             : 
     806           5 :     case MAV_CMD_NAV_DELAY:                    // 93 Delay the next navigation command
     807           5 :         do_nav_delay(cmd);
     808           5 :         break;
     809             : 
     810             : #if AP_MISSION_NAV_PAYLOAD_PLACE_ENABLED && AC_PAYLOAD_PLACE_ENABLED
     811           2 :     case MAV_CMD_NAV_PAYLOAD_PLACE:              // 94 place at Waypoint
     812           2 :         do_payload_place(cmd);
     813           2 :         break;
     814             : #endif
     815             : 
     816             : #if AP_SCRIPTING_ENABLED
     817           0 :     case MAV_CMD_NAV_SCRIPT_TIME:
     818           0 :         do_nav_script_time(cmd);
     819           0 :         break;
     820             : #endif
     821             : 
     822           0 :     case MAV_CMD_NAV_ATTITUDE_TIME:
     823           0 :         do_nav_attitude_time(cmd);
     824           0 :         break;
     825             : 
     826             :     //
     827             :     // conditional commands
     828             :     //
     829           0 :     case MAV_CMD_CONDITION_DELAY:             // 112
     830           0 :         do_wait_delay(cmd);
     831           0 :         break;
     832             : 
     833           0 :     case MAV_CMD_CONDITION_DISTANCE:             // 114
     834           0 :         do_within_distance(cmd);
     835           0 :         break;
     836             : 
     837           4 :     case MAV_CMD_CONDITION_YAW:             // 115
     838           4 :         do_yaw(cmd);
     839           4 :         break;
     840             : 
     841             :     ///
     842             :     /// do commands
     843             :     ///
     844           8 :     case MAV_CMD_DO_CHANGE_SPEED:             // 178
     845           8 :         do_change_speed(cmd);
     846           8 :         break;
     847             : 
     848           0 :     case MAV_CMD_DO_SET_HOME:             // 179
     849           0 :         do_set_home(cmd);
     850           0 :         break;
     851             : 
     852           0 :     case MAV_CMD_DO_SET_ROI_LOCATION:       // 195
     853             :     case MAV_CMD_DO_SET_ROI_NONE:           // 197
     854             :     case MAV_CMD_DO_SET_ROI:                // 201
     855             :         // point the copter and camera at a region of interest (ROI)
     856             :         // ROI_NONE can be handled by the regular ROI handler because lat, lon, alt are always zero
     857           0 :         do_roi(cmd);
     858           0 :         break;
     859             : 
     860             : #if HAL_MOUNT_ENABLED
     861           0 :     case MAV_CMD_DO_MOUNT_CONTROL:          // 205
     862             :         // point the camera to a specified angle
     863           0 :         do_mount_control(cmd);
     864           0 :         break;
     865             : #endif
     866             : 
     867             : #if AC_NAV_GUIDED
     868           0 :     case MAV_CMD_DO_GUIDED_LIMITS:                      // 220  accept guided mode limits
     869           0 :         do_guided_limits(cmd);
     870           0 :         break;
     871             : #endif
     872             : 
     873             : #if AP_WINCH_ENABLED
     874           0 :     case MAV_CMD_DO_WINCH:                             // Mission command to control winch
     875           0 :         do_winch(cmd);
     876           0 :         break;
     877             : #endif
     878             : 
     879           7 :     case MAV_CMD_DO_RETURN_PATH_START:
     880             :     case MAV_CMD_DO_LAND_START:
     881           7 :         break;
     882             : 
     883           0 :     default:
     884             :         // unable to use the command, allow the vehicle to try the next command
     885           0 :         return false;
     886             :     }
     887             : 
     888             :     // always return success
     889         426 :     return true;
     890             : }
     891             : 
     892             : // exit_mission - function that is called once the mission completes
     893          52 : void ModeAuto::exit_mission()
     894             : {
     895             :     // play a tone
     896          52 :     AP_Notify::events.mission_complete = 1;
     897             :     // if we are not on the ground switch to loiter or land
     898          52 :     if (!copter.ap.land_complete) {
     899             :         // try to enter loiter but if that fails land
     900           3 :         if (!loiter_start()) {
     901           0 :             set_mode(Mode::Number::LAND, ModeReason::MISSION_END);
     902             :         }
     903             :     } else {
     904             :         // if we've landed it's safe to disarm
     905          49 :         copter.arming.disarm(AP_Arming::Method::MISSIONEXIT);
     906             :     }
     907          52 : }
     908             : 
     909             : // do_guided - start guided mode
     910           0 : bool ModeAuto::do_guided(const AP_Mission::Mission_Command& cmd)
     911             : {
     912             :     // only process guided waypoint if we are in guided mode
     913           0 :     if (!copter.flightmode->in_guided_mode()) {
     914           0 :         return false;
     915             :     }
     916             : 
     917             :     // switch to handle different commands
     918           0 :     switch (cmd.id) {
     919             : 
     920           0 :         case MAV_CMD_NAV_WAYPOINT:
     921             :         {
     922             :             // set wp_nav's destination
     923           0 :             Location dest(cmd.content.location);
     924           0 :             return copter.mode_guided.set_destination(dest);
     925             :         }
     926             : 
     927           0 :         case MAV_CMD_CONDITION_YAW:
     928           0 :             do_yaw(cmd);
     929           0 :             return true;
     930             : 
     931           0 :         default:
     932             :             // reject unrecognised command
     933           0 :             return false;
     934             :     }
     935             : 
     936             :     return true;
     937             : }
     938             : 
     939       52149 : float ModeAuto::wp_distance_m() const
     940             : {
     941             :     // the circle orbit is flown as an S-curve waypoint leg, so all submodes use wp_nav
     942       52149 :     return wp_nav->get_wp_distance_to_destination_m();
     943             : }
     944             : 
     945       52149 : float ModeAuto::wp_bearing_deg() const
     946             : {
     947       52149 :     return degrees(wp_nav->get_wp_bearing_to_destination_rad());
     948             : }
     949             : 
     950       50107 : bool ModeAuto::get_wp(Location& destination) const
     951             : {
     952       50107 :     switch (_mode) {
     953           0 :     case SubMode::NAVGUIDED:
     954           0 :         return copter.mode_guided.get_wp(destination);
     955       20639 :     case SubMode::WP:
     956             :     case SubMode::CIRCLE_MOVE_TO_EDGE:
     957             :     case SubMode::CIRCLE:
     958             :         // the orbit and the move-to-edge leg are wp_nav legs like WP
     959       20639 :         return wp_nav->get_oa_wp_destination(destination);
     960       17426 :     case SubMode::RTL:
     961       17426 :         return copter.mode_rtl.get_wp(destination);
     962       12042 :     default:
     963       12042 :         return false;
     964             :     }
     965             : }
     966             : 
     967             : /*******************************************************************************
     968             : Verify command Handlers
     969             : 
     970             : Each type of mission element has a "verify" operation. The verify
     971             : operation returns true when the mission element has completed and we
     972             : should move onto the next mission element.
     973             : Return true if we do not recognize the command so that we move on to the next command
     974             : *******************************************************************************/
     975             : 
     976             : // verify_command - callback function called from ap-mission at 10hz or higher when a command is being run
     977             : //      we double check that the flight mode is AUTO to avoid the possibility of ap-mission triggering actions while we're not in AUTO mode
     978     3369496 : bool ModeAuto::verify_command(const AP_Mission::Mission_Command& cmd)
     979             : {
     980     3369496 :     if (copter.flightmode != &copter.mode_auto) {
     981           0 :         return false;
     982             :     }
     983             : 
     984     3369496 :     bool cmd_complete = false;
     985             : 
     986     3369496 :     switch (cmd.id) {
     987             :     //
     988             :     // navigation commands
     989             :     //
     990      681192 :     case MAV_CMD_NAV_VTOL_TAKEOFF:
     991             :     case MAV_CMD_NAV_TAKEOFF:
     992      681192 :         cmd_complete = verify_takeoff();
     993      681192 :         break;
     994             : 
     995     1199774 :     case MAV_CMD_NAV_WAYPOINT:
     996             :     case MAV_CMD_NAV_ARC_WAYPOINT:
     997     1199774 :         cmd_complete = verify_nav_wp(cmd);
     998     1199774 :         break;
     999             : 
    1000      202530 :     case MAV_CMD_NAV_VTOL_LAND:
    1001             :     case MAV_CMD_NAV_LAND:
    1002      202530 :         cmd_complete = verify_land();
    1003      202530 :         break;
    1004             : 
    1005             : #if AP_MISSION_NAV_PAYLOAD_PLACE_ENABLED && AC_PAYLOAD_PLACE_ENABLED
    1006       23788 :     case MAV_CMD_NAV_PAYLOAD_PLACE:
    1007       23788 :         cmd_complete = payload_place.verify();
    1008       23788 :         break;
    1009             : #endif
    1010             : 
    1011           0 :     case MAV_CMD_NAV_LOITER_UNLIM:
    1012           0 :         cmd_complete = verify_loiter_unlimited();
    1013           0 :         break;
    1014             : 
    1015       77227 :     case MAV_CMD_NAV_LOITER_TURNS:
    1016       77227 :         cmd_complete = verify_circle(cmd);
    1017       77227 :         break;
    1018             : 
    1019       14710 :     case MAV_CMD_NAV_LOITER_TIME:
    1020       14710 :         cmd_complete = verify_loiter_time(cmd);
    1021       14710 :         break;
    1022             : 
    1023       14519 :     case MAV_CMD_NAV_LOITER_TO_ALT:
    1024       14519 :         return verify_loiter_to_alt();
    1025             : 
    1026      950728 :     case MAV_CMD_NAV_RETURN_TO_LAUNCH:
    1027      950728 :         cmd_complete = verify_RTL();
    1028      950728 :         break;
    1029             : 
    1030      107716 :     case MAV_CMD_NAV_SPLINE_WAYPOINT:
    1031      107716 :         cmd_complete = verify_spline_wp(cmd);
    1032      107716 :         break;
    1033             : 
    1034             : #if AC_NAV_GUIDED
    1035           0 :     case MAV_CMD_NAV_GUIDED_ENABLE:
    1036           0 :         cmd_complete = verify_nav_guided_enable(cmd);
    1037           0 :         break;
    1038             : #endif
    1039             : 
    1040       87074 :      case MAV_CMD_NAV_DELAY:
    1041       87074 :         cmd_complete = verify_nav_delay(cmd);
    1042       87074 :         break;
    1043             : 
    1044             : #if AP_SCRIPTING_ENABLED
    1045           0 :     case MAV_CMD_NAV_SCRIPT_TIME:
    1046           0 :         cmd_complete = verify_nav_script_time();
    1047           0 :         break;
    1048             : #endif
    1049             : 
    1050           0 :     case MAV_CMD_NAV_ATTITUDE_TIME:
    1051           0 :         cmd_complete = verify_nav_attitude_time(cmd);
    1052           0 :         break;
    1053             : 
    1054             :     ///
    1055             :     /// conditional commands
    1056             :     ///
    1057           0 :     case MAV_CMD_CONDITION_DELAY:
    1058           0 :         cmd_complete = verify_wait_delay();
    1059           0 :         break;
    1060             : 
    1061           0 :     case MAV_CMD_CONDITION_DISTANCE:
    1062           0 :         cmd_complete = verify_within_distance();
    1063           0 :         break;
    1064             : 
    1065       10225 :     case MAV_CMD_CONDITION_YAW:
    1066       10225 :         cmd_complete = verify_yaw();
    1067       10225 :         break;
    1068             : 
    1069             :     // do commands (always return true)
    1070          13 :     case MAV_CMD_DO_CHANGE_SPEED:
    1071             :     case MAV_CMD_DO_SET_HOME:
    1072             :     case MAV_CMD_DO_SET_ROI_LOCATION:
    1073             :     case MAV_CMD_DO_SET_ROI_NONE:
    1074             :     case MAV_CMD_DO_SET_ROI:
    1075             : #if HAL_MOUNT_ENABLED
    1076             :     case MAV_CMD_DO_MOUNT_CONTROL:
    1077             : #endif
    1078             : #if AC_NAV_GUIDED
    1079             :     case MAV_CMD_DO_GUIDED_LIMITS:
    1080             : #endif
    1081             : #if AP_FENCE_ENABLED
    1082             :     case MAV_CMD_DO_FENCE_ENABLE:
    1083             : #endif
    1084             : #if AP_WINCH_ENABLED
    1085             :     case MAV_CMD_DO_WINCH:
    1086             : #endif
    1087             :     case MAV_CMD_DO_RETURN_PATH_START:
    1088             :     case MAV_CMD_DO_LAND_START:
    1089          13 :         cmd_complete = true;
    1090          13 :         break;
    1091             : 
    1092           0 :     default:
    1093             :         // error message
    1094           0 :         gcs().send_text(MAV_SEVERITY_WARNING,"Skipping invalid cmd #%i",cmd.id);
    1095             :         // return true if we do not recognize the command so that we move on to the next command
    1096           0 :         cmd_complete = true;
    1097           0 :         break;
    1098             :     }
    1099             : 
    1100             : 
    1101             :     // send message to GCS
    1102     3354977 :     if (cmd_complete) {
    1103         348 :         gcs().send_mission_item_reached_message(cmd.index);
    1104             :     }
    1105             : 
    1106     3354977 :     return cmd_complete;
    1107             : }
    1108             : 
    1109             : // takeoff_run - takeoff in auto mode
    1110             : //      called by auto_run at 100hz or more
    1111      685267 : void ModeAuto::takeoff_run()
    1112             : {
    1113             :     // if the user doesn't want to raise the throttle we can set it automatically
    1114             :     // note that this can defeat the disarm check on takeoff
    1115      685267 :     if (option_is_enabled(Option::AllowTakeOffWithoutRaisingThrottle)) {
    1116      548278 :         copter.set_auto_armed(true);
    1117             :     }
    1118      685267 :     auto_takeoff.run();
    1119      685267 : }
    1120             : 
    1121             : // auto_wp_run - runs the auto waypoint controller
    1122             : //      called by auto_run at 100hz or more
    1123     1508159 : void ModeAuto::wp_run()
    1124             : {
    1125             :     // if not armed set throttle to zero and exit immediately
    1126     1508159 :     if (is_disarmed_or_landed()) {
    1127       17764 :         make_safe_ground_handling();
    1128       17764 :         return;
    1129             :     }
    1130             : 
    1131             :     // set motors to full range
    1132     1490395 :     motors->set_desired_spool_state(AP_Motors::DesiredSpoolState::THROTTLE_UNLIMITED);
    1133             : 
    1134             :     // run waypoint controller
    1135     1490395 :     copter.failsafe_terrain_set_status(wp_nav->update_wpnav());
    1136             : 
    1137             :     // WP_Nav has set the vertical position control targets
    1138             :     // run the vertical position controller and set output throttle
    1139     1490395 :     pos_control->D_update_controller();
    1140             : 
    1141             :     // call attitude controller with auto yaw
    1142     1490395 :     attitude_control->input_thrust_vector_heading(pos_control->get_thrust_vector(), auto_yaw.get_heading());
    1143             : }
    1144             : 
    1145             : // auto_land_run - lands in auto mode
    1146             : //      called by auto_run at 100hz or more
    1147      151476 : void ModeAuto::land_run()
    1148             : {
    1149             : 
    1150             :     // if not armed set throttle to zero and exit immediately
    1151      151476 :     if (is_disarmed_or_landed()) {
    1152       17019 :         make_safe_ground_handling();
    1153       17019 :         return;
    1154             :     }
    1155             : 
    1156             :     // set motors to full range
    1157      134457 :     motors->set_desired_spool_state(AP_Motors::DesiredSpoolState::THROTTLE_UNLIMITED);
    1158             : 
    1159             :     // run normal landing or precision landing (if enabled)
    1160      134457 :     land_run_normal_or_precland();
    1161             : }
    1162             : 
    1163             : // auto_rtl_run - rtl in AUTO flight mode
    1164             : //      called by auto_run at 100hz or more
    1165     1014590 : void ModeAuto::rtl_run()
    1166             : {
    1167             :     // call regular rtl flight mode run function
    1168     1014590 :     copter.mode_rtl.run(false);
    1169     1014590 : }
    1170             : 
    1171             : #if AC_NAV_GUIDED || AP_SCRIPTING_ENABLED
    1172             : // auto_nav_guided_run - allows control by external navigation controller
    1173             : //      called by auto_run at 100hz or more
    1174           0 : void ModeAuto::nav_guided_run()
    1175             : {
    1176             :     // call regular guided flight mode run function
    1177           0 :     copter.mode_guided.run();
    1178           0 : }
    1179             : #endif  // AC_NAV_GUIDED || AP_SCRIPTING_ENABLED
    1180             : 
    1181             : // auto_loiter_run - loiter in AUTO flight mode
    1182             : //      called by auto_run at 100hz or more
    1183       91648 : void ModeAuto::loiter_run()
    1184             : {
    1185             :     // if not armed set throttle to zero and exit immediately
    1186       91648 :     if (is_disarmed_or_landed()) {
    1187       75110 :         make_safe_ground_handling();
    1188       75110 :         return;
    1189             :     }
    1190             : 
    1191             :     // set motors to full range
    1192       16538 :     motors->set_desired_spool_state(AP_Motors::DesiredSpoolState::THROTTLE_UNLIMITED);
    1193             : 
    1194             :     // run waypoint and z-axis position controller
    1195       16538 :     copter.failsafe_terrain_set_status(wp_nav->update_wpnav());
    1196             : 
    1197       16538 :     pos_control->D_update_controller();
    1198             : 
    1199             :     // call attitude controller with auto yaw
    1200       16538 :     attitude_control->input_thrust_vector_heading(pos_control->get_thrust_vector(), auto_yaw.get_heading());
    1201             : }
    1202             : 
    1203             : // auto_loiter_run - loiter to altitude in AUTO flight mode
    1204             : //      called by auto_run at 100hz or more
    1205       14519 : void ModeAuto::loiter_to_alt_run()
    1206             : {
    1207             :     // if not auto armed or motor interlock not enabled set throttle to zero and exit immediately
    1208       14519 :     if (is_disarmed_or_landed() || !motors->get_interlock()) {
    1209           0 :         make_safe_ground_handling();
    1210           0 :         return;
    1211             :     }
    1212             : 
    1213             :     // possibly just run the waypoint controller:
    1214       14519 :     if (!loiter_to_alt.reached_destination_xy) {
    1215        3642 :         loiter_to_alt.reached_destination_xy = wp_nav->reached_wp_destination_NE();
    1216        3642 :         if (!loiter_to_alt.reached_destination_xy) {
    1217        3640 :             wp_run();
    1218        3640 :             return;
    1219             :         }
    1220             :     }
    1221             : 
    1222       10879 :     if (!loiter_to_alt.loiter_start_done) {
    1223             :         // set horizontal speed and acceleration limits
    1224           2 :         pos_control->NE_set_max_speed_accel_m(wp_nav->get_default_speed_NE_ms(), wp_nav->get_wp_acceleration_mss());
    1225           2 :         pos_control->NE_set_correction_speed_accel_m(wp_nav->get_default_speed_NE_ms(), wp_nav->get_wp_acceleration_mss());
    1226             : 
    1227           2 :         if (!pos_control->NE_is_active()) {
    1228           0 :             pos_control->NE_init_controller();
    1229             :         }
    1230             : 
    1231           2 :         loiter_to_alt.loiter_start_done = true;
    1232             :     }
    1233       10879 :     const float alt_error_m = copter.current_loc.alt * 0.01 - loiter_to_alt.alt_m;
    1234       10879 :     if (fabsf(alt_error_m) < 0.05) { // random numbers R US
    1235           2 :         loiter_to_alt.reached_alt = true;
    1236       10877 :     } else if (alt_error_m * loiter_to_alt.alt_error_m < 0) {
    1237             :         // we were above and are now below, or vice-versa
    1238           0 :         loiter_to_alt.reached_alt = true;
    1239             :     }
    1240       10879 :     loiter_to_alt.alt_error_m = alt_error_m;
    1241             : 
    1242             :     // loiter...
    1243             : 
    1244       10879 :     land_run_horizontal_control();
    1245             : 
    1246             :     // Compute a vertical velocity demand such that the vehicle
    1247             :     // approaches the desired altitude.
    1248       10879 :     float target_climb_rate_ms = sqrt_controller(
    1249             :         -alt_error_m,
    1250       10879 :         pos_control->D_get_pos_p().kP(),
    1251       10879 :         pos_control->D_get_max_accel_mss(),
    1252       10879 :         G_Dt);
    1253       10879 :     target_climb_rate_ms = constrain_float(target_climb_rate_ms, -pos_control->get_max_speed_down_ms(), pos_control->get_max_speed_up_ms());
    1254             : 
    1255             :     // get avoidance adjusted climb rate
    1256       10879 :     target_climb_rate_ms = get_avoidance_adjusted_climbrate_ms(target_climb_rate_ms);
    1257             : 
    1258             : #if AP_RANGEFINDER_ENABLED
    1259             :     // update the vertical offset based on the surface measurement
    1260       10879 :     copter.surface_tracking.update_surface_offset();
    1261             : #endif
    1262             : 
    1263             :     // Send the commanded climb rate to the position controller
    1264       10879 :     pos_control->D_set_pos_target_from_climb_rate_ms(target_climb_rate_ms);
    1265             : 
    1266       10879 :     pos_control->D_update_controller();
    1267             : }
    1268             : 
    1269             : // maintain an attitude for a specified time
    1270           0 : void ModeAuto::nav_attitude_time_run()
    1271             : {
    1272             :     // if not auto armed or motor interlock not enabled set throttle to zero and exit immediately
    1273           0 :     if (is_disarmed_or_landed() || !motors->get_interlock()) {
    1274           0 :         make_safe_ground_handling();
    1275           0 :         return;
    1276             :     }
    1277             : 
    1278             :     // constrain climb rate
    1279           0 :     float target_climb_rate_ms = constrain_float(nav_attitude_time.climb_rate_ms, -pos_control->get_max_speed_down_ms(), pos_control->get_max_speed_up_ms());
    1280             : 
    1281             :     // get avoidance adjusted climb rate
    1282           0 :     target_climb_rate_ms = get_avoidance_adjusted_climbrate_ms(target_climb_rate_ms);
    1283             : 
    1284             :     // limit and scale lean angles
    1285             :     // todo: change euler magnitiude limit to lean angle limit
    1286           0 :     const float angle_limit_rad = MAX(radians(10.0f), MIN(attitude_control->lean_angle_max_rad(), attitude_control->get_althold_lean_angle_max_rad()));
    1287           0 :     Vector2f target_rp_rad(radians(nav_attitude_time.roll_deg), radians(nav_attitude_time.pitch_deg));
    1288           0 :     target_rp_rad.limit_length(angle_limit_rad);
    1289             : 
    1290             :     // send targets to attitude controller
    1291           0 :     attitude_control->input_euler_angle_roll_pitch_yaw_rad(target_rp_rad.x, target_rp_rad.y, radians(nav_attitude_time.yaw_deg), true);
    1292             : 
    1293             :     // Send the commanded climb rate to the position controller
    1294           0 :     pos_control->D_set_pos_target_from_climb_rate_ms(target_climb_rate_ms);
    1295             : 
    1296           0 :     pos_control->D_update_controller();
    1297             : }
    1298             : 
    1299             : #if AC_PAYLOAD_PLACE_ENABLED
    1300             : // auto_payload_place_run - places an object in auto mode
    1301             : //      called by auto_run at 100hz or more
    1302       23788 : void PayloadPlace::run()
    1303             : {
    1304       23788 :     const char* prefix_str = "PayloadPlace:";
    1305             : 
    1306       23788 :     if (copter.flightmode->is_disarmed_or_landed()) {
    1307           0 :         copter.flightmode->make_safe_ground_handling();
    1308        3440 :         return;
    1309             :     }
    1310             : 
    1311             :     // set motors to full range
    1312       23788 :     copter.motors->set_desired_spool_state(AP_Motors::DesiredSpoolState::THROTTLE_UNLIMITED);
    1313             : 
    1314       23788 :     const uint32_t descent_thrust_cal_duration_ms = 2000; // milliseconds
    1315       23788 :     const uint32_t placed_check_duration_ms = 500; // how long we have to be below a throttle threshold before considering placed
    1316             : 
    1317       23788 :     auto &g2 = copter.g2;
    1318       23788 :     auto *attitude_control = copter.attitude_control;
    1319       23788 :     const auto &pos_control = copter.pos_control;
    1320       23788 :     const auto &wp_nav = copter.wp_nav;
    1321             : 
    1322             :     // Vertical thrust is taken from the attitude controller before angle boost is added
    1323       23788 :     const float thrust_level = attitude_control->get_throttle_in();
    1324       23788 :     const uint32_t now_ms = AP_HAL::millis();
    1325             : 
    1326             :     // relax position target if we might be landed
    1327             :     // if we discover we've landed then immediately release the load:
    1328       23788 :     if (copter.ap.land_complete || copter.ap.land_complete_maybe) {
    1329           0 :         pos_control->NE_soften_for_landing();
    1330           0 :         switch (state) {
    1331           0 :         case State::FlyToLocation:
    1332             :             // this is handled in wp_run()
    1333           0 :             break;
    1334           0 :         case State::Descent_Start:
    1335             :             // do nothing on this loop
    1336           0 :             break;
    1337           0 :         case State::Descent:
    1338           0 :             gcs().send_text(MAV_SEVERITY_INFO, "%s landed", prefix_str);
    1339           0 :             state = State::Release;
    1340           0 :             break;
    1341           0 :         case State::Release:
    1342             :         case State::Releasing:
    1343             :         case State::Delay:
    1344             :         case State::Ascent_Start:
    1345             :         case State::Ascent:
    1346             :         case State::Done:
    1347           0 :             break;
    1348             :         }
    1349             :     }
    1350             : 
    1351             : #if AP_GRIPPER_ENABLED
    1352             :     // if pilot releases load manually:
    1353       23788 :     if (AP::gripper().valid() && AP::gripper().released()) {
    1354        4118 :         switch (state) {
    1355           1 :         case State::FlyToLocation:
    1356             :         case State::Descent_Start:
    1357           1 :             gcs().send_text(MAV_SEVERITY_INFO, "%s Abort: Gripper Open", prefix_str);
    1358             :             // Descent_Start has not run so we must also initialise descent_start_altitude_m
    1359           1 :             descent_start_altitude_m = pos_control->get_pos_desired_U_m();
    1360           1 :             state = State::Done;
    1361           1 :             break;
    1362           0 :         case State::Descent:
    1363           0 :             gcs().send_text(MAV_SEVERITY_INFO, "%s Manual release", prefix_str);
    1364           0 :             state = State::Release;
    1365           0 :             break;
    1366        4117 :         case State::Release:
    1367             :         case State::Releasing:
    1368             :         case State::Delay:
    1369             :         case State::Ascent_Start:
    1370             :         case State::Ascent:
    1371             :         case State::Done:
    1372        4117 :             break;
    1373             :         }
    1374             :     }
    1375             : #endif
    1376             : 
    1377       23788 :     switch (state) {
    1378        3441 :     case State::FlyToLocation:
    1379        3441 :         if (copter.wp_nav->reached_wp_destination()) {
    1380           1 :             start_descent();
    1381             :         }
    1382        3441 :         break;
    1383             : 
    1384           1 :     case State::Descent_Start:
    1385           1 :         descent_established_time_ms = now_ms;
    1386           1 :         descent_start_altitude_m = pos_control->get_pos_desired_U_m();
    1387             :         // limiting the decent rate to the limit set in wp_nav is not necessary but done for safety
    1388           1 :         descent_speed_ms = MIN((is_positive(g2.pldp_descent_speed_ms)) ? g2.pldp_descent_speed_ms : copter.mode_land.get_land_speed_ms(), wp_nav->get_default_speed_down_ms());
    1389           1 :         descent_thrust_level = 1.0;
    1390           1 :         state = State::Descent;
    1391             :         FALLTHROUGH;
    1392             : 
    1393       16014 :     case State::Descent:
    1394             :         // check maximum decent distance
    1395       16014 :         if (!is_zero(descent_max_m) &&
    1396           0 :             descent_start_altitude_m - pos_control->get_pos_desired_U_m() > descent_max_m) {
    1397           0 :             state = State::Ascent_Start;
    1398           0 :             gcs().send_text(MAV_SEVERITY_WARNING, "%s Reached maximum descent", prefix_str);
    1399           0 :             break;
    1400             :         }
    1401             :         // calibrate the decent thrust after aircraft has reached constant decent rate and release if threshold is reached
    1402       16014 :         if (pos_control->get_vel_desired_U_ms() > -0.95 * descent_speed_ms) {
    1403             :             // decent rate has not reached descent_speed_ms
    1404         326 :             descent_established_time_ms = now_ms;
    1405         326 :             break;
    1406       15688 :         } else if (now_ms - descent_established_time_ms < descent_thrust_cal_duration_ms) {
    1407             :             // record minimum thrust for descent_thrust_cal_duration_ms
    1408         799 :             descent_thrust_level = MIN(descent_thrust_level, thrust_level);
    1409         799 :             place_start_time_ms = now_ms;
    1410         799 :             break;
    1411       14889 :         } else if (thrust_level > g2.pldp_thrust_placed_fraction * descent_thrust_level) {
    1412             :             // thrust is above minimum threshold
    1413       14688 :             place_start_time_ms = now_ms;
    1414       14688 :             break;
    1415         201 :         } else if (is_positive(g2.pldp_range_finder_maximum_m)) {
    1416           0 :             if (!copter.rangefinder_state.enabled) {
    1417             :                 // abort payload place because rangefinder is not enabled
    1418           0 :                 state = State::Ascent_Start;
    1419           0 :                 gcs().send_text(MAV_SEVERITY_WARNING, "%s PLDP_RNG_MAX set and rangefinder not enabled", prefix_str);
    1420           0 :                 break;
    1421           0 :             } else if (copter.rangefinder_alt_ok() && (copter.rangefinder_state.glitch_count == 0) && (copter.rangefinder_state.alt_m > g2.pldp_range_finder_maximum_m)) {
    1422             :                 // range finder altitude is above maximum
    1423           0 :                 place_start_time_ms = now_ms;
    1424           0 :                 break;
    1425             :             }
    1426             :         }
    1427             : 
    1428             :         // If we get here:
    1429             :         // 1. we have reached decent velocity
    1430             :         // 2. measured the thrust level required for decent
    1431             :         // 3. detected that our thrust requirements have reduced
    1432             :         // 4. rangefinder range has dropped below minimum if set
    1433             :         // 5. place_start_time_ms has been initialised
    1434             : 
    1435             :         // payload touchdown must be detected for 0.5 seconds
    1436             : 
    1437         201 :         if (now_ms - place_start_time_ms > placed_check_duration_ms) {
    1438           1 :             state = State::Release;
    1439           1 :             gcs().send_text(MAV_SEVERITY_INFO, "%s payload release thrust threshold: %f", prefix_str, static_cast<double>(g2.pldp_thrust_placed_fraction * descent_thrust_level));
    1440             :         }
    1441         201 :         break;
    1442             : 
    1443           1 :     case State::Release:
    1444             :         // Reinitialise vertical position controller to remove discontinuity due to touch down of payload
    1445           1 :         pos_control->D_init_controller_no_descent();
    1446             : #if AP_GRIPPER_ENABLED
    1447           1 :         if (AP::gripper().valid()) {
    1448           1 :             gcs().send_text(MAV_SEVERITY_INFO, "%s Releasing the gripper", prefix_str);
    1449           1 :             AP::gripper().release();
    1450           1 :             state = State::Releasing;
    1451             :         } else {
    1452           0 :             state = State::Delay;
    1453             :         }
    1454             : #else
    1455             :         state = State::Delay;
    1456             : #endif
    1457           1 :         break;
    1458             : 
    1459         215 :     case State::Releasing:
    1460             : #if AP_GRIPPER_ENABLED
    1461         215 :         if (AP::gripper().valid() && !AP::gripper().released()) {
    1462         214 :             break;
    1463             :         }
    1464             : #endif
    1465           1 :         state = State::Delay;
    1466             :         FALLTHROUGH;
    1467             : 
    1468           1 :     case State::Delay:
    1469             :         // If we get here we have finished releasing the gripper
    1470           1 :         if (now_ms - place_start_time_ms < placed_check_duration_ms + g2.pldp_delay_s * 1000.0) {
    1471           0 :             break;
    1472             :         }
    1473             :         FALLTHROUGH;
    1474             : 
    1475             :     case State::Ascent_Start:
    1476           1 :         state = State::Ascent;
    1477             :         FALLTHROUGH;
    1478             : 
    1479        4117 :     case State::Ascent: {
    1480             :         // Ascent complete when we are less than 10% of the stopping
    1481             :         // distance from the target altitude stopping distance from
    1482             :         // vel_threshold_fraction * max velocity
    1483        4117 :         const float vel_threshold_fraction = 0.1;
    1484        4117 :         const float stop_distance_m = 0.5 * sq(vel_threshold_fraction * copter.pos_control->get_max_speed_up_ms()) / copter.pos_control->D_get_max_accel_mss();
    1485        4117 :         bool reached_altitude = pos_control->get_pos_desired_U_m() >= descent_start_altitude_m - stop_distance_m;
    1486        4117 :         if (reached_altitude) {
    1487           1 :             state = State::Done;
    1488             :         }
    1489        4117 :         break;
    1490             :     }
    1491           1 :     case State::Done:
    1492           1 :         break;
    1493           0 :     default:
    1494             :         // this should never happen
    1495           0 :         INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
    1496           0 :         break;
    1497             :     }
    1498             : 
    1499       23788 :     switch (state) {
    1500        3440 :     case State::FlyToLocation:
    1501             :         // this should never happen
    1502        3440 :         return copter.mode_auto.wp_run();
    1503       16014 :     case State::Descent_Start:
    1504             :     case State::Descent:
    1505       16014 :         copter.flightmode->land_run_horizontal_control();
    1506             :         // update altitude target and call position controller
    1507       16014 :         pos_control->D_set_pos_target_from_climb_rate_ms(-descent_speed_ms, true);
    1508       20348 :         break;
    1509         216 :     case State::Release:
    1510             :     case State::Releasing:
    1511             :     case State::Delay:
    1512             :     case State::Ascent_Start:
    1513         216 :         copter.flightmode->land_run_horizontal_control();
    1514             :         // update altitude target and call position controller
    1515         216 :         pos_control->D_set_pos_target_from_climb_rate_ms(0.0);
    1516         216 :         break;
    1517        4118 :     case State::Ascent:
    1518             :     case State::Done:
    1519        4118 :         float vel_d_zero = 0.0;
    1520        4118 :         copter.flightmode->land_run_horizontal_control();
    1521        4118 :         float pos_d_m = -descent_start_altitude_m;
    1522        4118 :         pos_control->input_pos_vel_accel_D_m(pos_d_m, vel_d_zero, 0.0);
    1523        4118 :         break;
    1524             :     }
    1525       20348 :     pos_control->D_update_controller();
    1526             : }
    1527             : #endif
    1528             : 
    1529             : // subtract position controller offsets from target location
    1530             : // should be used when the location will be used as a target for the position controller
    1531         260 : void ModeAuto::subtract_pos_offsets(Location& target_loc) const
    1532             : {
    1533             :     // subtract position controller offsets from target location
    1534         260 :     const Vector3p& pos_ofs_ned_m = pos_control->get_pos_offset_NED_m();
    1535         260 :     target_loc.offset(-pos_ofs_ned_m);
    1536         260 : }
    1537             : 
    1538             : /********************************************************************************/
    1539             : // Nav (Must) commands
    1540             : /********************************************************************************/
    1541             : 
    1542             : // do_takeoff - initiate takeoff navigation command
    1543          92 : void ModeAuto::do_takeoff(const AP_Mission::Mission_Command& cmd)
    1544             : {
    1545             :     // Set wp navigation target to safe altitude above current position
    1546          92 :     takeoff_start(cmd.content.location);
    1547          92 : }
    1548             : 
    1549             : // get the Location portion of a command.  If the command's lat and lon and/or alt are zero the default_loc's lat,lon and/or alt are returned instead
    1550             : // returns false if the location cannot be determined which only happens if the terrain data is unavailable
    1551         605 : bool ModeAuto::get_loc_from_cmd(const AP_Mission::Mission_Command& cmd, const Location& default_loc, Location& loc) const
    1552             : {
    1553         605 :     loc = cmd.content.location;
    1554             : 
    1555             :     // use current lat, lon if zero
    1556         605 :     if (loc.lat == 0 && loc.lng == 0) {
    1557          72 :         loc.lat = default_loc.lat;
    1558          72 :         loc.lng = default_loc.lng;
    1559             :     }
    1560             : 
    1561             :     // use default altitude if not provided in cmd
    1562         605 :     if (loc.alt == 0) {
    1563             :         // set to default_loc's altitude but in command's alt frame
    1564             :         // note that this may use the terrain database
    1565             :         float default_alt_m;
    1566          76 :         if (!default_loc.get_alt_m(loc.get_alt_frame(), default_alt_m)) {
    1567           1 :             return false;
    1568             :         }
    1569          75 :         loc.set_alt_m(default_alt_m, loc.get_alt_frame());
    1570             :     }
    1571             : 
    1572         604 :     return true;
    1573             : }
    1574             : 
    1575             : // do_nav_wp - initiate move to next waypoint
    1576         215 : void ModeAuto::do_nav_wp(const AP_Mission::Mission_Command& cmd)
    1577             : {
    1578             :     // calculate default location used when lat, lon or alt is zero
    1579         215 :     Location default_loc = copter.current_loc;
    1580             : 
    1581             :     // subtract position offsets
    1582         215 :     subtract_pos_offsets(default_loc);
    1583             : 
    1584         215 :     if (wp_nav->is_active() && wp_nav->reached_wp_destination()) {
    1585         146 :         if (!wp_nav->get_wp_destination_loc(default_loc)) {
    1586             :             // this should never happen
    1587           0 :             INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
    1588             :         }
    1589             :     }
    1590             : 
    1591             :     // get waypoint's location from command and send to wp_nav
    1592         215 :     Location target_loc;
    1593         215 :     if (!get_loc_from_cmd(cmd, default_loc, target_loc) || !wp_start(target_loc)) {
    1594             :         // failure to get the location or set next destination can only be because of missing terrain data or unhealthy rangefinder
    1595           1 :         copter.failsafe_terrain_on_event();
    1596           1 :         return;
    1597             :     }
    1598             : 
    1599             :     // this will be used to remember the time in millis after we reach or pass the WP.
    1600         214 :     loiter_time = 0;
    1601             :     // this is the delay, stored in seconds
    1602         214 :     if (cmd.id == MAV_CMD_NAV_ARC_WAYPOINT) {
    1603          37 :         loiter_time_max = 0;
    1604             :     } else {
    1605         177 :         loiter_time_max = cmd.p1;
    1606             :     }
    1607             : 
    1608             :     // set next destination if necessary
    1609         214 :     if (!set_next_wp(cmd, target_loc)) {
    1610             :         // failure to set next destination can only be because of missing terrain data
    1611           0 :         copter.failsafe_terrain_on_event();
    1612           0 :         return;
    1613             :     }
    1614             : }
    1615             : 
    1616             : // checks the next mission command and adds it as a destination if necessary
    1617             : // supports both straight line and spline waypoints
    1618             : // cmd should be the current command
    1619             : // default_loc should be the destination from the current_cmd but corrected for cases where user set lat, lon or alt to zero
    1620             : // returns true on success, false on failure which should only happen due to a failure to retrieve terrain data
    1621         238 : bool ModeAuto::set_next_wp(const AP_Mission::Mission_Command& current_cmd, const Location &default_loc)
    1622             : {
    1623             :     // Do not add the next WP if the current command includes a delay (p1 > 0).
    1624             :     // Only MAV_CMD_NAV_WAYPOINT and MAV_CMD_NAV_SPLINE_WAYPOINT use p1 as a delay.
    1625         238 :     if (current_cmd.p1 > 0 && (current_cmd.id == MAV_CMD_NAV_WAYPOINT || current_cmd.id == MAV_CMD_NAV_SPLINE_WAYPOINT)) {
    1626          23 :         return true;
    1627             :     }
    1628             : 
    1629             :     // do not add next wp if there are no more navigation commands
    1630         215 :     AP_Mission::Mission_Command next_cmd;
    1631         215 :     if (!mission.get_next_nav_cmd(current_cmd.index+1, next_cmd)) {
    1632          11 :         return true;
    1633             :     }
    1634             : 
    1635             :     // whether vehicle should stop at the target position depends upon the next command
    1636         204 :     switch (next_cmd.id) {
    1637          11 :     case MAV_CMD_NAV_VTOL_LAND:
    1638             :     case MAV_CMD_NAV_LAND:
    1639             :         // ensure landing alt is zero so it is populated from the current altitude
    1640          11 :         next_cmd.content.location.alt = 0;
    1641             :         FALLTHROUGH;
    1642         119 :     case MAV_CMD_NAV_WAYPOINT:
    1643             :     case MAV_CMD_NAV_LOITER_UNLIM:
    1644             : #if AP_MISSION_NAV_PAYLOAD_PLACE_ENABLED
    1645             :     case MAV_CMD_NAV_PAYLOAD_PLACE:
    1646             : #endif
    1647             :     case MAV_CMD_NAV_LOITER_TIME: {
    1648         119 :         Location dest_loc;
    1649         119 :         Location next_dest_loc;
    1650         238 :         if (!get_loc_from_cmd(current_cmd, default_loc, dest_loc) ||
    1651         119 :             !get_loc_from_cmd(next_cmd, dest_loc, next_dest_loc)) {
    1652           0 :             return false;
    1653             :         }
    1654         119 :         return wp_nav->set_wp_destination_next_loc(next_dest_loc);
    1655             :     }
    1656          14 :     case MAV_CMD_NAV_SPLINE_WAYPOINT: {
    1657             :         // get spline's location and next location from command and send to wp_nav
    1658          14 :         Location next_dest_loc, next_next_dest_loc;
    1659             :         bool next_next_dest_loc_is_spline;
    1660          14 :         if (!get_spline_from_cmd(next_cmd, default_loc, next_dest_loc, next_next_dest_loc, next_next_dest_loc_is_spline)) {
    1661           0 :             return false;
    1662             :         }
    1663          14 :         return wp_nav->set_spline_destination_next_loc(next_dest_loc, next_next_dest_loc, next_next_dest_loc_is_spline);
    1664             :     }
    1665          31 :     case MAV_CMD_NAV_ARC_WAYPOINT: {
    1666          31 :         Location dest_loc;
    1667          31 :         Location next_dest_loc;
    1668          62 :         if (!get_loc_from_cmd(current_cmd, default_loc, dest_loc) ||
    1669          31 :             !get_loc_from_cmd(next_cmd, dest_loc, next_dest_loc)) {
    1670           0 :             return false;
    1671             :         }
    1672          31 :         const float arc_angle_rad = next_cmd.get_arc_angle_rad();
    1673          31 :         return wp_nav->set_wp_destination_next_loc(next_dest_loc, arc_angle_rad);
    1674             :     }
    1675          40 :     case MAV_CMD_NAV_LOITER_TURNS:
    1676             :     case MAV_CMD_NAV_RETURN_TO_LAUNCH:
    1677             :     case MAV_CMD_NAV_VTOL_TAKEOFF:
    1678             :     case MAV_CMD_NAV_TAKEOFF:
    1679             :         // always stop for RTL and takeoff commands
    1680             :     default:
    1681             :         // for unsupported commands it is safer to stop
    1682          40 :         break;
    1683             :     }
    1684             : 
    1685          40 :     return true;
    1686             : }
    1687             : 
    1688             : // do_land - initiate landing procedure
    1689          11 : void ModeAuto::do_land(const AP_Mission::Mission_Command& cmd)
    1690             : {
    1691             :     // To-Do: check if we have already landed
    1692             : 
    1693             :     // if location provided we fly to that location at current altitude
    1694          11 :     if (cmd.content.location.lat != 0 || cmd.content.location.lng != 0) {
    1695             :         // set state to fly to location
    1696           7 :         state = State::FlyToLocation;
    1697             : 
    1698             :         // calculate default location used when alt is zero
    1699           7 :         Location default_loc = copter.current_loc;
    1700           7 :         subtract_pos_offsets(default_loc);
    1701             : 
    1702             :         // ensure landing alt is zero so it is populated from the current altitude
    1703           7 :         AP_Mission::Mission_Command cmd_alt_zero = cmd;
    1704           7 :         cmd_alt_zero.content.location.alt = 0;
    1705             : 
    1706             :         // get location from command and send to wp_nav
    1707           7 :         Location target_loc;
    1708           7 :         if (!get_loc_from_cmd(cmd_alt_zero, default_loc, target_loc) || !wp_start(target_loc)) {
    1709             :             // failure to get location or set next destination can only be because of missing terrain data
    1710           0 :             copter.failsafe_terrain_on_event();
    1711           0 :             return;
    1712             :         }
    1713           7 :     } else {
    1714             :         // set landing state
    1715           4 :         state = State::Descending;
    1716             : 
    1717             :         // initialise landing controller
    1718           4 :         land_start();
    1719             :     }
    1720             : }
    1721             : 
    1722             : // do_loiter_unlimited - start loitering with no end conditions
    1723             : // note: caller should set yaw_mode
    1724           7 : bool ModeAuto::do_loiter_unlimited(const AP_Mission::Mission_Command& cmd)
    1725             : {
    1726             :     // calculate default location used when lat, lon or alt is zero
    1727           7 :     Location default_loc = copter.current_loc;
    1728             : 
    1729             :     // subtract position offsets
    1730           7 :     subtract_pos_offsets(default_loc);
    1731             : 
    1732             :     // use previous waypoint destination as default if available
    1733           7 :     if (wp_nav->is_active() && wp_nav->reached_wp_destination()) {
    1734           4 :         if (!wp_nav->get_wp_destination_loc(default_loc)) {
    1735             :             // this should never happen
    1736           0 :             INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
    1737             :         }
    1738             :     }
    1739             : 
    1740             :     // get location from command and send to wp_nav
    1741           7 :     Location target_loc;
    1742           7 :     if (!get_loc_from_cmd(cmd, default_loc, target_loc) || !wp_start(target_loc)) {
    1743             :         // failure to get location or set next destination can only be because of missing terrain data
    1744           0 :         copter.failsafe_terrain_on_event();
    1745           0 :         return false;
    1746             :     }
    1747             : 
    1748           7 :     return true;
    1749             : }
    1750             : 
    1751             : // do_circle - initiate moving in a circle
    1752           5 : void ModeAuto::do_circle(const AP_Mission::Mission_Command& cmd)
    1753             : {
    1754             :     // calculate default location used when lat, lon or alt is zero
    1755           5 :     Location default_loc = copter.current_loc;
    1756             : 
    1757             :     // subtract position offsets
    1758           5 :     subtract_pos_offsets(default_loc);
    1759             : 
    1760           5 :     Location circle_center;
    1761           5 :     if (!get_loc_from_cmd(cmd, default_loc, circle_center)) {
    1762             :         // failure to get location can only be because of missing terrain data
    1763           0 :         copter.failsafe_terrain_on_event();
    1764           0 :         return;
    1765             :     }
    1766             : 
    1767             :     // calculate radius
    1768           5 :     uint16_t radius_m = HIGHBYTE(cmd.p1); // circle radius held in high byte of p1
    1769           5 :     if (cmd.id == MAV_CMD_NAV_LOITER_TURNS &&
    1770           5 :         cmd.type_specific_bits & (1U << 0)) {
    1771             :         // special storage handling allows for larger radii
    1772           0 :         radius_m *= 10;
    1773             :     }
    1774             : 
    1775             :     // true if circle should be ccw
    1776           5 :     const bool circle_direction_ccw = cmd.content.location.loiter_ccw;
    1777             : 
    1778             :     // signed number of turns for the S-curve orbit (sign selects direction, matching arc waypoints)
    1779           5 :     circle.turns_signed = (circle_direction_ccw ? -1.0f : 1.0f) * cmd.get_loiter_turns();
    1780             : 
    1781             :     // move to edge of circle (verify_circle) will ensure we begin circling once we reach the edge
    1782           5 :     circle_movetoedge_start(circle_center, radius_m);
    1783             : }
    1784             : 
    1785             : // do_loiter_time - initiate loitering at a point for a given time period
    1786             : // note: caller should set yaw_mode
    1787           5 : void ModeAuto::do_loiter_time(const AP_Mission::Mission_Command& cmd)
    1788             : {
    1789             :     // re-use loiter unlimited
    1790           5 :     if (!do_loiter_unlimited(cmd)) {
    1791           0 :         return;
    1792             :     }
    1793             : 
    1794             :     // setup loiter timer
    1795           5 :     loiter_time     = 0;
    1796           5 :     loiter_time_max = cmd.p1;     // units are (seconds)
    1797             : }
    1798             : 
    1799             : // do_loiter_alt - initiate loitering at a point until a given altitude is reached
    1800             : // note: caller should set yaw_mode
    1801           2 : void ModeAuto::do_loiter_to_alt(const AP_Mission::Mission_Command& cmd)
    1802             : {
    1803             :     // re-use loiter unlimited
    1804           2 :     if (!do_loiter_unlimited(cmd)) {
    1805           0 :         return;
    1806             :     }
    1807             : 
    1808             :     // if we aren't navigating to a location then we have to adjust
    1809             :     // altitude for current location
    1810           2 :     Location target_loc(cmd.content.location);
    1811           2 :     if (target_loc.lat == 0 && target_loc.lng == 0) {
    1812           1 :         target_loc.lat = copter.current_loc.lat;
    1813           1 :         target_loc.lng = copter.current_loc.lng;
    1814             :     }
    1815             : 
    1816           2 :     if (!target_loc.get_alt_m(Location::AltFrame::ABOVE_HOME, loiter_to_alt.alt_m)) {
    1817           0 :         loiter_to_alt.reached_destination_xy = true;
    1818           0 :         loiter_to_alt.reached_alt = true;
    1819           0 :         gcs().send_text(MAV_SEVERITY_INFO, "bad do_loiter_to_alt");
    1820           0 :         return;
    1821             :     }
    1822           2 :     loiter_to_alt.reached_destination_xy = false;
    1823           2 :     loiter_to_alt.loiter_start_done = false;
    1824           2 :     loiter_to_alt.reached_alt = false;
    1825           2 :     loiter_to_alt.alt_error_m = 0;
    1826             : 
    1827             :     // set vertical speed and acceleration limits
    1828           2 :     pos_control->D_set_max_speed_accel_m(wp_nav->get_default_speed_down_ms(), wp_nav->get_default_speed_up_ms(), wp_nav->get_accel_D_mss());
    1829           2 :     pos_control->D_set_correction_speed_accel_m(wp_nav->get_default_speed_down_ms(), wp_nav->get_default_speed_up_ms(), wp_nav->get_accel_D_mss());
    1830             : 
    1831             :     // set submode
    1832           2 :     set_submode(SubMode::LOITER_TO_ALT);
    1833             : }
    1834             : 
    1835             : // do_spline_wp - initiate move to next waypoint
    1836          24 : void ModeAuto::do_spline_wp(const AP_Mission::Mission_Command& cmd)
    1837             : {
    1838             :     // calculate default location used when lat, lon or alt is zero
    1839          24 :     Location default_loc = copter.current_loc;
    1840             : 
    1841             :     // subtract position offsets
    1842          24 :     subtract_pos_offsets(default_loc);
    1843             : 
    1844          24 :     if (wp_nav->is_active() && wp_nav->reached_wp_destination()) {
    1845          22 :         if (!wp_nav->get_wp_destination_loc(default_loc)) {
    1846             :             // this should never happen
    1847           0 :             INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
    1848             :         }
    1849             :     }
    1850             : 
    1851             :     // get spline's location and next location from command and send to wp_nav
    1852          24 :     Location dest_loc, next_dest_loc;
    1853             :     bool next_dest_loc_is_spline;
    1854          24 :     if (!get_spline_from_cmd(cmd, default_loc, dest_loc, next_dest_loc, next_dest_loc_is_spline)) {
    1855           0 :         copter.failsafe_terrain_on_event();
    1856           0 :         return;
    1857             :     }
    1858          24 :     if (!wp_nav->set_spline_destination_loc(dest_loc, next_dest_loc, next_dest_loc_is_spline)) {
    1859             :         // failure to set destination can only be because of missing terrain data
    1860           0 :         copter.failsafe_terrain_on_event();
    1861           0 :         return;
    1862             :     }
    1863             : 
    1864             :     // this will be used to remember the time in millis after we reach or pass the WP.
    1865          24 :     loiter_time = 0;
    1866             :     // this is the delay, stored in seconds
    1867          24 :     loiter_time_max = cmd.p1;
    1868             : 
    1869             :     // set next destination if necessary
    1870          24 :     if (!set_next_wp(cmd, dest_loc)) {
    1871             :         // failure to set next destination can only be because of missing terrain data
    1872           0 :         copter.failsafe_terrain_on_event();
    1873           0 :         return;
    1874             :     }
    1875             : 
    1876             :     // initialise yaw
    1877             :     // To-Do: reset the yaw only when the previous navigation command is not a WP.  this would allow removing the special check for ROI
    1878          24 :     if (auto_yaw.mode() != AutoYaw::Mode::ROI && !(auto_yaw.mode() == AutoYaw::Mode::FIXED && copter.g.wp_yaw_behavior == Copter::WPYawBehavior::NONE)) {
    1879          24 :         auto_yaw.set_mode_to_default(false);
    1880             :     }
    1881             : 
    1882             :     // set submode
    1883          24 :     set_submode(SubMode::WP);
    1884             : }
    1885             : 
    1886             : // calculate locations required to build a spline curve from a mission command
    1887             : // dest_loc is populated from cmd's location using default_loc in cases where the lat and lon or altitude is zero
    1888             : // next_dest_loc and nest_dest_loc_is_spline is filled in with the following navigation command's location if it exists.  If it does not exist it is set to the dest_loc and false
    1889          38 : bool ModeAuto::get_spline_from_cmd(const AP_Mission::Mission_Command& cmd, const Location& default_loc, Location& dest_loc, Location& next_dest_loc, bool& next_dest_loc_is_spline)
    1890             : {
    1891          38 :     if (!get_loc_from_cmd(cmd, default_loc, dest_loc)) {
    1892           0 :         return false;
    1893             :     }
    1894             : 
    1895             :     // if there is no delay at the end of this segment get next nav command
    1896          38 :     AP_Mission::Mission_Command temp_cmd;
    1897          38 :     if (cmd.p1 == 0 && mission.get_next_nav_cmd(cmd.index+1, temp_cmd)) {
    1898          31 :         if (!get_loc_from_cmd(temp_cmd, dest_loc, next_dest_loc)) {
    1899           0 :             return false;
    1900             :         }
    1901          31 :         next_dest_loc_is_spline = temp_cmd.id == MAV_CMD_NAV_SPLINE_WAYPOINT;
    1902             :     } else {
    1903           7 :         next_dest_loc = dest_loc;
    1904           7 :         next_dest_loc_is_spline = false;
    1905             :     }
    1906             : 
    1907          38 :     return true;
    1908             : }
    1909             : 
    1910             : #if AC_NAV_GUIDED
    1911             : // do_nav_guided_enable - initiate accepting commands from external nav computer
    1912           0 : void ModeAuto::do_nav_guided_enable(const AP_Mission::Mission_Command& cmd)
    1913             : {
    1914           0 :     if (cmd.p1 > 0) {
    1915             :         // start guided within auto
    1916           0 :         nav_guided_start();
    1917             :     }
    1918           0 : }
    1919             : 
    1920             : // do_guided_limits - pass guided limits to guided controller
    1921           0 : void ModeAuto::do_guided_limits(const AP_Mission::Mission_Command& cmd)
    1922             : {
    1923           0 :     copter.mode_guided.limit_set(
    1924           0 :         cmd.p1 * 1000, // convert seconds to ms
    1925           0 :         cmd.content.guided_limits.alt_min,
    1926           0 :         cmd.content.guided_limits.alt_max,
    1927           0 :         cmd.content.guided_limits.horiz_max);
    1928           0 : }
    1929             : #endif  // AC_NAV_GUIDED
    1930             : 
    1931             : // do_nav_delay - Delay the next navigation command
    1932           5 : void ModeAuto::do_nav_delay(const AP_Mission::Mission_Command& cmd)
    1933             : {
    1934           5 :     nav_delay_time_start_ms = millis();
    1935             : 
    1936           5 :     if (cmd.content.nav_delay.seconds > 0) {
    1937             :         // relative delay
    1938           3 :         nav_delay_time_max_ms = cmd.content.nav_delay.seconds * 1000; // convert seconds to milliseconds
    1939             :     } else {
    1940             :         // absolute delay to utc time
    1941             : #if AP_RTC_ENABLED
    1942           2 :         nav_delay_time_max_ms = AP::rtc().get_time_utc(cmd.content.nav_delay.hour_utc, cmd.content.nav_delay.min_utc, cmd.content.nav_delay.sec_utc, 0);
    1943             : #else
    1944             :         nav_delay_time_max_ms = 0;
    1945             : #endif
    1946             :     }
    1947           5 :     gcs().send_text(MAV_SEVERITY_INFO, "Delaying %u sec", (unsigned)(nav_delay_time_max_ms/1000));
    1948           5 : }
    1949             : 
    1950             : #if AP_SCRIPTING_ENABLED
    1951             : // start accepting position, velocity and acceleration targets from lua scripts
    1952           0 : void ModeAuto::do_nav_script_time(const AP_Mission::Mission_Command& cmd)
    1953             : {
    1954             :     // call regular guided flight mode initialisation
    1955           0 :     if (copter.mode_guided.init(true)) {
    1956           0 :         nav_scripting.done = false;
    1957           0 :         nav_scripting.id++;
    1958           0 :         nav_scripting.start_ms = millis();
    1959           0 :         nav_scripting.command = cmd.content.nav_script_time.command;
    1960           0 :         nav_scripting.timeout_s = cmd.content.nav_script_time.timeout_s;
    1961           0 :         nav_scripting.arg1 = cmd.content.nav_script_time.arg1.get();
    1962           0 :         nav_scripting.arg2 = cmd.content.nav_script_time.arg2.get();
    1963           0 :         nav_scripting.arg3 = cmd.content.nav_script_time.arg3;
    1964           0 :         nav_scripting.arg4 = cmd.content.nav_script_time.arg4;
    1965           0 :         set_submode(SubMode::NAV_SCRIPT_TIME);
    1966             :     } else {
    1967             :         // for safety we set nav_scripting to done to protect against the mission getting stuck
    1968           0 :         nav_scripting.done = true;
    1969             :     }
    1970           0 : }
    1971             : #endif
    1972             : 
    1973             : // start maintaining an attitude for a specified time
    1974           0 : void ModeAuto::do_nav_attitude_time(const AP_Mission::Mission_Command& cmd)
    1975             : {
    1976             :     // copy command arguments into local structure
    1977           0 :     nav_attitude_time.roll_deg = cmd.content.nav_attitude_time.roll_deg;
    1978           0 :     nav_attitude_time.pitch_deg = cmd.content.nav_attitude_time.pitch_deg;
    1979           0 :     nav_attitude_time.yaw_deg = cmd.content.nav_attitude_time.yaw_deg;
    1980           0 :     nav_attitude_time.climb_rate_ms = cmd.content.nav_attitude_time.climb_rate;
    1981           0 :     nav_attitude_time.start_ms = AP_HAL::millis();
    1982           0 :     set_submode(SubMode::NAV_ATTITUDE_TIME);
    1983           0 : }
    1984             : 
    1985             : /********************************************************************************/
    1986             : // Condition (May) commands
    1987             : /********************************************************************************/
    1988             : 
    1989           0 : void ModeAuto::do_wait_delay(const AP_Mission::Mission_Command& cmd)
    1990             : {
    1991           0 :     condition_start = millis();
    1992           0 :     condition_value = cmd.content.delay.seconds * 1000;     // convert seconds to milliseconds
    1993           0 : }
    1994             : 
    1995           0 : void ModeAuto::do_within_distance(const AP_Mission::Mission_Command& cmd)
    1996             : {
    1997           0 :     condition_value  = cmd.content.distance.meters;
    1998           0 : }
    1999             : 
    2000           4 : void ModeAuto::do_yaw(const AP_Mission::Mission_Command& cmd)
    2001             : {
    2002           4 :     auto_yaw.set_fixed_yaw_rad(
    2003           4 :         radians(cmd.content.yaw.angle_deg),
    2004           4 :         radians(cmd.content.yaw.turn_rate_dps),
    2005           4 :         cmd.content.yaw.direction,
    2006           4 :         cmd.content.yaw.relative_angle > 0);
    2007           4 : }
    2008             : 
    2009             : /********************************************************************************/
    2010             : // Do (Now) commands
    2011             : /********************************************************************************/
    2012             : 
    2013           8 : void ModeAuto::do_change_speed(const AP_Mission::Mission_Command& cmd)
    2014             : {
    2015           8 :     if (cmd.content.speed.target_ms > 0) {
    2016           8 :         switch (cmd.content.speed.speed_type) {
    2017           0 :         case SPEED_TYPE_CLIMB_SPEED:
    2018           0 :             copter.wp_nav->set_speed_up_ms(cmd.content.speed.target_ms);
    2019           0 :             desired_speed_override_ms.up = cmd.content.speed.target_ms;
    2020           0 :             break;
    2021           0 :         case SPEED_TYPE_DESCENT_SPEED:
    2022           0 :             copter.wp_nav->set_speed_down_ms(cmd.content.speed.target_ms);
    2023           0 :             desired_speed_override_ms.down = cmd.content.speed.target_ms;
    2024           0 :             break;
    2025           8 :         case SPEED_TYPE_AIRSPEED:
    2026             :         case SPEED_TYPE_GROUNDSPEED:
    2027           8 :             copter.wp_nav->set_speed_NE_ms(cmd.content.speed.target_ms);
    2028           8 :             desired_speed_override_ms.xy = cmd.content.speed.target_ms;
    2029           8 :             break;
    2030             :         }
    2031             :     }
    2032           8 : }
    2033             : 
    2034           0 : void ModeAuto::do_set_home(const AP_Mission::Mission_Command& cmd)
    2035             : {
    2036           0 :     if (cmd.p1 == 1 || !cmd.content.location.initialised()) {
    2037           0 :         if (!copter.set_home_to_current_location(false)) {
    2038             :             // ignore failure
    2039             :         }
    2040             :     } else {
    2041           0 :         if (!copter.set_home(cmd.content.location, false)) {
    2042             :             // ignore failure
    2043             :         }
    2044             :     }
    2045           0 : }
    2046             : 
    2047             : // do_roi - starts actions required by MAV_CMD_DO_SET_ROI
    2048             : //          this involves either moving the camera to point at the ROI (region of interest)
    2049             : //          and possibly rotating the copter to point at the ROI if our mount type does not support a yaw feature
    2050             : // TO-DO: add support for other features of MAV_CMD_DO_SET_ROI including pointing at a given waypoint
    2051           0 : void ModeAuto::do_roi(const AP_Mission::Mission_Command& cmd)
    2052             : {
    2053           0 :     auto_yaw.set_roi(cmd.content.location);
    2054           0 : }
    2055             : 
    2056             : #if HAL_MOUNT_ENABLED
    2057             : // point the camera to a specified angle
    2058           0 : void ModeAuto::do_mount_control(const AP_Mission::Mission_Command& cmd)
    2059             : {
    2060             :     // if vehicle has a camera mount but it doesn't do pan control then yaw the entire vehicle instead
    2061           0 :     if ((copter.camera_mount.get_mount_type() != AP_Mount::Type::None) &&
    2062           0 :         !copter.camera_mount.has_pan_control()) {
    2063             :         // Per the handler in AP_Mount, DO_MOUNT_CONTROL yaw angle is in body frame, which is
    2064             :         // equivalent to an offset to the current yaw demand.
    2065           0 :         auto_yaw.set_yaw_angle_offset_deg(cmd.content.mount_control.yaw);
    2066             :     }
    2067             :     // pass the target angles to the camera mount
    2068           0 :     copter.camera_mount.set_angle_target(cmd.content.mount_control.roll, cmd.content.mount_control.pitch, cmd.content.mount_control.yaw, false);
    2069           0 : }
    2070             : #endif  // HAL_MOUNT_ENABLED
    2071             : 
    2072             : #if AP_WINCH_ENABLED
    2073             : // control winch based on mission command
    2074           0 : void ModeAuto::do_winch(const AP_Mission::Mission_Command& cmd)
    2075             : {
    2076             :     // Note: we ignore the gripper num parameter because we only support one gripper
    2077           0 :     switch (cmd.content.winch.action) {
    2078           0 :         case WINCH_RELAXED:
    2079           0 :             g2.winch.relax();
    2080           0 :             break;
    2081           0 :         case WINCH_RELATIVE_LENGTH_CONTROL:
    2082           0 :             g2.winch.release_length(cmd.content.winch.release_length);
    2083           0 :             break;
    2084           0 :         case WINCH_RATE_CONTROL:
    2085           0 :             g2.winch.set_desired_rate(cmd.content.winch.release_rate);
    2086           0 :             break;
    2087           0 :         default:
    2088             :             // do nothing
    2089           0 :             break;
    2090             :     }
    2091           0 : }
    2092             : #endif
    2093             : 
    2094             : #if AP_MISSION_NAV_PAYLOAD_PLACE_ENABLED && AC_PAYLOAD_PLACE_ENABLED
    2095             : // do_payload_place - initiate placing procedure
    2096           2 : void ModeAuto::do_payload_place(const AP_Mission::Mission_Command& cmd)
    2097             : {
    2098             :     // if location provided we fly to that location first
    2099           2 :     if (cmd.content.location.lat != 0 || cmd.content.location.lng != 0 || cmd.content.location.alt != 0) {
    2100             :         // set state to fly to location
    2101           2 :         payload_place.state = PayloadPlace::State::FlyToLocation;
    2102             : 
    2103             :         // calculate default location used when lat, lon or alt is zero
    2104           2 :         Location default_loc = copter.current_loc;
    2105           2 :         subtract_pos_offsets(default_loc);
    2106             : 
    2107             :         // get location from command and send to wp_nav
    2108           2 :         Location target_loc;
    2109           2 :         if (!get_loc_from_cmd(cmd, default_loc, target_loc) || !wp_start(target_loc)) {
    2110             :             // failure to get location or set next destination can only be because of missing terrain data
    2111           0 :             copter.failsafe_terrain_on_event();
    2112           0 :             return;
    2113             :         }
    2114           2 :     } else {
    2115             :         // initialise placing controller
    2116           0 :         payload_place.start_descent();
    2117             :     }
    2118           2 :     payload_place.descent_max_m = cmd.p1 * 0.01;
    2119             : 
    2120             :     // set submode
    2121           2 :     set_submode(SubMode::NAV_PAYLOAD_PLACE);
    2122             : }
    2123             : #endif  // AP_MISSION_NAV_PAYLOAD_PLACE_ENABLED
    2124             : 
    2125             : // do_RTL - start Return-to-Launch
    2126          46 : void ModeAuto::do_RTL(void)
    2127             : {
    2128             :     // start rtl in auto flight mode
    2129          46 :     rtl_start();
    2130          46 : }
    2131             : 
    2132             : /********************************************************************************/
    2133             : // Verify Nav (Must) commands
    2134             : /********************************************************************************/
    2135             : 
    2136             : // verify_takeoff - check if we have completed the takeoff
    2137      681192 : bool ModeAuto::verify_takeoff()
    2138             : {
    2139             : #if AP_LANDINGGEAR_ENABLED
    2140             :     // if we have reached our destination
    2141      681192 :     if (auto_takeoff.complete) {
    2142             :         // retract the landing gear
    2143          78 :         copter.landinggear.retract_after_takeoff();
    2144             :     }
    2145             : #endif
    2146             : 
    2147      681192 :     return auto_takeoff.complete;
    2148             : }
    2149             : 
    2150             : // verify_land - returns true if landing has been completed
    2151      202530 : bool ModeAuto::verify_land()
    2152             : {
    2153      202530 :     bool retval = false;
    2154             : 
    2155      202530 :     switch (state) {
    2156       51064 :         case State::FlyToLocation:
    2157             :             // check if we've reached the location
    2158       51064 :             if (copter.wp_nav->reached_wp_destination()) {
    2159             :                 // initialise landing controller
    2160           6 :                 land_start();
    2161             : 
    2162             :                 // advance to next state
    2163           6 :                 state = State::Descending;
    2164             :             }
    2165       51064 :             break;
    2166             : 
    2167      151466 :         case State::Descending:
    2168             :             // rely on THROTTLE_LAND mode to correctly update landing status
    2169      151466 :             retval = copter.ap.land_complete && (motors->get_spool_state() == AP_Motors::SpoolState::GROUND_IDLE);
    2170      151466 :             if (retval && !mission.continue_after_land_check_for_takeoff() && copter.motors->armed()) {
    2171             :                 /*
    2172             :                   we want to stop mission processing on land
    2173             :                   completion. Disarm now, then return false. This
    2174             :                   leaves mission state machine in the current NAV_LAND
    2175             :                   mission item. After disarming the mission will reset
    2176             :                 */
    2177           9 :                 copter.arming.disarm(AP_Arming::Method::LANDED);
    2178           9 :                 retval = false;
    2179             :             }
    2180      151466 :             break;
    2181             : 
    2182           0 :         default:
    2183             :             // this should never happen
    2184           0 :             INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
    2185           0 :             retval = true;
    2186           0 :             break;
    2187             :     }
    2188             : 
    2189             :     // true is returned if we've successfully landed
    2190      202530 :     return retval;
    2191             : }
    2192             : 
    2193             : #if AC_PAYLOAD_PLACE_ENABLED
    2194             : // verify_payload_place - returns true if placing has been completed
    2195       23788 : bool PayloadPlace::verify()
    2196             : {
    2197       23788 :     switch (state) {
    2198       23786 :     case State::FlyToLocation:
    2199             :     case State::Descent_Start:
    2200             :     case State::Descent:
    2201             :     case State::Release:
    2202             :     case State::Releasing:
    2203             :     case State::Delay:
    2204             :     case State::Ascent_Start:
    2205             :     case State::Ascent:
    2206       23786 :         return false;
    2207           2 :     case State::Done:
    2208           2 :         return true;
    2209             :     }
    2210             :     // should never get here
    2211           0 :     return true;
    2212             : }
    2213             : #endif
    2214             : 
    2215           0 : bool ModeAuto::verify_loiter_unlimited()
    2216             : {
    2217           0 :     return false;
    2218             : }
    2219             : 
    2220             : // verify_loiter_time - check if we have loitered long enough
    2221       14710 : bool ModeAuto::verify_loiter_time(const AP_Mission::Mission_Command& cmd)
    2222             : {
    2223             :     // return immediately if we haven't reached our destination
    2224       14710 :     if (!copter.wp_nav->reached_wp_destination()) {
    2225        2705 :         return false;
    2226             :     }
    2227             : 
    2228             :     // start our loiter timer
    2229       12005 :     if ( loiter_time == 0 ) {
    2230           5 :         loiter_time = millis();
    2231             :     }
    2232             : 
    2233             :     // check if loiter timer has run out
    2234       12005 :     if (((millis() - loiter_time) / 1000) >= loiter_time_max) {
    2235           5 :         gcs().send_text(MAV_SEVERITY_INFO, "Reached command #%i",cmd.index);
    2236           5 :         return true;
    2237             :     }
    2238             : 
    2239       12000 :     return false;
    2240             : }
    2241             : 
    2242             : // verify_loiter_to_alt - check if we have reached both destination
    2243             : // (roughly) and altitude (precisely)
    2244       14519 : bool ModeAuto::verify_loiter_to_alt() const
    2245             : {
    2246       14519 :     if (loiter_to_alt.reached_destination_xy &&
    2247       10879 :         loiter_to_alt.reached_alt) {
    2248           2 :         return true;
    2249             :     }
    2250       14517 :     return false;
    2251             : }
    2252             : 
    2253             : // verify_RTL - return true once RTL has completed successfully
    2254             : // do_RTL should have been called once first to initialise all variables
    2255      950728 : bool ModeAuto::verify_RTL()
    2256             : {
    2257             :     // return immediately if RTL's current state has not completed
    2258      950728 :     if (!copter.mode_rtl.state_complete()) {
    2259      933316 :         return false;
    2260             :     }
    2261             : 
    2262       17412 :     switch (copter.mode_rtl.state()) {
    2263           1 :         case ModeRTL::SubMode::FINAL_DESCENT:
    2264           1 :             return true;
    2265       17273 :         case ModeRTL::SubMode::LAND:
    2266             :             // if landing ensure motors have spooled down
    2267       17273 :             return motors->get_spool_state() == AP_Motors::SpoolState::GROUND_IDLE;
    2268         138 :         default:
    2269         138 :             break;
    2270             :     }
    2271             : 
    2272             :     // RTL has not completed
    2273         138 :     return false;
    2274             : }
    2275             : 
    2276             : /********************************************************************************/
    2277             : // Verify Condition (May) commands
    2278             : /********************************************************************************/
    2279             : 
    2280           0 : bool ModeAuto::verify_wait_delay()
    2281             : {
    2282           0 :     if (millis() - condition_start > (uint32_t)MAX(condition_value,0)) {
    2283           0 :         condition_value = 0;
    2284           0 :         return true;
    2285             :     }
    2286           0 :     return false;
    2287             : }
    2288             : 
    2289           0 : bool ModeAuto::verify_within_distance()
    2290             : {
    2291           0 :     if (wp_distance_m() < MAX(condition_value,0)) {
    2292           0 :         condition_value = 0;
    2293           0 :         return true;
    2294             :     }
    2295           0 :     return false;
    2296             : }
    2297             : 
    2298             : // verify_yaw - return true if we have reached the desired heading
    2299       10225 : bool ModeAuto::verify_yaw()
    2300             : {
    2301             :     // make sure still in fixed yaw mode, the waypoint controller often retakes control of yaw as it executes a new waypoint command
    2302       10225 :     auto_yaw.set_mode(AutoYaw::Mode::FIXED);
    2303             : 
    2304             :     // check if we have reached the target heading
    2305       10225 :     return auto_yaw.reached_fixed_yaw_target();
    2306             : }
    2307             : 
    2308             : // verify_nav_wp - check if we have reached the next way point
    2309     1199774 : bool ModeAuto::verify_nav_wp(const AP_Mission::Mission_Command& cmd)
    2310             : {
    2311             :     // check if we have reached the waypoint
    2312     1199774 :     if ( !copter.wp_nav->reached_wp_destination() ) {
    2313     1183199 :         return false;
    2314             :     }
    2315             : 
    2316             :     // start timer if necessary
    2317       16575 :     if (loiter_time == 0) {
    2318         172 :         loiter_time = millis();
    2319         172 :         if (loiter_time_max > 0) {
    2320             :             // play a tone
    2321          17 :             AP_Notify::events.waypoint_complete = 1;
    2322             :         }
    2323             :     }
    2324             : 
    2325             :     // check if timer has run out
    2326       16575 :     if (((millis() - loiter_time) / 1000) >= loiter_time_max) {
    2327         172 :         if (loiter_time_max == 0) {
    2328             :             // play a tone
    2329         155 :             AP_Notify::events.waypoint_complete = 1;
    2330             :         }
    2331         172 :         gcs().send_text(MAV_SEVERITY_INFO, "Reached command #%i",cmd.index);
    2332         172 :         return true;
    2333             :     }
    2334       16403 :     return false;
    2335             : }
    2336             : 
    2337             : // verify_circle - check if we have circled the point enough
    2338       77227 : bool ModeAuto::verify_circle(const AP_Mission::Mission_Command& cmd)
    2339             : {
    2340             :     // check if we've reached the edge
    2341       77227 :     if (_mode == SubMode::CIRCLE_MOVE_TO_EDGE) {
    2342       16157 :         if (copter.wp_nav->reached_wp_destination()) {
    2343             :             // start circling
    2344           5 :             circle_start();
    2345             :         }
    2346       16157 :         return false;
    2347             :     }
    2348             : 
    2349       61070 :     if (!is_positive(circle.radius_m)) {
    2350             :         // radius-0 panorama: position is held while the yaw target slews through the requested
    2351             :         // turns.  The elapsed angle is counted here rather than read back from the yaw
    2352             :         // controller, so anything that takes the heading away, such as an ROI, pilot yaw or
    2353             :         // weathervaning, changes where the aircraft points without changing how long the
    2354             :         // command runs.  AC_Circle's angle counter behaved the same way
    2355       14400 :         const float angle_swept_rad = circle.panorama_rate_rads * (millis() - circle.panorama_start_ms) * 0.001;
    2356       14400 :         return angle_swept_rad >= fabsf(circle.turns_signed) * M_2PI;
    2357             :     }
    2358             : 
    2359             :     // report each whole turn as it is completed.  The angle comes from the orbit leg itself, so
    2360             :     // it tracks the path actually flown rather than an independently integrated rate
    2361       46670 :     const uint16_t turns_done = copter.wp_nav->get_circle_angle_covered_rad() / M_2PI;
    2362       46670 :     if (turns_done > circle.turns_reported) {
    2363           4 :         circle.turns_reported = turns_done;
    2364           4 :         GCS_SEND_TEXT(MAV_SEVERITY_INFO, "Mission: circled %u/%.1f turns",
    2365             :                       (unsigned)turns_done, (double)fabsf(circle.turns_signed));
    2366             :     }
    2367             : 
    2368             :     // the orbit is a single S-curve leg spanning all requested turns; it is complete
    2369             :     // once that leg finishes
    2370       46670 :     return copter.wp_nav->reached_wp_destination();
    2371             : }
    2372             : 
    2373             : // verify_spline_wp - check if we have reached the next way point using spline
    2374      107716 : bool ModeAuto::verify_spline_wp(const AP_Mission::Mission_Command& cmd)
    2375             : {
    2376             :     // check if we have reached the waypoint
    2377      107716 :     if ( !copter.wp_nav->reached_wp_destination() ) {
    2378      100492 :         return false;
    2379             :     }
    2380             : 
    2381             :     // start timer if necessary
    2382        7224 :     if (loiter_time == 0) {
    2383          23 :         loiter_time = millis();
    2384             :     }
    2385             : 
    2386             :     // check if timer has run out
    2387        7224 :     if (((millis() - loiter_time) / 1000) >= loiter_time_max) {
    2388          23 :         gcs().send_text(MAV_SEVERITY_INFO, "Reached command #%i",cmd.index);
    2389          23 :         return true;
    2390             :     }
    2391        7201 :     return false;
    2392             : }
    2393             : 
    2394             : #if AC_NAV_GUIDED
    2395             : // verify_nav_guided - check if we have breached any limits
    2396           0 : bool ModeAuto::verify_nav_guided_enable(const AP_Mission::Mission_Command& cmd)
    2397             : {
    2398             :     // if disabling guided mode then immediately return true so we move to next command
    2399           0 :     if (cmd.p1 == 0) {
    2400           0 :         return true;
    2401             :     }
    2402             : 
    2403             :     // check time and position limits
    2404           0 :     return copter.mode_guided.limit_check();
    2405             : }
    2406             : #endif  // AC_NAV_GUIDED
    2407             : 
    2408             : // verify_nav_delay - check if we have waited long enough
    2409       87074 : bool ModeAuto::verify_nav_delay(const AP_Mission::Mission_Command& cmd)
    2410             : {
    2411       87074 :     if (millis() - nav_delay_time_start_ms > nav_delay_time_max_ms) {
    2412           4 :         nav_delay_time_max_ms = 0;
    2413           4 :         return true;
    2414             :     }
    2415       87070 :     return false;
    2416             : }
    2417             : 
    2418             : #if AP_SCRIPTING_ENABLED
    2419             : // check if verify_nav_script_time command has completed
    2420           0 : bool ModeAuto::verify_nav_script_time()
    2421             : {
    2422             :     // if done or timeout then return true
    2423           0 :     if (nav_scripting.done ||
    2424           0 :         ((nav_scripting.timeout_s > 0) &&
    2425           0 :          (AP_HAL::millis() - nav_scripting.start_ms) > (nav_scripting.timeout_s * 1000))) {
    2426           0 :         return true;
    2427             :     }
    2428           0 :     return false;
    2429             : }
    2430             : #endif
    2431             : 
    2432             : // check if nav_attitude_time command has completed
    2433           0 : bool ModeAuto::verify_nav_attitude_time(const AP_Mission::Mission_Command& cmd)
    2434             : {
    2435           0 :     return ((AP_HAL::millis() - nav_attitude_time.start_ms) > (cmd.content.nav_attitude_time.time_sec * 1000));
    2436             : }
    2437             : 
    2438             : // pause - Prevent aircraft from progressing along the track
    2439           4 : bool ModeAuto::pause()
    2440             : {
    2441             :     // do not pause unless progressing along a wp_nav leg: the circle orbit and the move-to-edge
    2442             :     // leg are S-curve legs like WP, so they pause the same way
    2443           4 :     const bool wp_leg_active = (_mode == SubMode::WP) || (_mode == SubMode::CIRCLE_MOVE_TO_EDGE) || (_mode == SubMode::CIRCLE);
    2444           4 :     if (!wp_leg_active || wp_nav->reached_wp_destination()) {
    2445           0 :         return false;
    2446             :     }
    2447             : 
    2448           4 :     wp_nav->set_pause();
    2449           4 :     return true;
    2450             : }
    2451             : 
    2452             : // resume - Allow aircraft to progress along the track
    2453           4 : bool ModeAuto::resume()
    2454             : {
    2455           4 :     wp_nav->set_resume();
    2456           4 :     return true;
    2457             : }
    2458             : 
    2459      386004 : bool ModeAuto::paused() const
    2460             : {
    2461      386004 :     return (wp_nav != nullptr) && wp_nav->paused();
    2462             : }
    2463             : 
    2464             : /*
    2465             :   get a height above ground estimate for landing
    2466             :  */
    2467      273575 : float ModeAuto::get_alt_above_ground_m() const
    2468             : {
    2469             :     // Only override if in landing submode
    2470      273575 :     if (_mode == SubMode::LAND) {
    2471             :         // Rangefinder takes priority
    2472             :         float alt_above_ground_m;
    2473      268914 :         if (copter.get_rangefinder_height_interpolated_m(alt_above_ground_m)) {
    2474      195426 :             return alt_above_ground_m;
    2475             :         }
    2476             : 
    2477             :         // Take land altitude from command
    2478      206600 :         const AP_Mission::Mission_Command& cmd = mission.get_current_nav_cmd();
    2479      206600 :         switch (cmd.id) {
    2480      206600 :         case MAV_CMD_NAV_VTOL_LAND:
    2481             :         case MAV_CMD_NAV_LAND: {
    2482      206600 :             if (cmd.content.location.lat != 0 || cmd.content.location.lng != 0) {
    2483             :                 // If land location is valid return height above it
    2484             :                 ftype dist_m;
    2485      133112 :                 if (copter.current_loc.get_height_above(cmd.content.location, dist_m)) {
    2486      133112 :                     return dist_m;
    2487             :                 }
    2488             :             }
    2489       73488 :             break;
    2490             :         }
    2491             : 
    2492           0 :         default:
    2493             :             // Really should not end up here as were in SubMode land
    2494           0 :             break;
    2495             :         }
    2496             :     }
    2497             : 
    2498             :     // Use default method
    2499       78149 :     return Mode::get_alt_above_ground_m();
    2500             : }
    2501             : 
    2502             : #endif

Generated by: LCOV version 1.14