LCOV - code coverage report
Current view: top level - home/pbarker/rc/ardupilot/ArduCopter - mode_rtl.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 193 256 75.4 %
Date: 2026-09-09 11:02:08 Functions: 23 26 88.5 %

          Line data    Source code
       1             : #include "Copter.h"
       2             : 
       3             : #if MODE_RTL_ENABLED
       4             : 
       5             : // table of user settable parameters
       6             : const AP_Param::GroupInfo ModeRTL::var_info[] = {
       7             : 
       8             :     // @Param: ALT_M
       9             :     // @DisplayName: RTL Altitude
      10             :     // @Description: The minimum alt above home the vehicle will climb to before returning. If the vehicle is flying higher than this value it will return at its current altitude.
      11             :     // @Units: m
      12             :     // @Range: 0.30 3000
      13             :     // @Increment: 0.1
      14             :     // @User: Standard
      15             :     AP_GROUPINFO("ALT_M", 1, ModeRTL, altitude_m, RTL_ALT_M_DEFAULT),
      16             : 
      17             :     // @Param: ALT_FINAL_M
      18             :     // @DisplayName: RTL Final Altitude
      19             :     // @Description: Altitude the vehicle will move to as the final stage of Returning to Launch or after completing a mission. Set to zero to land.
      20             :     // @Units: m
      21             :     // @Range: 0 10
      22             :     // @Increment: 0.1
      23             :     // @User: Standard
      24             :     AP_GROUPINFO("ALT_FINAL_M", 2, ModeRTL, alt_final_m, RTL_ALT_FINAL_M_DEFAULT),
      25             : 
      26             :     // @Param: CLIMB_MIN_M
      27             :     // @DisplayName: RTL minimum climb
      28             :     // @Description: The vehicle will climb this many meters during the initial climb portion of the RTL
      29             :     // @Units: m
      30             :     // @Range: 0 30
      31             :     // @Increment: 0.1
      32             :     // @User: Standard
      33             :     AP_GROUPINFO("CLIMB_MIN_M", 3, ModeRTL, climb_min_m, RTL_CLIMB_MIN_M_DEFAULT),
      34             : 
      35             :     // @Param: SPEED_MS
      36             :     // @DisplayName: RTL speed
      37             :     // @Description: The speed in m/s which the aircraft will attempt to maintain horizontally while flying home. If this is set to zero, WP_SPD will be used instead.
      38             :     // @Units: m/s
      39             :     // @Range: 0 20
      40             :     // @Increment: 0.5
      41             :     // @User: Standard
      42             :     AP_GROUPINFO("SPEED_MS", 4, ModeRTL, speed_ms, 0),
      43             : 
      44             :     AP_GROUPEND
      45             : };
      46             : 
      47             : // constructor
      48        2002 : ModeRTL::ModeRTL() : Mode()
      49             : {
      50             :     // load parameter defaults
      51        2002 :     AP_Param::setup_object_defaults(this, var_info);
      52        2002 : }
      53             : 
      54             : // convert parameters
      55        1001 : void ModeRTL::convert_params()
      56             : {
      57             :     // PARAMETER_CONVERSION - Added: Jan-2026 for ArduPilot-4.7
      58             : 
      59             :     // return immediately if parameter conversion has already been performed
      60        1001 :     if (altitude_m.configured() || speed_ms.configured() || alt_final_m.configured() || climb_min_m.configured()) {
      61         120 :         return;
      62             :     }
      63             : 
      64             :     static const AP_Param::ConversionInfo conversion_info[] = {
      65             :         { Parameters::k_param_rtl_altitude_cm, 0, AP_PARAM_INT32, "RTL_ALT_M" },        // RTL_ALT moved to RTL_ALT_M
      66             :         { Parameters::k_param_rtl_speed_cms, 0, AP_PARAM_INT16, "RTL_SPEED_MS" },       // RTL_SPEED moved to RTL_SPEED_MS
      67             :         { Parameters::k_param_rtl_alt_final_cm, 0, AP_PARAM_INT16, "RTL_ALT_FINAL_M" }, // RTL_ALT_FINAL moved to RTL_ALT_FINAL_M
      68             :         { Parameters::k_param_rtl_climb_min_cm, 0, AP_PARAM_INT16, "RTL_CLIMB_MIN_M" }, // RTL_CLIMB_MIN moved to RTL_CLIMB_MIN_M
      69             :     };
      70         881 :     AP_Param::convert_old_parameters_scaled(conversion_info, ARRAY_SIZE(conversion_info), 0.01, 0);
      71             : }
      72             : 
      73             : /*
      74             :  * Init and run calls for RTL flight mode
      75             :  *
      76             :  * There are two parts to RTL, the high level decision making which controls which state we are in
      77             :  * and the lower implementation of the waypoint or landing controllers within those states
      78             :  */
      79             : 
      80             : // rtl_init - initialise rtl controller
      81         315 : bool ModeRTL::init(bool ignore_checks)
      82             : {
      83         315 :     if (!ignore_checks) {
      84         250 :         if (!AP::ahrs().home_is_set()) {
      85           0 :             return false;
      86             :         }
      87             :     }
      88             :     // initialise waypoint and spline controller
      89         315 :     wp_nav->wp_and_spline_init_m(speed_ms.get());
      90         315 :     _state = SubMode::STARTING;
      91         315 :     _state_complete = true; // see run() method below
      92         315 :     terrain_following_allowed = !copter.failsafe.terrain;
      93             :     // reset flag indicating if pilot has applied roll or pitch inputs during landing
      94         315 :     copter.ap.land_repo_active = false;
      95             : 
      96             :     // this will be set true if prec land is later active
      97         315 :     copter.ap.prec_land_active = false;
      98             : 
      99             : #if AC_PRECLAND_ENABLED
     100             :     // initialise precland state machine
     101         315 :     copter.precland_statemachine.init();
     102             : #endif
     103             : 
     104         315 :     return true;
     105             : }
     106             : 
     107             : // re-start RTL with terrain following disabled
     108           0 : void ModeRTL::restart_without_terrain()
     109             : {
     110             : #if HAL_LOGGING_ENABLED
     111           0 :     LOGGER_WRITE_ERROR(LogErrorSubsystem::NAVIGATION, LogErrorCode::RESTARTED_RTL);
     112             : #endif
     113           0 :     terrain_following_allowed = false;
     114           0 :     _state = SubMode::STARTING;
     115           0 :     _state_complete = true;
     116           0 :     gcs().send_text(MAV_SEVERITY_CRITICAL,"Restarting RTL - Terrain data missing");
     117           0 : }
     118             : 
     119      178406 : ModeRTL::RTLAltType ModeRTL::get_alt_type() const
     120             : {
     121             :     // sanity check parameter
     122      178406 :     switch ((ModeRTL::RTLAltType)g.rtl_alt_type) {
     123      178406 :     case RTLAltType::RELATIVE ... RTLAltType::TERRAIN:
     124      178406 :         return g.rtl_alt_type;
     125             :     }
     126             :     // user has an invalid value
     127           0 :     return RTLAltType::RELATIVE;
     128             : }
     129             : 
     130             : // rtl_run - runs the return-to-launch controller
     131             : // should be called at 100hz or more
     132     5813446 : void ModeRTL::run(bool disarm_on_land)
     133             : {
     134     5813446 :     if (!motors->armed()) {
     135      505187 :         return;
     136             :     }
     137             : 
     138             :     // check if we need to move to next state
     139     5308259 :     if (_state_complete) {
     140      113094 :         switch (_state) {
     141         296 :         case SubMode::STARTING:
     142         296 :             build_path();
     143         296 :             climb_start();
     144         296 :             break;
     145         278 :         case SubMode::INITIAL_CLIMB:
     146         278 :             return_start();
     147         278 :             break;
     148         273 :         case SubMode::RETURN_HOME:
     149         273 :             loiterathome_start();
     150         273 :             break;
     151         273 :         case SubMode::LOITER_AT_HOME:
     152         273 :             if (rtl_path.land || copter.failsafe.radio) {
     153         271 :                 land_start();
     154             :             } else {
     155           2 :                 descent_start();
     156             :             }
     157         273 :             break;
     158        8271 :         case SubMode::FINAL_DESCENT:
     159             :             // do nothing
     160        8271 :             break;
     161      103703 :         case SubMode::LAND:
     162             :             // do nothing - rtl_land_run will take care of disarming motors
     163      103703 :             break;
     164             :         }
     165             :     }
     166             : 
     167             :     // call the correct run function
     168     5308259 :     switch (_state) {
     169             : 
     170           0 :     case SubMode::STARTING:
     171             :         // should not be reached:
     172           0 :         _state = SubMode::INITIAL_CLIMB;
     173             :         FALLTHROUGH;
     174             : 
     175     1207034 :     case SubMode::INITIAL_CLIMB:
     176             :     case SubMode::RETURN_HOME:
     177     1207034 :         climb_return_run();
     178     1207034 :         break;
     179             : 
     180      545130 :     case SubMode::LOITER_AT_HOME:
     181      545130 :         loiterathome_run();
     182      545130 :         break;
     183             : 
     184       18452 :     case SubMode::FINAL_DESCENT:
     185       18452 :         descent_run();
     186       18452 :         break;
     187             : 
     188     3537643 :     case SubMode::LAND:
     189     3537643 :         land_run(disarm_on_land);
     190     3537643 :         break;
     191             :     }
     192             : }
     193             : 
     194             : // rtl_climb_start - initialise climb to RTL altitude
     195         296 : void ModeRTL::climb_start()
     196             : {
     197         296 :     _state = SubMode::INITIAL_CLIMB;
     198         296 :     _state_complete = false;
     199             : 
     200             :     // set the destination
     201         296 :     if (!wp_nav->set_wp_destination_loc(rtl_path.climb_target) || !wp_nav->set_wp_destination_next_loc(rtl_path.return_target)) {
     202             :         // this should not happen because rtl_build_path will have checked terrain data was available
     203           0 :         gcs().send_text(MAV_SEVERITY_CRITICAL,"RTL: unexpected error setting climb target");
     204           0 :         LOGGER_WRITE_ERROR(LogErrorSubsystem::NAVIGATION, LogErrorCode::FAILED_TO_SET_DESTINATION);
     205           0 :         copter.set_mode(Mode::Number::LAND, ModeReason::TERRAIN_FAILSAFE);
     206           0 :         return;
     207             :     }
     208             : 
     209             :     // hold current yaw during initial climb
     210         296 :     auto_yaw.set_mode(AutoYaw::Mode::HOLD);
     211             : }
     212             : 
     213             : // rtl_return_start - initialise return to home
     214         278 : void ModeRTL::return_start()
     215             : {
     216         278 :     _state = SubMode::RETURN_HOME;
     217         278 :     _state_complete = false;
     218             : 
     219         278 :     if (!wp_nav->set_wp_destination_loc(rtl_path.return_target)) {
     220             :         // failure must be caused by missing terrain data, restart RTL
     221           0 :         restart_without_terrain();
     222             :     }
     223             : 
     224             :     // initialise yaw to point home (maybe)
     225         278 :     auto_yaw.set_mode_to_default(true);
     226         278 : }
     227             : 
     228             : // rtl_climb_return_run - implements the initial climb, return home and descent portions of RTL which all rely on the wp controller
     229             : //      called by rtl_run at 100hz or more
     230     1207034 : void ModeRTL::climb_return_run()
     231             : {
     232             :     // if not armed set throttle to zero and exit immediately
     233     1207034 :     if (is_disarmed_or_landed()) {
     234           0 :         make_safe_ground_handling();
     235           0 :         return;
     236             :     }
     237             : 
     238             :     // set motors to full range
     239     1207034 :     motors->set_desired_spool_state(AP_Motors::DesiredSpoolState::THROTTLE_UNLIMITED);
     240             : 
     241             :     // run waypoint controller
     242     1207034 :     copter.failsafe_terrain_set_status(wp_nav->update_wpnav());
     243             : 
     244             :     // WP_Nav has set the vertical position control targets
     245             :     // run the vertical position controller and set output throttle
     246     1207034 :     pos_control->D_update_controller();
     247             : 
     248             :     // call attitude controller with auto yaw
     249     1207034 :     attitude_control->input_thrust_vector_heading(pos_control->get_thrust_vector(), auto_yaw.get_heading());
     250             : 
     251             :     // check if we've completed this stage of RTL
     252     1207034 :     _state_complete = wp_nav->reached_wp_destination();
     253             : }
     254             : 
     255             : // loiterathome_start - initialise return to home
     256         273 : void ModeRTL::loiterathome_start()
     257             : {
     258         273 :     _state = SubMode::LOITER_AT_HOME;
     259         273 :     _state_complete = false;
     260         273 :     _loiter_start_time = millis();
     261             : 
     262             :     // yaw back to initial take-off heading yaw unless pilot has already overridden yaw
     263         273 :     if (auto_yaw.default_mode(true) != AutoYaw::Mode::HOLD) {
     264          19 :         auto_yaw.set_mode(AutoYaw::Mode::RESET_TO_ARMED_YAW);
     265             :     } else {
     266         254 :         auto_yaw.set_mode(AutoYaw::Mode::HOLD);
     267             :     }
     268         273 : }
     269             : 
     270             : // rtl_climb_return_descent_run - implements the initial climb, return home and descent portions of RTL which all rely on the wp controller
     271             : //      called by rtl_run at 100hz or more
     272      545130 : void ModeRTL::loiterathome_run()
     273             : {
     274             :     // if not armed set throttle to zero and exit immediately
     275      545130 :     if (is_disarmed_or_landed()) {
     276           0 :         make_safe_ground_handling();
     277           0 :         return;
     278             :     }
     279             : 
     280             :     // set motors to full range
     281      545130 :     motors->set_desired_spool_state(AP_Motors::DesiredSpoolState::THROTTLE_UNLIMITED);
     282             : 
     283             :     // run waypoint controller
     284      545130 :     copter.failsafe_terrain_set_status(wp_nav->update_wpnav());
     285             : 
     286             :     // WP_Nav has set the vertical position control targets
     287             :     // run the vertical position controller and set output throttle
     288      545130 :     pos_control->D_update_controller();
     289             : 
     290             :     // call attitude controller with auto yaw
     291      545130 :     attitude_control->input_thrust_vector_heading(pos_control->get_thrust_vector(), auto_yaw.get_heading());
     292             : 
     293             :     // check if we've completed this stage of RTL
     294      545130 :     if ((millis() - _loiter_start_time) >= (uint32_t)g.rtl_loiter_time.get()) {
     295         273 :         if (auto_yaw.mode() == AutoYaw::Mode::RESET_TO_ARMED_YAW) {
     296             :             // check if heading is within 2 degrees of heading when vehicle was armed
     297             :             // todo: Use the target heading instead of the actual heading to allow landing even if yaw control is lost.
     298          19 :             if (fabsf(wrap_PI(ahrs.get_yaw_rad() - copter.initial_armed_bearing_rad)) <= radians(2.0)) {
     299          19 :                 _state_complete = true;
     300             :             }
     301             :         } else {
     302             :             // we have loitered long enough
     303         254 :             _state_complete = true;
     304             :         }
     305             :     }
     306             : }
     307             : 
     308             : // rtl_descent_start - initialise descent to final alt
     309           2 : void ModeRTL::descent_start()
     310             : {
     311           2 :     _state = SubMode::FINAL_DESCENT;
     312           2 :     _state_complete = false;
     313             : 
     314             :     // initialise altitude target to stopping point
     315           2 :     pos_control->D_init_controller_stopping_point();
     316             : 
     317             :     // initialise yaw
     318           2 :     auto_yaw.set_mode(AutoYaw::Mode::HOLD);
     319             : 
     320             : #if AP_LANDINGGEAR_ENABLED
     321             :     // optionally deploy landing gear
     322           2 :     copter.landinggear.deploy_for_landing();
     323             : #endif
     324           2 : }
     325             : 
     326             : // rtl_descent_run - implements the final descent to the RTL_ALT_M
     327             : //      called by rtl_run at 100hz or more
     328       18452 : void ModeRTL::descent_run()
     329             : {
     330       18452 :     Vector2f vel_correction_ms;
     331             : 
     332             :     // if not armed set throttle to zero and exit immediately
     333       18452 :     if (is_disarmed_or_landed()) {
     334           0 :         make_safe_ground_handling();
     335           0 :         return;
     336             :     }
     337             : 
     338             :     // process pilot's input
     339       18452 :     if (rc().has_valid_input()) {
     340       18452 :         if ((g.throttle_behavior & THR_BEHAVE_HIGH_THROTTLE_CANCELS_LAND) != 0 && copter.rc_throttle_control_in_filter.get() > LAND_CANCEL_TRIGGER_THR){
     341           0 :             LOGGER_WRITE_EVENT(LogEvent::LAND_CANCELLED_BY_PILOT);
     342             :             // exit land if throttle is high
     343           0 :             if (!copter.set_mode(Mode::Number::LOITER, ModeReason::THROTTLE_LAND_ESCAPE)) {
     344             : #if MODE_ALTHOLD_ENABLED
     345           0 :                 copter.set_mode(Mode::Number::ALT_HOLD, ModeReason::THROTTLE_LAND_ESCAPE);
     346             : #endif
     347             :             }
     348             :         }
     349             : 
     350       18452 :         if (g.land_repositioning) {
     351             :             // convert pilot input to reposition velocity
     352       18452 :             vel_correction_ms = get_pilot_desired_velocity(wp_nav->get_wp_acceleration_mss() * 0.5);
     353             : 
     354             :             // record if pilot has overridden roll or pitch
     355       18452 :             if (!vel_correction_ms.is_zero()) {
     356           0 :                 if (!copter.ap.land_repo_active) {
     357           0 :                     LOGGER_WRITE_EVENT(LogEvent::LAND_REPO_ACTIVE);
     358             :                 }
     359           0 :                 copter.ap.land_repo_active = true;
     360             :             }
     361             :         }
     362             :     }
     363             : 
     364             :     // set motors to full range
     365       18452 :     motors->set_desired_spool_state(AP_Motors::DesiredSpoolState::THROTTLE_UNLIMITED);
     366             : 
     367       18452 :     Vector2f accel;
     368       18452 :     pos_control->input_vel_accel_NE_m(vel_correction_ms, accel);
     369       18452 :     pos_control->NE_update_controller();
     370             : 
     371             :     // WP_Nav has set the vertical position control targets
     372             :     // run the vertical position controller and set output throttle
     373       18452 :     pos_control->D_set_alt_target_with_slew_m(rtl_path.descent_target.alt * 0.01);
     374       18452 :     pos_control->D_update_controller();
     375             : 
     376             :     // roll & pitch from waypoint controller, yaw rate from pilot
     377       18452 :     attitude_control->input_thrust_vector_heading(pos_control->get_thrust_vector(), auto_yaw.get_heading());
     378             : 
     379             :     // check if we've reached within 20cm of final altitude
     380       18452 :     _state_complete = fabsf(rtl_path.descent_target.alt * 0.01 - pos_control->get_pos_estimate_U_m()) < 0.2;
     381             : }
     382             : 
     383             : // land_start - initialise controllers to loiter over home
     384         279 : void ModeRTL::land_start()
     385             : {
     386         279 :     _state = SubMode::LAND;
     387         279 :     _state_complete = false;
     388             : 
     389             :     // set horizontal speed and acceleration limits
     390         279 :     pos_control->NE_set_max_speed_accel_m(wp_nav->get_default_speed_NE_ms(), wp_nav->get_wp_acceleration_mss());
     391         279 :     pos_control->NE_set_correction_speed_accel_m(wp_nav->get_default_speed_NE_ms(), wp_nav->get_wp_acceleration_mss());
     392             : 
     393             :     // initialise loiter target destination
     394         279 :     if (!pos_control->NE_is_active()) {
     395           0 :         pos_control->NE_init_controller();
     396             :     }
     397             : 
     398             :     // initialise the vertical position controller
     399         279 :     if (!pos_control->D_is_active()) {
     400           0 :         pos_control->D_init_controller();
     401             :     }
     402             : 
     403             :     // initialise yaw
     404         279 :     auto_yaw.set_mode(AutoYaw::Mode::HOLD);
     405             : 
     406             : #if AP_LANDINGGEAR_ENABLED
     407             :     // optionally deploy landing gear
     408         279 :     copter.landinggear.deploy_for_landing();
     409             : #endif
     410         279 : }
     411             : 
     412     1974602 : bool ModeRTL::is_landing() const
     413             : {
     414     1974602 :     return _state == SubMode::LAND;
     415             : }
     416             : 
     417             : // land_run - run the landing controllers to put the aircraft on the ground
     418             : // called by rtl_run at 100hz or more
     419     3566207 : void ModeRTL::land_run(bool disarm_on_land)
     420             : {
     421             :     // check if we've completed this stage of RTL
     422     3566207 :     _state_complete = copter.ap.land_complete;
     423             : 
     424             :     // disarm when the landing detector says we've landed
     425     3566207 :     if (disarm_on_land && copter.ap.land_complete && motors->get_spool_state() == AP_Motors::SpoolState::GROUND_IDLE) {
     426         229 :         copter.arming.disarm(AP_Arming::Method::LANDED);
     427             :     }
     428             : 
     429             :     // if not armed set throttle to zero and exit immediately
     430     3566207 :     if (is_disarmed_or_landed()) {
     431      113171 :         make_safe_ground_handling();
     432      113171 :         return;
     433             :     }
     434             : 
     435             :     // set motors to full range
     436     3453036 :     motors->set_desired_spool_state(AP_Motors::DesiredSpoolState::THROTTLE_UNLIMITED);
     437             : 
     438             :     // run normal landing or precision landing (if enabled)
     439     3453036 :     land_run_normal_or_precland();
     440             : }
     441             : 
     442         296 : void ModeRTL::build_path()
     443             : {
     444             :     // origin point is our stopping point
     445         296 :     rtl_path.origin_point = get_stopping_point();
     446         296 :     rtl_path.origin_point.change_alt_frame(Location::AltFrame::ABOVE_HOME);
     447             : 
     448             :     // compute return target
     449         296 :     compute_return_target();
     450             : 
     451             :     // climb target is above our origin point at the return altitude
     452         296 :     rtl_path.climb_target = Location(rtl_path.origin_point.lat, rtl_path.origin_point.lng, rtl_path.return_target.alt, rtl_path.return_target.get_alt_frame());
     453             : 
     454             :     // descent target is below return target at rtl_alt_final_m
     455         296 :     rtl_path.descent_target = Location(rtl_path.return_target.lat, rtl_path.return_target.lng, alt_final_m.get() * 100, Location::AltFrame::ABOVE_HOME);
     456             : 
     457             :     // Target altitude is passed directly to the position controller so must be relative to origin
     458         296 :     rtl_path.descent_target.change_alt_frame(Location::AltFrame::ABOVE_ORIGIN);
     459             : 
     460             :     // set land flag
     461         296 :     rtl_path.land = alt_final_m.get() <= 0;
     462         296 : }
     463             : 
     464             : // compute the return target - home or rally point
     465             : //   return target's altitude is updated to a higher altitude that the vehicle can safely return at (frame may also be set)
     466         296 : void ModeRTL::compute_return_target()
     467             : {
     468             :     // set return target to nearest rally point or home position
     469             : #if HAL_RALLY_ENABLED
     470         296 :     rtl_path.return_target = copter.rally.calc_best_rally_or_home_location(copter.current_loc, ahrs.get_home().alt);
     471         296 :     rtl_path.return_target.change_alt_frame(Location::AltFrame::ABSOLUTE);
     472             : #else
     473             :     rtl_path.return_target = ahrs.get_home();
     474             : #endif
     475             : 
     476             :     // get position controller Z-axis offset in cm above EKF origin
     477         296 :     float pos_offset_u_m = pos_control->get_pos_offset_U_m();
     478             : 
     479             :     // curr_alt_m is current altitude, with any offset removed, above home or above terrain depending upon use_terrain
     480         296 :     float curr_alt_m = copter.current_loc.alt * 0.01 - pos_offset_u_m;
     481             : 
     482             :     // determine altitude type of return journey (alt-above-home, alt-above-terrain using range finder or alt-above-terrain using terrain database)
     483         296 :     ReturnTargetAltType alt_type = ReturnTargetAltType::RELATIVE;
     484         296 :     if (terrain_following_allowed && (get_alt_type() == RTLAltType::TERRAIN)) {
     485             :         // convert RTL_ALT_TYPE and WP_RFNG_USE parameters to ReturnTargetAltType
     486           0 :         switch (wp_nav->get_terrain_source()) {
     487           0 :         case AC_WPNav::TerrainSource::TERRAIN_UNAVAILABLE:
     488           0 :             alt_type = ReturnTargetAltType::RELATIVE;
     489           0 :             LOGGER_WRITE_ERROR(LogErrorSubsystem::NAVIGATION, LogErrorCode::RTL_MISSING_RNGFND);
     490           0 :             gcs().send_text(MAV_SEVERITY_CRITICAL, "RTL: no terrain data, using alt-above-home");
     491           0 :             break;
     492           0 :         case AC_WPNav::TerrainSource::TERRAIN_FROM_RANGEFINDER:
     493           0 :             alt_type = ReturnTargetAltType::RANGEFINDER;
     494           0 :             break;
     495           0 :         case AC_WPNav::TerrainSource::TERRAIN_FROM_TERRAINDATABASE:
     496           0 :             alt_type = ReturnTargetAltType::TERRAINDATABASE;
     497           0 :             break;
     498             :         }
     499             :     }
     500             : 
     501             :     // set curr_alt_m and return_target.alt from range finder
     502         296 :     if (alt_type == ReturnTargetAltType::RANGEFINDER) {
     503           0 :         if (copter.get_rangefinder_height_interpolated_m(curr_alt_m)) {
     504             :             // subtract vertical offset from altitude.
     505           0 :             curr_alt_m -= pos_offset_u_m;
     506             :             // set return_target.alt
     507           0 :             rtl_path.return_target.set_alt_m(MAX(curr_alt_m + MAX(0.0f, climb_min_m.get()), MAX(altitude_m.get(), RTL_ALT_MIN_M)), Location::AltFrame::ABOVE_TERRAIN);
     508             :         } else {
     509             :             // fallback to relative alt and warn user
     510           0 :             alt_type = ReturnTargetAltType::RELATIVE;
     511           0 :             gcs().send_text(MAV_SEVERITY_CRITICAL, "RTL: rangefinder unhealthy, using alt-above-home");
     512           0 :             LOGGER_WRITE_ERROR(LogErrorSubsystem::NAVIGATION, LogErrorCode::RTL_MISSING_RNGFND);
     513             :         }
     514             :     }
     515             : 
     516             :     // set curr_alt_m and return_target.alt from terrain database
     517         296 :     if (alt_type == ReturnTargetAltType::TERRAINDATABASE) {
     518             :         // set curr_alt_m to current altitude above terrain
     519             :         // convert return_target.alt from an abs (above MSL) to altitude above terrain
     520             :         //   Note: the return_target may be a rally point with the alt set above the terrain alt (like the top of a building)
     521             :         float curr_terr_alt_m;
     522           0 :         if (copter.current_loc.get_alt_m(Location::AltFrame::ABOVE_TERRAIN, curr_terr_alt_m) &&
     523           0 :             rtl_path.return_target.change_alt_frame(Location::AltFrame::ABOVE_TERRAIN)) {
     524             :             // subtract vertical offset from altitude.
     525           0 :             curr_alt_m = curr_terr_alt_m - pos_offset_u_m;
     526             :         } else {
     527             :             // fallback to relative alt and warn user
     528           0 :             alt_type = ReturnTargetAltType::RELATIVE;
     529           0 :             LOGGER_WRITE_ERROR(LogErrorSubsystem::TERRAIN, LogErrorCode::MISSING_TERRAIN_DATA);
     530           0 :             gcs().send_text(MAV_SEVERITY_CRITICAL, "RTL: no terrain data, using alt-above-home");
     531             :         }
     532             :     }
     533             : 
     534             :     // for the default case we must convert return-target alt (which is an absolute alt) to alt-above-home
     535         296 :     if (alt_type == ReturnTargetAltType::RELATIVE) {
     536         296 :         if (!rtl_path.return_target.change_alt_frame(Location::AltFrame::ABOVE_HOME)) {
     537             :             // this should never happen but just in case
     538           0 :             rtl_path.return_target.set_alt_m(0, Location::AltFrame::ABOVE_HOME);
     539           0 :             gcs().send_text(MAV_SEVERITY_WARNING, "RTL: unexpected error calculating target alt");
     540             :         }
     541             :     }
     542             : 
     543             :     // set new target altitude to return target altitude
     544             :     // Note: this is alt-above-home or terrain-alt depending upon rtl_alt_type
     545             :     // Note: ignore negative altitudes which could happen if user enters negative altitude for rally point or terrain is higher at rally point compared to home
     546         296 :     float target_alt_m = MAX(rtl_path.return_target.alt, 0) * 0.01;
     547             : 
     548             :     // increase target to maximum of current altitude + climb_min and rtl altitude
     549         296 :     const float min_rtl_alt_m = MAX(RTL_ALT_MIN_M, curr_alt_m + MAX(0.0f, climb_min_m.get()));
     550         296 :     target_alt_m = MAX(target_alt_m, MAX(altitude_m.get(), min_rtl_alt_m));
     551             : 
     552             :     // reduce climb if close to return target
     553         296 :     float rtl_return_dist_m = rtl_path.return_target.get_distance(rtl_path.origin_point);
     554             :     // don't allow really shallow slopes
     555         296 :     if (g.rtl_cone_slope >= RTL_MIN_CONE_SLOPE) {
     556         296 :         target_alt_m = MIN(target_alt_m, MAX(rtl_return_dist_m * g.rtl_cone_slope, min_rtl_alt_m));
     557             :     }
     558             : 
     559             :     // set returned target alt to new target_alt_m (don't change altitude type)
     560         296 :     rtl_path.return_target.set_alt_m(target_alt_m, (alt_type == ReturnTargetAltType::RELATIVE) ? Location::AltFrame::ABOVE_HOME : Location::AltFrame::ABOVE_TERRAIN);
     561             : 
     562             : #if AP_FENCE_ENABLED
     563             :     // ensure not above fence altitude if alt fence is enabled
     564             :     // Note: because the rtl_path.climb_target's altitude is simply copied from the return_target's altitude,
     565             :     //       if terrain altitudes are being used, the code below which reduces the return_target's altitude can lead to
     566             :     //       the vehicle not climbing at all as RTL begins.  This can be overly conservative and it might be better
     567             :     //       to apply the fence alt limit independently on the origin_point and return_target
     568         296 :     if ((copter.fence.get_enabled_fences() & AC_FENCE_TYPE_ALT_MAX) != 0) {
     569             :         // get return target in max alt frame so it can be compared to fence's alt
     570          16 :         if (rtl_path.return_target.get_alt_m(copter.fence.get_alt_max_frame(), target_alt_m)) {
     571          16 :             float fence_alt_m = copter.fence.get_safe_alt_max_m();
     572          16 :             if (target_alt_m > fence_alt_m) {
     573             :                 // reduce target alt to the fence alt
     574          13 :                 rtl_path.return_target.alt -= (target_alt_m - fence_alt_m) * 100.0;
     575             :             }
     576             :         }
     577             :     }
     578             : #endif
     579             : 
     580             :     // ensure we do not descend
     581         296 :     rtl_path.return_target.alt = MAX(rtl_path.return_target.alt, curr_alt_m * 100.0);
     582         296 : }
     583             : 
     584       77195 : bool ModeRTL::get_wp(Location& destination) const
     585             : {
     586             :     // provide target in states which use wp_nav
     587       77195 :     switch (_state) {
     588       24933 :     case SubMode::STARTING:
     589             :     case SubMode::INITIAL_CLIMB:
     590             :     case SubMode::RETURN_HOME:
     591             :     case SubMode::LOITER_AT_HOME:
     592             :     case SubMode::FINAL_DESCENT:
     593       24933 :         return wp_nav->get_oa_wp_destination(destination);
     594       52262 :     case SubMode::LAND:
     595       52262 :         return false;
     596             :     }
     597             : 
     598             :     // we should never get here but just in case
     599           0 :     return false;
     600             : }
     601             : 
     602       71706 : float ModeRTL::wp_distance_m() const
     603             : {
     604       71706 :     return wp_nav->get_wp_distance_to_destination_m();
     605             : }
     606             : 
     607       71706 : float ModeRTL::wp_bearing_deg() const
     608             : {
     609       71706 :     return degrees(wp_nav->get_wp_bearing_to_destination_rad());
     610             : }
     611             : 
     612             : // returns true if pilot's yaw input should be used to adjust vehicle's heading
     613     5060639 : bool ModeRTL::use_pilot_yaw(void) const
     614             : {
     615             :     // use land mode setting during descent
     616     5060639 :     if (_state == SubMode::FINAL_DESCENT || _state == SubMode::LAND) {
     617     3348658 :         return copter.mode_land.use_pilot_yaw();
     618             :     }
     619     1711981 :     return !option_is_enabled(Option::IgnorePilotYaw);
     620             : }
     621             : 
     622         397 : bool ModeRTL::set_speed_NE_ms(float speed_ne_ms)
     623             : {
     624         397 :     copter.wp_nav->set_speed_NE_ms(speed_ne_ms);
     625         397 :     return true;
     626             : }
     627             : 
     628           0 : bool ModeRTL::set_speed_up_ms(float speed_up_ms)
     629             : {
     630           0 :     copter.wp_nav->set_speed_up_ms(speed_up_ms);
     631           0 :     return true;
     632             : }
     633             : 
     634           0 : bool ModeRTL::set_speed_down_ms(float speed_down_ms)
     635             : {
     636           0 :     copter.wp_nav->set_speed_down_ms(speed_down_ms);
     637           0 :     return true;
     638             : }
     639             : 
     640     1711981 : bool ModeRTL::option_is_enabled(Option option) const
     641             : {
     642     1711981 :     return ((copter.g2.rtl_options & (uint32_t)option) != 0);
     643             : }
     644             : 
     645             : #endif

Generated by: LCOV version 1.14