LCOV - code coverage report
Current view: top level - home/pbarker/rc/ardupilot/ArduPlane - mode.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 157 174 90.2 %
Date: 2026-09-09 11:02:08 Functions: 14 15 93.3 %

          Line data    Source code
       1             : #include "Plane.h"
       2             : 
       3       17992 : Mode::Mode() :
       4       17992 :     unused_integer{17},
       5             : #if HAL_QUADPLANE_ENABLED
       6       17992 :     pos_control(plane.quadplane.pos_control),
       7       17992 :     attitude_control(plane.quadplane.attitude_control),
       8       17992 :     loiter_nav(plane.quadplane.loiter_nav),
       9       17992 :     quadplane(plane.quadplane),
      10       17992 :     poscontrol(plane.quadplane.poscontrol),
      11             : #endif
      12       17992 :     ahrs(plane.ahrs)
      13             : {
      14       17992 : }
      15             : 
      16        1926 : void Mode::exit()
      17             : {
      18             :     // call sub-classes exit
      19        1926 :     _exit();
      20             :     // stop autotuning if not AUTOTUNE mode
      21        1926 :     if (plane.control_mode != &plane.mode_autotune){
      22        1923 :         plane.autotune_restore();
      23             :     }
      24             : 
      25        1926 : }
      26             : 
      27        1930 : bool Mode::enter()
      28             : {
      29             : #if AP_SCRIPTING_ENABLED
      30             :     // reset nav_scripting.enabled
      31        1930 :     plane.nav_scripting.enabled = false;
      32             : #endif
      33             : 
      34             :     // cancel inverted flight
      35        1930 :     plane.auto_state.inverted_flight = false;
      36             :     
      37             :     // cancel waiting for rudder neutral
      38        1930 :     plane.takeoff_state.waiting_for_rudder_neutral = false;
      39             : 
      40             :     // don't cross-track when starting a mission
      41        1930 :     plane.auto_state.next_wp_crosstrack = false;
      42             : 
      43             :     // reset landing check
      44        1930 :     plane.auto_state.checked_for_autoland = false;
      45             : 
      46             :     // zero locked course
      47        1930 :     plane.steer_state.locked_course_err = 0;
      48        1930 :     plane.steer_state.locked_course = false;
      49             : 
      50             :     // reset crash detection
      51        1930 :     plane.crash_state.is_crashed = false;
      52        1930 :     plane.crash_state.impact_detected = false;
      53             : 
      54             :     // reset external attitude guidance
      55        1930 :     plane.guided_state.last_forced_rpy_ms.zero();
      56        1930 :     plane.guided_state.last_forced_throttle_ms = 0;
      57             : 
      58             : #if AP_PLANE_OFFBOARD_GUIDED_SLEW_ENABLED
      59        1930 :     plane.guided_state.target_heading = -4; // radians here are in range -3.14 to 3.14, so a default value needs to be outside that range
      60        1930 :     plane.guided_state.target_heading_type = GUIDED_HEADING_NONE;
      61        1930 :     plane.guided_state.target_airspeed_cm = -1; // same as above, although an airspeed of -1 is rare on plane.
      62        1930 :     plane.guided_state.target_alt_time_ms = 0;
      63        1930 :     plane.guided_state.target_location.set_alt_cm(-1, Location::AltFrame::ABSOLUTE); 
      64             : #endif
      65             : 
      66             : #if AP_CAMERA_ENABLED
      67        1930 :     plane.camera.set_is_auto_mode(this == &plane.mode_auto);
      68             : #endif
      69             : 
      70             :     // zero initial pitch and highest airspeed on mode change
      71        1930 :     plane.auto_state.highest_airspeed = 0;
      72        1930 :     plane.auto_state.initial_pitch_cd = ahrs.pitch_sensor;
      73             : 
      74             :     // disable taildrag takeoff on mode change
      75        1930 :     plane.auto_state.fbwa_tdrag_takeoff_mode = false;
      76             : 
      77             :     // wipe the takeoff rotation complete state
      78        1930 :     plane.auto_state.rotation_complete = false;
      79             : 
      80             :     // start with previous WP at current location
      81        1930 :     plane.prev_WP_loc = plane.current_loc;
      82             : 
      83             :     // new mode means new loiter
      84        1930 :     plane.loiter.start_time_ms = 0;
      85             : 
      86             :     // record time of mode change
      87        1930 :     plane.last_mode_change_ms = AP_HAL::millis();
      88             : 
      89             :     // set VTOL auto state
      90        1930 :     plane.auto_state.vtol_mode = is_vtol_mode();
      91        1930 :     plane.auto_state.vtol_loiter = false;
      92             : 
      93             :     // initialize speed variable used in AUTO and GUIDED for DO_CHANGE_SPEED commands
      94        1930 :     plane.new_airspeed_cm = -1;
      95             :     
      96             :     // clear postponed long failsafe if mode change (from GCS) occurs before recall of long failsafe
      97        1930 :     plane.long_failsafe_pending = false;
      98             : 
      99             : #if HAL_QUADPLANE_ENABLED
     100        1930 :     quadplane.mode_enter();
     101             : #endif
     102             : 
     103             : #if AP_TERRAIN_AVAILABLE
     104        1930 :     plane.target_altitude.terrain_following_pending = false;
     105             : #endif
     106             : 
     107             : #if AP_PLANE_SYSTEMID_ENABLED
     108        1930 :     plane.g2.systemid.stop();
     109             : #endif
     110             : 
     111             :     // disable auto mode servo idle during altitude wait command
     112        1930 :     plane.auto_state.idle_mode = false;
     113             : 
     114        1930 :     bool enter_result = _enter();
     115             : 
     116        1930 :     if (enter_result) {
     117             :         // -------------------
     118             :         // these must be done AFTER _enter() because they use the results to set more flags
     119             : 
     120             :         // start with throttle suppressed in auto_throttle modes
     121        1929 :         plane.throttle_suppressed = does_auto_throttle();
     122             : #if HAL_ADSB_ENABLED
     123        1929 :         plane.adsb.set_is_auto_mode(does_auto_navigation());
     124             : #endif
     125             : 
     126             :         // set the nav controller stale AFTER _enter() so that we can check if we're currently in a loiter during the mode change
     127        1929 :         plane.nav_controller->set_data_is_stale();
     128             : 
     129             :         // reset steering integrator on mode change
     130        1929 :         plane.steerController.reset_I();
     131             : 
     132             :         // update RC failsafe, as mode change may have necessitated changing the failsafe throttle
     133        1929 :         plane.control_failsafe();
     134             : 
     135             : #if AP_FENCE_ENABLED
     136             :         // pilot requested flight mode change during a fence breach indicates pilot is attempting to manually recover
     137             :         // this flight mode change could be automatic (i.e. fence, battery, GPS or GCS failsafe)
     138             :         // but it should be harmless to disable the fence temporarily in these situations as well
     139        1929 :         plane.fence.manual_recovery_start();
     140             : #endif
     141             :         //reset mission if in landing sequence, disarmed, not flying, and have changed to a non-autothrottle mode to clear prearm
     142        1929 :         if (plane.mission.get_in_landing_sequence_flag() &&
     143        1932 :             !plane.is_flying() && !plane.arming.is_armed_and_safety_off() &&
     144           3 :             !plane.control_mode->does_auto_navigation()) {
     145           3 :            GCS_SEND_TEXT(MAV_SEVERITY_INFO, "In landing sequence: mission reset");
     146           3 :            plane.mission.reset();
     147             :         }
     148             : 
     149             :         // Make sure the flight stage is correct for the new mode
     150        1929 :         plane.update_flight_stage();
     151             :         
     152             :         // reset landing state
     153        1929 :         plane.landing.reset();
     154             : 
     155             : 
     156             : #if HAL_QUADPLANE_ENABLED
     157        1929 :         if (quadplane.enabled()) {
     158             :             float aspeed;
     159         929 :             bool have_airspeed = quadplane.ahrs.airspeed_EAS(aspeed);
     160         929 :             quadplane.assisted_flight = quadplane.assist.should_assist(aspeed, have_airspeed);
     161             :         }
     162             : 
     163        1929 :         if (is_vtol_mode() && !quadplane.tailsitter.enabled()) {
     164             :             // if flying inverted and entering a VTOL mode cancel
     165             :             // inverted flight
     166         143 :             plane.inverted_flight = false;
     167             :         }
     168             : #endif
     169             :     }
     170             : 
     171        1930 :     return enter_result;
     172             : }
     173             : 
     174     3715674 : bool Mode::is_vtol_man_throttle() const
     175             : {
     176             : #if HAL_QUADPLANE_ENABLED
     177     3720387 :     if (plane.quadplane.tailsitter.is_in_fw_flight() &&
     178        4713 :         plane.quadplane.assisted_flight) {
     179             :         // We are a tailsitter that has fully transitioned to Q-assisted forward flight.
     180             :         // In this case the forward throttle directly drives the vertical throttle so
     181             :         // set vertical throttle state to match the forward throttle state. Confusingly the booleans are inverted,
     182             :         // forward throttle uses 'does_auto_throttle' whereas vertical uses 'is_vtol_man_throttle'.
     183        4713 :         return !does_auto_throttle();
     184             :     }
     185             : #endif
     186     3710961 :     return false;
     187             : }
     188             : 
     189      527309 : void Mode::update_target_altitude()
     190             : {
     191      527309 :     Location target_location;
     192             : 
     193      527309 :     if (plane.landing.is_flaring()) {
     194             :         // during a landing flare, use TECS_LAND_SINK as a target sink
     195             :         // rate, and ignores the target altitude
     196       23257 :         plane.set_target_altitude_location(plane.next_WP_loc);
     197      504052 :     } else if (plane.landing.is_on_approach()) {
     198        6253 :         plane.landing.setup_landing_glide_slope(plane.prev_WP_loc, plane.next_WP_loc, plane.current_loc, plane.target_altitude.offset_cm);
     199             : #if AP_RANGEFINDER_ENABLED
     200        6253 :         plane.landing.adjust_landing_slope_for_rangefinder_bump(plane.rangefinder_state, plane.prev_WP_loc, plane.next_WP_loc, plane.current_loc, plane.auto_state.wp_distance, plane.target_altitude.offset_cm);
     201             : #endif
     202      497799 :     } else if (plane.landing.get_target_altitude_location(target_location)) {
     203         739 :         plane.set_target_altitude_location(target_location);
     204             : #if HAL_SOARING_ENABLED
     205      497060 :     } else if (plane.g2.soaring_controller.is_active() && plane.g2.soaring_controller.get_throttle_suppressed()) {
     206             :         // Reset target alt to current alt, to prevent large altitude errors when gliding.
     207        8191 :         plane.set_target_altitude_location(plane.current_loc);
     208        8191 :         plane.reset_offset_altitude();
     209             : #endif
     210      488869 :     } else if (plane.reached_loiter_target()) {
     211             :         // once we reach a loiter target then lock to the final
     212             :         // altitude target
     213      102007 :         plane.set_target_altitude_location(plane.next_WP_loc);
     214             : #if AP_TERRAIN_AVAILABLE
     215      445357 :     } else if (plane.next_WP_loc.terrain_alt &&
     216       58495 :                plane.set_target_altitude_proportion_terrain()) {
     217             :         // special case for target as terrain relative handled inside
     218             :         // set_target_altitude_proportion_terrain
     219             : #endif
     220      405823 :     } else if (plane.target_altitude.offset_cm != 0 &&
     221       77456 :                !plane.current_loc.past_interval_finish_line(plane.prev_WP_loc, plane.next_WP_loc)) {
     222             :         // control climb/descent rate
     223       69082 :         plane.set_target_altitude_proportion(plane.next_WP_loc, 1.0f-plane.auto_state.wp_proportion);
     224             : 
     225             :         // stay within the range of the start and end locations in altitude
     226       69082 :         plane.constrain_target_altitude_location(plane.next_WP_loc, plane.prev_WP_loc);
     227             :     } else {
     228      259285 :         plane.set_target_altitude_location(plane.next_WP_loc);
     229             :     }
     230      527309 : }
     231             : 
     232             : // returns true if the vehicle can be armed in this mode
     233       26163 : bool Mode::pre_arm_checks(size_t buflen, char *buffer) const
     234             : {
     235       26163 :     if (!_pre_arm_checks(buflen, buffer)) {
     236         466 :         if (strlen(buffer) == 0) {
     237             :             // If no message is provided add a generic one
     238         388 :             hal.util->snprintf(buffer, buflen, "mode not armable");
     239             :         }
     240         466 :         return false;
     241             :     }
     242             : 
     243       25697 :     return true;
     244             : }
     245             : 
     246             : // Auto and Guided do not call this to bypass the q-mode check.
     247       23885 : bool Mode::_pre_arm_checks(size_t buflen, char *buffer) const
     248             : {
     249             : #if HAL_QUADPLANE_ENABLED
     250       30569 :     if (plane.quadplane.enabled() && !is_vtol_mode() &&
     251        6684 :             plane.quadplane.option_is_set(QuadPlane::Option::ONLY_ARM_IN_QMODE_OR_AUTO)) {
     252           0 :         hal.util->snprintf(buffer, buflen, "not Q mode");
     253           0 :         return false;
     254             :     }
     255             : #endif
     256       23885 :     return true;
     257             : }
     258             : 
     259     5922671 : void Mode::run()
     260             : {
     261             :     // Direct stick mixing functionality has been removed, so as not to remove all stick mixing from the user completely
     262             :     // the old direct option is now used to enable fbw mixing, this is easier than doing a param conversion.
     263     5922671 :     switch ((StickMixing)plane.g.stick_mixing) {
     264     5922671 :         case StickMixing::FBW:
     265             :         case StickMixing::FBW_NO_PITCH:
     266             :         case StickMixing::DIRECT_REMOVED:
     267     5922671 :             plane.stabilize_stick_mixing_fbw();
     268     5922671 :             break;
     269           0 :         case StickMixing::NONE:
     270             :         case StickMixing::VTOL_YAW:
     271           0 :             break;
     272             :     }
     273     5922671 :     plane.stabilize_roll();
     274     5922671 :     plane.stabilize_pitch();
     275     5922671 :     plane.stabilize_yaw();
     276     5922671 : }
     277             : 
     278             : // Reset rate and steering controllers
     279      813554 : void Mode::reset_controllers()
     280             : {
     281             :     // reset integrators
     282      813554 :     plane.rollController.reset();
     283      813554 :     plane.pitchController.reset();
     284      813554 :     plane.yawController.reset_I();
     285             : 
     286             :     // reset steering controls
     287      813554 :     plane.steer_state.locked_course = false;
     288      813554 :     plane.steer_state.locked_course_err = 0;
     289             : 
     290             :     // reset TECS
     291      813554 :     plane.TECS_controller.reset();
     292      813554 : }
     293             : 
     294        6734 : bool Mode::is_taking_off() const
     295             : {
     296        6734 :     return (plane.flight_stage == AP_FixedWing::FlightStage::TAKEOFF);
     297             : }
     298             : 
     299             : // Helper to output to both k_rudder and k_steering servo functions
     300     1766932 : void Mode::output_rudder_and_steering(float val)
     301             : {
     302     1766932 :     SRV_Channels::set_output_scaled(SRV_Channel::k_rudder, val);
     303     1766932 :     SRV_Channels::set_output_scaled(SRV_Channel::k_steering, val);
     304     1766932 : }
     305             : 
     306             : // Output pilot throttle, this is used in stabilized modes without auto throttle control
     307             : // Direct mapping if THR_PASS_STAB is set
     308             : // Otherwise apply curve for trim correction if configured
     309     1131208 : void Mode::output_pilot_throttle()
     310             : {
     311     1131208 :     if (plane.g.throttle_passthru_stabilize) {
     312             :         // THR_PASS_STAB set, direct mapping
     313           0 :         SRV_Channels::set_output_scaled(SRV_Channel::k_throttle, plane.get_throttle_input(true));
     314           0 :         return;
     315             :     }
     316             : 
     317             :     // get throttle, but adjust center to output TRIM_THROTTLE if flight option set
     318     1131208 :     SRV_Channels::set_output_scaled(SRV_Channel::k_throttle, plane.get_adjusted_throttle_input(true));
     319             : }
     320             : 
     321             : // true if throttle min/max limits should be applied
     322     7763246 : bool Mode::use_throttle_limits() const
     323             : {
     324             : #if AP_SCRIPTING_ENABLED
     325     7763246 :     if (plane.nav_scripting_active()) {
     326        6657 :         return false;
     327             :     }
     328             : #endif
     329             : 
     330     7756589 :     if (this == &plane.mode_stabilize ||
     331     7753820 :         this == &plane.mode_training ||
     332     7751512 :         this == &plane.mode_acro ||
     333     6656889 :         this == &plane.mode_fbwa ||
     334             :         this == &plane.mode_autotune) {
     335             :         // a manual throttle mode
     336     1119445 :         return !plane.g.throttle_passthru_stabilize;
     337             :     }
     338             : 
     339     6637144 :     if (is_guided_mode() && plane.guided_throttle_passthru) {
     340             :         // manual pass through of throttle while in GUIDED
     341          16 :         return false;
     342             :     }
     343             : 
     344             : #if HAL_QUADPLANE_ENABLED
     345     6637128 :     if (quadplane.in_vtol_mode()) {
     346     2741745 :         return quadplane.allow_forward_throttle_in_vtol_mode();
     347             :     }
     348             : #endif
     349             : 
     350     3895383 :     return true;
     351             : }
     352             : 
     353             : // true if voltage correction should be applied to throttle
     354     7763246 : bool Mode::use_battery_compensation() const
     355             : {
     356             : #if AP_SCRIPTING_ENABLED
     357     7763246 :     if (plane.nav_scripting_active()) {
     358        6657 :         return false;
     359             :     }
     360             : #endif
     361             : 
     362     7756589 :     if (this == &plane.mode_stabilize ||
     363     7753820 :         this == &plane.mode_training ||
     364     7751512 :         this == &plane.mode_acro ||
     365     6656889 :         this == &plane.mode_fbwa ||
     366             :         this == &plane.mode_autotune) {
     367             :         // a manual throttle mode
     368     1119445 :         return false;
     369             :     }
     370             : 
     371     6637144 :     if (is_guided_mode() && plane.guided_throttle_passthru) {
     372             :         // manual pass through of throttle while in GUIDED
     373          16 :         return false;
     374             :     }
     375             : 
     376             : #if HAL_QUADPLANE_ENABLED
     377     6637128 :     if (quadplane.in_vtol_mode()) {
     378     2741745 :         return false;
     379             :     }
     380             : #endif
     381             : 
     382     3895383 :     return true;
     383             : }
     384             : 
     385             : #if AP_PLANE_SYSTEMID_ENABLED
     386             : // Return true if fixed wing system ID should be allowed
     387           0 : bool Mode::allow_fw_systemid() const {
     388             : 
     389           0 :     if (!supports_fw_systemid()) {
     390             :         // Mode does not support fw system ID
     391           0 :         return false;
     392             :     }
     393             : 
     394           0 :     if (is_taking_off() || is_landing()) {
     395             :         // Taking off or landing
     396           0 :         return false;
     397             :     }
     398             : 
     399             : #if HAL_QUADPLANE_ENABLED
     400           0 :     if (quadplane.available()) {
     401           0 :         if (quadplane.in_assisted_flight()) {
     402             :             // VTOL motors assisting
     403           0 :             return false;
     404             :         }
     405           0 :         if (!quadplane.transition->complete()) {
     406             :             // Still in transition
     407           0 :             return false;
     408             :         }
     409             :     }
     410             : #endif // HAL_QUADPLANE_ENABLED
     411             : 
     412           0 :     return true;
     413             : }
     414             : #endif // AP_PLANE_SYSTEMID_ENABLED

Generated by: LCOV version 1.14