LCOV - code coverage report
Current view: top level - home/pbarker/rc/ardupilot/ArduCopter - AP_Arming_Copter.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 317 369 85.9 %
Date: 2026-09-09 11:02:08 Functions: 22 22 100.0 %

          Line data    Source code
       1             : #include "Copter.h"
       2             : 
       3             : #pragma GCC diagnostic push
       4             : #if defined(__clang_major__) && __clang_major__ >= 14
       5             : #pragma GCC diagnostic ignored "-Wbitwise-instead-of-logical"
       6             : #endif
       7             : 
       8      142354 : bool AP_Arming_Copter::pre_arm_checks(bool display_failure)
       9             : {
      10      142354 :     const bool passed = run_pre_arm_checks(display_failure);
      11      142354 :     set_pre_arm_check(passed);
      12      142354 :     return passed;
      13             : }
      14             : 
      15             : // perform pre-arm checks
      16             : //  return true if the checks pass successfully
      17      142354 : bool AP_Arming_Copter::run_pre_arm_checks(bool display_failure)
      18             : {
      19             :     // exit immediately if already armed
      20      142354 :     if (copter.motors->armed()) {
      21       49313 :         return true;
      22             :     }
      23             : 
      24       93041 :     if (!hal.scheduler->is_system_initialized()) {
      25           0 :         check_failed(display_failure, "System not initialised");
      26           0 :         return false;
      27             :     }
      28             : 
      29             :     // check if motor interlock and either Emergency Stop aux switches are used
      30             :     // at the same time.  This cannot be allowed.
      31       93041 :     bool passed = true;
      32       98411 :     if (rc().find_channel_for_option(RC_Channel::AUX_FUNC::MOTOR_INTERLOCK) &&
      33        5370 :         (rc().find_channel_for_option(RC_Channel::AUX_FUNC::MOTOR_ESTOP) || 
      34        2685 :         rc().find_channel_for_option(RC_Channel::AUX_FUNC::ARM_EMERGENCY_STOP))){
      35           0 :         check_failed(display_failure, "Interlock/E-Stop Conflict");
      36           0 :         passed = false;
      37             :     }
      38             : 
      39             :     // check if motor interlock aux switch is in use
      40             :     // if it is, switch needs to be in disabled position to arm
      41             :     // otherwise exit immediately.
      42       93041 :     if (copter.ap.using_interlock && copter.ap.motor_interlock_switch) {
      43         155 :         check_failed(display_failure, "Motor Interlock Enabled");
      44         155 :         passed = false;
      45             :     }
      46             : 
      47       93041 :     if (!disarm_switch_checks(display_failure)) {
      48           0 :         passed = false;
      49             :     }
      50             : 
      51             :     // always check motors
      52       93041 :     char failure_msg[100] {};
      53       93041 :     if (!copter.motors->arming_checks(ARRAY_SIZE(failure_msg), failure_msg)) {
      54          61 :         check_failed(display_failure, "Motors: %s", failure_msg);
      55          61 :         passed = false;
      56             :     }
      57             : 
      58             : #if FRAME_CONFIG == HELI_FRAME && MODE_AUTOROTATE_ENABLED
      59             :     // check on autorotation config
      60        2339 :     if (!copter.g2.arot.arming_checks(ARRAY_SIZE(failure_msg), failure_msg)) {
      61          91 :         check_failed(display_failure, "AROT: %s", failure_msg);
      62          91 :         passed = false;
      63             :     }
      64             : #endif
      65             : 
      66             :     // If not passed all checks return false
      67       93041 :     if (!passed) {
      68         307 :         return false;
      69             :     }
      70             : 
      71             :     // if pre arm checks are disabled run only the mandatory checks
      72       92734 :     if (should_skip_all_checks()) {
      73          18 :         return mandatory_checks(display_failure);
      74             :     }
      75             : 
      76             :     // bitwise & ensures all checks are run
      77       92716 :     return parameter_checks(display_failure)
      78       92716 :         & oa_checks(display_failure)
      79       92716 :         & gcs_failsafe_check(display_failure)
      80       92716 :         & winch_checks(display_failure)
      81       92716 :         & rc_throttle_failsafe_checks(display_failure)
      82       92716 :         & alt_checks(display_failure)
      83             : #if AP_AIRSPEED_ENABLED
      84       92716 :         & AP_Arming::airspeed_checks(display_failure)
      85             : #endif
      86       92716 :         & AP_Arming::pre_arm_checks(display_failure);
      87             : }
      88             : 
      89       92716 : bool AP_Arming_Copter::rc_throttle_failsafe_checks(bool display_failure) const
      90             : {
      91       92716 :     if (!check_enabled(Check::RC)) {
      92             :         // this check has been disabled
      93        2727 :         return true;
      94             :     }
      95             : 
      96             :     // throttle failsafe.  In this case the parameter also gates the
      97             :     // no-pulses RC failure case - the radio-in value can be zero due
      98             :     // to not having received any RC pulses at all.  Note that if we
      99             :     // have ever seen RC and then we *lose* RC then these checks are
     100             :     // likely to pass if the user is relying on no-pulses to detect RC
     101             :     // failure.  However, arming is precluded in that case by being in
     102             :     // RC failsafe.
     103       89989 :     if (copter.g.failsafe_throttle == Copter::FS_THR_Action::DISABLED) {
     104         111 :         return true;
     105             :     }
     106             : 
     107             : #if FRAME_CONFIG == HELI_FRAME
     108        2149 :     const char *rc_item = "Collective";
     109             : #else
     110       87729 :     const char *rc_item = "Throttle";
     111             : #endif
     112             : 
     113       89878 :     if (!rc().has_had_rc_receiver() && !rc().has_had_rc_override()) {
     114          12 :         check_failed(Check::RC, display_failure, "RC not found");
     115          12 :         return false;
     116             :     }
     117             : 
     118             :     // check throttle is not too low - must be above failsafe throttle
     119       89866 :     if (copter.channel_throttle->get_radio_in() < copter.g.failsafe_throttle_value) {
     120          33 :         check_failed(Check::RC, display_failure, "%s below failsafe", rc_item);
     121          33 :         return false;
     122             :     }
     123             : 
     124       89833 :     return true;
     125             : }
     126             : 
     127       92716 : bool AP_Arming_Copter::barometer_checks(bool display_failure)
     128             : {
     129       92716 :     if (!AP_Arming::barometer_checks(display_failure)) {
     130          10 :         return false;
     131             :     }
     132             : 
     133       92706 :     bool ret = true;
     134             :     // check Baro
     135       92706 :     if (check_enabled(Check::BARO)) {
     136             :         // Check baro & inav alt are within 1m if EKF is operating in an absolute position mode.
     137             :         // Do not check if intending to operate in a ground relative height mode as EKF will output a ground relative height
     138             :         // that may differ from the baro height due to baro drift.
     139       89979 :         const auto &ahrs = AP::ahrs();
     140       89979 :         const bool using_baro_ref = !ahrs.has_status(AP_AHRS::Status::PRED_HORIZ_POS_REL) && ahrs.has_status(AP_AHRS::Status::PRED_HORIZ_POS_ABS);
     141       89979 :         float pos_d_m = 0;
     142       89979 :         UNUSED_RESULT(AP::ahrs().get_relative_position_D_origin_float(pos_d_m));
     143       89979 :         if (using_baro_ref) {
     144           0 :             if (fabsf(-pos_d_m - copter.baro_alt_m) > PREARM_MAX_ALT_DISPARITY_M) {
     145           0 :                 check_failed(Check::BARO, display_failure, "Altitude disparity");
     146           0 :                 ret = false;
     147             :             }
     148             :         }
     149             :     }
     150       92706 :     return ret;
     151             : }
     152             : 
     153       92716 : bool AP_Arming_Copter::ins_checks(bool display_failure)
     154             : {
     155       92716 :     bool ret = AP_Arming::ins_checks(display_failure);
     156             : 
     157       92716 :     if (check_enabled(Check::INS)) {
     158             : 
     159             :         // get ekf attitude (if bad, it's usually the gyro biases)
     160       89989 :         if (!pre_arm_ekf_attitude_check()) {
     161       69004 :             check_failed(Check::INS, display_failure, "EKF attitude is bad");
     162       69004 :             ret = false;
     163             :         }
     164             :     }
     165             : 
     166       92716 :     return ret;
     167             : }
     168             : 
     169       92716 : bool AP_Arming_Copter::board_voltage_checks(bool display_failure)
     170             : {
     171       92716 :     if (!AP_Arming::board_voltage_checks(display_failure)) {
     172           0 :         return false;
     173             :     }
     174             : 
     175             :     // check battery voltage
     176       92716 :     if (check_enabled(Check::VOLTAGE)) {
     177       89989 :         if (copter.battery.has_failsafed()) {
     178          27 :             check_failed(Check::VOLTAGE, display_failure, "Battery failsafe");
     179          27 :             return false;
     180             :         }
     181             :     }
     182             : 
     183       92689 :     return true;
     184             : }
     185             : 
     186             : // expected to return true if the terrain database is required to have
     187             : // all data loaded
     188       89989 : bool AP_Arming_Copter::terrain_database_required() const
     189             : {
     190             : 
     191       89989 :     if (copter.wp_nav->get_terrain_source() == AC_WPNav::TerrainSource::TERRAIN_FROM_RANGEFINDER) {
     192             :         // primary terrain source is from rangefinder, allow arming without terrain database
     193        1789 :         return false;
     194             :     }
     195             : 
     196      176322 :     if (copter.wp_nav->get_terrain_source() == AC_WPNav::TerrainSource::TERRAIN_FROM_TERRAINDATABASE &&
     197       88122 :         copter.mode_rtl.get_alt_type() == ModeRTL::RTLAltType::TERRAIN) {
     198           3 :         return true;
     199             :     }
     200       88197 :     return AP_Arming::terrain_database_required();
     201             : }
     202             : 
     203       92716 : bool AP_Arming_Copter::parameter_checks(bool display_failure)
     204             : {
     205             :     // check various parameter values
     206       92716 :     if (check_enabled(Check::PARAMETERS)) {
     207             : 
     208             :         // failsafe parameter checks
     209       89989 :         if (copter.g.failsafe_throttle) {
     210             :             // check throttle min is above throttle failsafe trigger and that the trigger is above ppm encoder's loss-of-signal value of 900
     211       89878 :             if (copter.channel_throttle->get_radio_min() <= copter.g.failsafe_throttle_value+10 || copter.g.failsafe_throttle_value < 910) {
     212           0 :                 check_failed(Check::PARAMETERS, display_failure, "Check FS_THR_VALUE");
     213           9 :                 return false;
     214             :             }
     215             :         }
     216       89989 :         if (copter.g.failsafe_gcs == Copter::FS_GCS_Action::CONTINUE_MISSION) {
     217             :             // FS_GCS_ENABLE == 2 has been removed
     218           0 :             check_failed(Check::PARAMETERS, display_failure, "FS_GCS_ENABLE=2 removed, see FS_OPTIONS");
     219             :         }
     220             : 
     221             :         // acro balance parameter check
     222             : #if MODE_ACRO_ENABLED || MODE_SPORT_ENABLED
     223      179978 :         if (is_negative(copter.g.acro_balance_roll) || is_negative(copter.g.acro_balance_pitch) ||
     224      269967 :             (copter.g.acro_balance_roll > copter.attitude_control->get_angle_roll_p().kP()) || 
     225       89989 :             (copter.g.acro_balance_pitch > copter.attitude_control->get_angle_pitch_p().kP())) {
     226           0 :             check_failed(Check::PARAMETERS, display_failure, "Check ACRO_BAL_ROLL/PITCH");
     227           0 :             return false;
     228             :         }
     229             : #endif
     230             : 
     231             :         // pilot-speed-up parameter check
     232       89989 :         if (copter.g2.pilot_speed_up_ms <= 0) {
     233           0 :             check_failed(Check::PARAMETERS, display_failure, "Check PILOT_SPD_UP");
     234           0 :             return false;
     235             :         }
     236             : 
     237             :         #if FRAME_CONFIG == HELI_FRAME
     238        2149 :         char fail_msg[100]{};
     239             :         // check input manager parameters
     240        2149 :         if (!copter.input_manager.parameter_check(fail_msg, ARRAY_SIZE(fail_msg))) {
     241           0 :             check_failed(Check::PARAMETERS, display_failure, "%s", fail_msg);
     242           0 :             return false;
     243             :         }
     244             : 
     245             :         // Ensure an Aux Channel is configured for motor interlock
     246        2149 :         if (rc().find_channel_for_option(RC_Channel::AUX_FUNC::MOTOR_INTERLOCK) == nullptr) {
     247           0 :             check_failed(Check::PARAMETERS, display_failure, "Motor Interlock not configured");
     248           0 :             return false;
     249             :         }
     250             : 
     251             :         #else
     252       87840 :         switch (copter.g2.frame_class.get()) {
     253           0 :         case AP_Motors::MOTOR_FRAME_HELI_QUAD:
     254             :         case AP_Motors::MOTOR_FRAME_HELI_DUAL:
     255             :         case AP_Motors::MOTOR_FRAME_HELI:
     256           0 :             check_failed(Check::PARAMETERS, display_failure, "Invalid MultiCopter FRAME_CLASS");
     257           0 :             return false;
     258             : 
     259       87840 :         default:
     260       87840 :             break;
     261             :         }
     262             :         #endif // HELI_FRAME
     263             : 
     264             :         // checks when using range finder for RTL
     265             : #if MODE_RTL_ENABLED
     266       89989 :         if (copter.mode_rtl.get_alt_type() == ModeRTL::RTLAltType::TERRAIN) {
     267             :             // get terrain source from wpnav
     268         449 :             const char *failure_template = "RTL_ALT_TYPE is above-terrain but %s";
     269         449 :             switch (copter.wp_nav->get_terrain_source()) {
     270           0 :             case AC_WPNav::TerrainSource::TERRAIN_UNAVAILABLE:
     271           0 :                 check_failed(Check::PARAMETERS, display_failure, failure_template, "no terrain data");
     272           0 :                 return false;
     273             :                 break;
     274         446 :             case AC_WPNav::TerrainSource::TERRAIN_FROM_RANGEFINDER:
     275             : #if AP_RANGEFINDER_ENABLED
     276         446 :                 if (!copter.rangefinder_state.enabled || !copter.rangefinder.has_orientation(ROTATION_PITCH_270)) {
     277           2 :                     check_failed(Check::PARAMETERS, display_failure, failure_template, "no rangefinder");
     278           2 :                     return false;
     279             :                 }
     280             :                 // check if RTL_ALT_M is higher than rangefinder's max range
     281         444 :                 if (copter.mode_rtl.get_altitude_m() > copter.rangefinder.max_distance_orient(ROTATION_PITCH_270)) {
     282           0 :                     check_failed(Check::PARAMETERS, display_failure, failure_template, "RTL_ALT_M above RNGFND_MAX");
     283           0 :                     return false;
     284             :                 }
     285             : #else
     286             :                 check_failed(Check::PARAMETERS, display_failure, failure_template, "rangefinder not in firmware");
     287             : #endif
     288         444 :                 break;
     289           3 :             case AC_WPNav::TerrainSource::TERRAIN_FROM_TERRAINDATABASE:
     290             :                 // these checks are done in AP_Arming
     291           3 :                 break;
     292             :             }
     293             :         }
     294             : #endif
     295             : 
     296             :         // check adsb avoidance failsafe
     297             : #if HAL_ADSB_ENABLED
     298       89987 :         if (copter.failsafe.adsb) {
     299           6 :             check_failed(Check::PARAMETERS, display_failure, "ADSB threat detected");
     300           6 :             return false;
     301             :         }
     302             : #endif
     303             : 
     304             :         // ensure controllers are OK with us arming:
     305       89981 :         char failure_msg[100] = {};
     306       89981 :         if (!copter.pos_control->pre_arm_checks("PSC", failure_msg, ARRAY_SIZE(failure_msg))) {
     307           1 :             check_failed(Check::PARAMETERS, display_failure, "Bad parameter: %s", failure_msg);
     308           1 :             return false;
     309             :         }
     310       89980 :         if (!copter.attitude_control->pre_arm_checks("ATC", failure_msg, ARRAY_SIZE(failure_msg))) {
     311           0 :             check_failed(Check::PARAMETERS, display_failure, "Bad parameter: %s", failure_msg);
     312           0 :             return false;
     313             :         }
     314             :     }
     315             : 
     316       92707 :     return true;
     317             : }
     318             : 
     319       92716 : bool AP_Arming_Copter::oa_checks(bool display_failure)
     320             : {
     321             : #if AP_OAPATHPLANNER_ENABLED
     322       92716 :     char failure_msg[100] = {};
     323       92716 :     if (copter.g2.oa.pre_arm_check(failure_msg, ARRAY_SIZE(failure_msg))) {
     324       92715 :         return true;
     325             :     }
     326             :     // display failure
     327           1 :     if (strlen(failure_msg) == 0) {
     328           0 :         check_failed(display_failure, "%s", "Check Object Avoidance");
     329             :     } else {
     330           1 :         check_failed(display_failure, "%s", failure_msg);
     331             :     }
     332           1 :     return false;
     333             : #else
     334             :     return true;
     335             : #endif
     336             : }
     337             : 
     338       90863 : bool AP_Arming_Copter::rc_calibration_checks(bool display_failure)
     339             : {
     340             :     const RC_Channel *channels[] = {
     341       90863 :         copter.channel_roll,
     342       90863 :         copter.channel_pitch,
     343       90863 :         copter.channel_throttle,
     344       90863 :         copter.channel_yaw
     345       90863 :     };
     346             : 
     347             :     // bitwise & ensures all checks are run
     348       90863 :     copter.ap.pre_arm_rc_check = rc_checks_copter_sub(display_failure, channels)
     349       90863 :         & AP_Arming::rc_calibration_checks(display_failure);
     350             : 
     351       90863 :     return copter.ap.pre_arm_rc_check;
     352             : }
     353             : 
     354             : // performs pre_arm gps related checks and returns true if passed
     355       92716 : bool AP_Arming_Copter::gps_checks(bool display_failure)
     356             : {
     357             :     // check if fence requires position
     358       92716 :     bool fence_requires_position = false;
     359             : #if AP_FENCE_ENABLED
     360             :     // if circular or polygon fence is enabled we need position
     361       92716 :     fence_requires_position = (copter.fence.get_enabled_fences() & (AC_FENCE_TYPE_CIRCLE | AC_FENCE_TYPE_POLYGON)) > 0;
     362             : #endif
     363             : 
     364             :     // check if flight mode requires GPS
     365       92716 :     const bool mode_requires_position = copter.flightmode->requires_position() || fence_requires_position || (copter.simple_mode == Copter::SimpleMode::SUPERSIMPLE);
     366       92716 :     const bool mode_requires_gps = AP::ahrs().active_backend_configured_to_use_gps() && mode_requires_position;
     367             : 
     368             :     // call parent gps checks
     369       92716 :     if (mode_requires_gps) {
     370        8514 :         if (!AP_Arming::gps_checks(display_failure)) {
     371        2167 :             AP_Notify::flags.pre_arm_gps_check = false;
     372        2167 :             return false;
     373             :         }
     374             :     }
     375             : 
     376             :     // run mandatory position checks first
     377       90549 :     if (!mandatory_position_checks(display_failure)) {
     378        5979 :         AP_Notify::flags.pre_arm_gps_check = false;
     379        5979 :         return false;
     380             :     }
     381             : 
     382             :     // return true if GPS is not required
     383       84570 :     if (!mode_requires_gps) {
     384       81810 :         AP_Notify::flags.pre_arm_gps_check = true;
     385       81810 :         return true;
     386             :     }
     387             : 
     388             :     // return true immediately if gps check is disabled
     389        2760 :     if (!check_enabled(Check::GPS)) {
     390           0 :         AP_Notify::flags.pre_arm_gps_check = true;
     391           0 :         return true;
     392             :     }
     393             : 
     394             :     // warn about hdop separately - to prevent user confusion with no gps lock
     395        2760 :     if ((copter.gps.num_sensors() > 0) && (copter.gps.get_hdop() > copter.g.gps_hdop_good)) {
     396           0 :         check_failed(Check::GPS, display_failure, "High GPS HDOP");
     397           0 :         AP_Notify::flags.pre_arm_gps_check = false;
     398           0 :         return false;
     399             :     }
     400             : 
     401             :     // if we got here all must be ok
     402        2760 :     AP_Notify::flags.pre_arm_gps_check = true;
     403        2760 :     return true;
     404             : }
     405             : 
     406             : // check ekf attitude is acceptable
     407       89989 : bool AP_Arming_Copter::pre_arm_ekf_attitude_check()
     408             : {
     409       89989 :     return AP::ahrs().has_status(AP_AHRS::Status::ATTITUDE_VALID);
     410             : }
     411             : 
     412             : #if HAL_PROXIMITY_ENABLED
     413             : // check nothing is too close to vehicle
     414       92716 : bool AP_Arming_Copter::proximity_checks(bool display_failure) const
     415             : {
     416             : 
     417       92716 :     if (!AP_Arming::proximity_checks(display_failure)) {
     418         121 :         return false;
     419             :     }
     420             : 
     421       92595 :     if (!check_enabled(Check::PARAMETERS)) {
     422             :         // check is disabled
     423        2727 :         return true;
     424             :     }
     425             : 
     426             :     // get closest object if we might use it for avoidance
     427             : #if AP_AVOIDANCE_ENABLED
     428             :     float angle_deg, distance;
     429       89868 :     if (copter.avoid.proximity_avoidance_enabled() && copter.g2.proximity.get_closest_object(angle_deg, distance)) {
     430             :         // display error if something is within 60cm
     431         264 :         const float tolerance = 0.6f;
     432         264 :         if (distance <= tolerance) {
     433          18 :             check_failed(Check::PARAMETERS, display_failure, "Proximity %d deg, %4.2fm (want > %0.1fm)", (int)angle_deg, (double)distance, (double)tolerance);
     434          18 :             return false;
     435             :         }
     436             :     }
     437             : #endif
     438             : 
     439       89850 :     return true;
     440             : }
     441             : #endif  // HAL_PROXIMITY_ENABLED
     442             : 
     443             : // performs mandatory position checks.  returns true if passed
     444       90571 : bool AP_Arming_Copter::mandatory_position_checks(bool display_failure)
     445             : {
     446             :     // check if flight mode requires position
     447       90571 :     bool mode_requires_position = copter.flightmode->requires_position();
     448             : 
     449             :     // always check if inertial nav has started and is ready
     450       90571 :     const auto &ahrs = AP::ahrs();
     451       90571 :     char failure_msg[100] = {};
     452       90571 :     if (!ahrs.pre_arm_check(mode_requires_position, failure_msg, sizeof(failure_msg))) {
     453        2392 :         check_failed(display_failure, "AHRS: %s", failure_msg);
     454        2392 :         return false;
     455             :     }
     456             : 
     457             :     // check if fence requires position estimate
     458       88179 :     bool fence_requires_position = false;
     459             : #if AP_FENCE_ENABLED
     460             :     // if circular or polygon fence is enabled we need position estimate
     461       88179 :     fence_requires_position = (copter.fence.get_enabled_fences() & (AC_FENCE_TYPE_CIRCLE | AC_FENCE_TYPE_POLYGON)) > 0;
     462             : #endif
     463             : 
     464       88179 :     if (mode_requires_position || require_location == RequireLocation::YES) {
     465        6345 :         if (!copter.position_ok()) {
     466             :             // vehicle level position estimate checks
     467        3557 :             check_failed(display_failure, "Need Position Estimate");
     468        3557 :             return false;
     469             :         }
     470       81834 :     } else if (fence_requires_position) {
     471          31 :         if (!copter.position_ok()) {
     472             :             // clarify to user why they need GPS in non-GPS flight mode
     473          24 :             check_failed(display_failure, "Fence enabled, need position estimate");
     474          24 :             return false;
     475             :         }
     476             :     } else {
     477             :         // return true if GPS is not required
     478       81803 :         return true;
     479             :     }
     480             : 
     481             :     // check for GPS glitch (as reported by EKF)
     482             :     // this can only be true if the EKF is using the GPS
     483             :     nav_filter_status filt_status;
     484        2795 :     if (ahrs.get_filter_status(filt_status)) {
     485        2795 :         if (filt_status.flags.gps_glitching) {
     486           0 :             check_failed(display_failure, "GPS glitching");
     487           0 :             return false;
     488             :         }
     489             :     }
     490             : 
     491             :     // check EKF's compass, position, height and velocity variances are below failsafe threshold
     492        2795 :     if (copter.g.fs_ekf_thresh > 0.0f) {
     493             :         float vel_variance, pos_variance, hgt_variance, tas_variance;
     494        2795 :         Vector3f mag_variance;
     495        2795 :         ahrs.get_variances(vel_variance, pos_variance, hgt_variance, mag_variance, tas_variance);
     496             :         const struct {
     497             :             const char *name;
     498             :             float value;
     499        2795 :         } variances[] {
     500        2795 :             { "compass", mag_variance.length() },
     501             :             { "position", pos_variance },
     502             :             { "velocity", vel_variance },
     503             :             { "height", hgt_variance },
     504        2795 :         };
     505       13953 :         for (auto &variance : variances) {
     506       11164 :             if (variance.value < copter.g.fs_ekf_thresh) {
     507       11158 :                 continue;
     508             :             }
     509           6 :             check_failed(display_failure, "EKF %s variance", variance.name);
     510           6 :             return false;
     511             :         }
     512             :     }
     513             : 
     514             :     // if we got here all must be ok
     515        2789 :     return true;
     516             : }
     517             : 
     518             : // Check GCS failsafe
     519       92716 : bool AP_Arming_Copter::gcs_failsafe_check(bool display_failure)
     520             : {
     521       92716 :     if (copter.failsafe.gcs) {
     522           8 :         check_failed(display_failure, "GCS failsafe on");
     523           8 :         return false;
     524             :     }
     525       92708 :     return true;
     526             : }
     527             : 
     528             : // check winch
     529       92716 : bool AP_Arming_Copter::winch_checks(bool display_failure) const
     530             : {
     531             : #if AP_WINCH_ENABLED
     532             :     // pass if parameter or all arming checks disabled
     533       92716 :     if (!check_enabled(Check::PARAMETERS)) {
     534        2727 :         return true;
     535             :     }
     536             : 
     537       89989 :     const AP_Winch *winch = AP::winch();
     538       89989 :     if (winch == nullptr) {
     539           0 :         return true;
     540             :     }
     541       89989 :     char failure_msg[100] = {};
     542       89989 :     if (!winch->pre_arm_check(failure_msg, sizeof(failure_msg))) {
     543           2 :         check_failed(display_failure, "%s", failure_msg);
     544           2 :         return false;
     545             :     }
     546             : #endif
     547       89987 :     return true;
     548             : }
     549             : 
     550             : // performs altitude checks.  returns true if passed
     551       92738 : bool AP_Arming_Copter::alt_checks(bool display_failure)
     552             : {
     553             :     // always EKF altitude estimate
     554       92738 :     if (!copter.flightmode->has_manual_throttle() && !copter.ekf_alt_ok()) {
     555         228 :         check_failed(display_failure, "Need Alt Estimate");
     556         228 :         return false;
     557             :     }
     558             : 
     559       92510 :     return true;
     560             : }
     561             : 
     562             : // arm_checks - perform final checks before arming
     563             : //  always called just before arming.  Return true if ok to arm
     564             : //  has side-effect that logging is started
     565         565 : bool AP_Arming_Copter::arm_checks(AP_Arming::Method method)
     566             : {
     567         565 :     const auto &ahrs = AP::ahrs();
     568             : 
     569             :     // always check if inertial nav has started and is ready
     570         565 :     if (!ahrs.healthy()) {
     571           0 :         check_failed(true, "AHRS not healthy");
     572           0 :         return false;
     573             :     }
     574             : 
     575             :     // if non-compass is source of heading we can skip compass health check
     576        1128 :     if (check_enabled(Check::COMPASS) &&
     577         563 :         !ahrs.using_noncompass_for_yaw()) {
     578         556 :         const Compass &_compass = AP::compass();
     579             :         // check compass health
     580         556 :         if (!_compass.healthy()) {
     581           0 :             check_failed(true, "Compass %d not healthy", _compass.get_first_usable() + 1);
     582           0 :             return false;
     583             :         }
     584             :     }
     585             : 
     586             :     // always check if the current mode allows arming
     587         565 :     if (!copter.flightmode->allows_arming(method)) {
     588          13 :         check_failed(true, "%s mode not armable", copter.flightmode->name());
     589          13 :         return false;
     590             :     }
     591             : 
     592             :     // succeed if arming checks are disabled
     593         552 :     if (should_skip_all_checks()) {
     594           1 :         return true;
     595             :     }
     596             : 
     597             :     // check lean angle
     598         551 :     if (check_enabled(Check::INS)) {
     599         547 :         if (acosf(ahrs.cos_roll()*ahrs.cos_pitch()) > copter.attitude_control->lean_angle_max_rad()) {
     600           0 :             check_failed(Check::INS, true, "Leaning");
     601           0 :             return false;
     602             :         }
     603             :     }
     604             : 
     605             :     // check adsb
     606             : #if HAL_ADSB_ENABLED
     607         551 :     if (check_enabled(Check::PARAMETERS)) {
     608         547 :         if (copter.failsafe.adsb) {
     609           0 :             check_failed(Check::PARAMETERS, true, "ADSB threat detected");
     610           0 :             return false;
     611             :         }
     612             :     }
     613             : #endif
     614             : 
     615             :     // check throttle
     616         551 :     if (check_enabled(Check::RC)) {
     617             : #if FRAME_CONFIG == HELI_FRAME
     618          59 :         const char *rc_item = "Collective";
     619             : #else
     620         488 :         const char *rc_item = "Throttle";
     621             : #endif
     622             :         // check throttle is not too high - skips checks if arming from GCS/scripting in Guided,Guided_NoGPS or Auto 
     623         547 :         if (!((AP_Arming::method_is_GCS(method) || method == AP_Arming::Method::SCRIPTING) && copter.flightmode->allows_GCS_or_SCR_arming_with_throttle_high())) {
     624             :             // above top of deadband is too always high
     625         349 :             if (copter.get_pilot_desired_climb_rate_ms() > 0.0f) {
     626           4 :                 check_failed(Check::RC, true, "%s too high", rc_item);
     627           4 :                 return false;
     628             :             }
     629             :             // in manual modes throttle must be at zero
     630             : #if FRAME_CONFIG == HELI_FRAME
     631          52 :             if ((copter.flightmode->has_manual_throttle() || copter.flightmode->mode_number() == Mode::Number::DRIFT) && copter.motors->get_takeoff_collective()) {
     632             : #else
     633         293 :             if ((copter.flightmode->has_manual_throttle() || copter.flightmode->mode_number() == Mode::Number::DRIFT) && copter.channel_throttle->get_control_in() > 0) {
     634             : #endif
     635           0 :                 check_failed(Check::RC, true, "%s too high", rc_item);
     636           0 :                 return false;
     637             :             }
     638             :         }
     639             :     }
     640             : 
     641             :     // check if safety switch has been pushed
     642         547 :     if (hal.util->safety_switch_state() == AP_HAL::Util::SAFETY_DISARMED) {
     643           0 :         check_failed(true, "Safety Switch");
     644           0 :         return false;
     645             :     }
     646             : 
     647             :     // superclass method should always be the last thing called; it
     648             :     // has side-effects which would need to be cleaned up if one of
     649             :     // our arm checks failed
     650         547 :     return AP_Arming::arm_checks(method);
     651             : }
     652             : 
     653             : // mandatory checks that will be run if ARMING_SKIPCHK skips all or arming forced
     654          22 : bool AP_Arming_Copter::mandatory_checks(bool display_failure)
     655             : {
     656             :     // call mandatory position checks and update notify status because regular position checks will not run
     657          22 :     bool result = mandatory_position_checks(display_failure);
     658          22 :     AP_Notify::flags.pre_arm_gps_check = result;
     659             : 
     660             :     // call mandatory alt check
     661          22 :     if (!alt_checks(display_failure)) {
     662           2 :         result = false;
     663             :     }
     664             : 
     665          22 :     return result & AP_Arming::mandatory_checks(display_failure);
     666             : }
     667             : 
     668      142354 : void AP_Arming_Copter::set_pre_arm_check(bool b)
     669             : {
     670      142354 :     copter.ap.pre_arm_check = b;
     671      142354 :     AP_Notify::flags.pre_arm_check = b;
     672      142354 : }
     673             : 
     674         630 : bool AP_Arming_Copter::arm(const AP_Arming::Method method, const bool do_arming_checks)
     675             : {
     676             :     static bool in_arm_motors = false;
     677             : 
     678             :     // exit immediately if already in this function
     679         630 :     if (in_arm_motors) {
     680           0 :         return false;
     681             :     }
     682         630 :     in_arm_motors = true;
     683             : 
     684             :     // return true if already armed
     685         630 :     if (copter.motors->armed()) {
     686          14 :         in_arm_motors = false;
     687          14 :         return true;
     688             :     }
     689             : 
     690         616 :     if (!AP_Arming::arm(method, do_arming_checks)) {
     691          64 :         AP_Notify::events.arming_failed = true;
     692          64 :         in_arm_motors = false;
     693          64 :         return false;
     694             :     }
     695             : 
     696             : #if HAL_LOGGING_ENABLED
     697             :     // let logger know that we're armed (it may open logs e.g.)
     698         552 :     AP::logger().set_vehicle_armed(true);
     699             : #endif
     700             : 
     701             :     // disable cpu failsafe because initialising everything takes a while
     702         552 :     copter.failsafe_disable();
     703             : 
     704             :     // notify that arming will occur (we do this early to give plenty of warning)
     705         552 :     AP_Notify::flags.armed = true;
     706             :     // call notify update a few times to ensure the message gets out
     707        6624 :     for (uint8_t i=0; i<=10; i++) {
     708        6072 :         AP::notify().update();
     709             :     }
     710             : 
     711             : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
     712         552 :     send_arm_disarm_statustext("Arming motors");
     713             : #endif
     714             : 
     715             :     // Remember Orientation
     716             :     // --------------------
     717         552 :     copter.init_simple_bearing();
     718             : 
     719         552 :     auto &ahrs = AP::ahrs();
     720             : 
     721         552 :     copter.initial_armed_bearing_rad = ahrs.get_yaw_rad();
     722             : 
     723         552 :     if (!ahrs.home_is_set()) {
     724             :         // Reset EKF altitude if home hasn't been set yet (we use EKF altitude as substitute for alt above home)
     725           8 :         ahrs.resetHeightDatum();
     726           8 :         LOGGER_WRITE_EVENT(LogEvent::EKF_ALT_RESET);
     727             : 
     728             :         // we have reset height, so arming height is zero
     729           8 :         copter.arming_altitude_m = 0;
     730         544 :     } else if (!ahrs.home_is_locked()) {
     731             :         // Reset home position if it has already been set before (but not locked)
     732         521 :         if (!copter.set_home_to_current_location(false)) {
     733             :             // ignore failure
     734             :         }
     735             : 
     736             :         // remember the height when we armed (ignore failures)
     737         521 :         float pos_d_m = 0;
     738         521 :         UNUSED_RESULT(AP::ahrs().get_relative_position_D_origin_float(pos_d_m));
     739         521 :         copter.arming_altitude_m = -pos_d_m;
     740             :     }
     741         552 :     copter.update_super_simple_bearing(false);
     742             : 
     743             :     // Reset SmartRTL return location. If activated, SmartRTL will ultimately try to land at this point
     744             : #if MODE_SMARTRTL_ENABLED
     745         552 :     copter.g2.smart_rtl.set_home(copter.position_ok());
     746             : #endif
     747             : 
     748         552 :     hal.util->set_soft_armed(true);
     749             : 
     750             : #if HAL_SPRAYER_ENABLED
     751             :     // turn off sprayer's test if on
     752         552 :     copter.sprayer.test_pump(false);
     753             : #endif
     754             : 
     755             :     // output lowest possible value to motors
     756         552 :     copter.motors->output_min();
     757             : 
     758             :     // finally actually arm the motors
     759         552 :     copter.motors->armed(true);
     760             : 
     761             : #if HAL_LOGGING_ENABLED
     762             :     // log flight mode in case it was changed while vehicle was disarmed
     763         552 :     AP::logger().Write_Mode((uint8_t)copter.flightmode->mode_number(), copter.control_mode_reason);
     764             : #endif
     765             : 
     766             :     // re-enable failsafe
     767         552 :     copter.failsafe_enable();
     768             : 
     769             :     // perf monitor ignores delay due to arming
     770         552 :     AP::scheduler().perf_info.ignore_this_loop();
     771             : 
     772             :     // flag exiting this function
     773         552 :     in_arm_motors = false;
     774             : 
     775             :     // Log time stamp of arming event
     776         552 :     copter.arm_time_ms = millis();
     777             : 
     778             :     // Start the arming delay
     779         552 :     copter.ap.in_arming_delay = true;
     780             : 
     781             :     // assumed armed without a arming, switch. Overridden in switches.cpp
     782         552 :     copter.ap.armed_with_airmode_switch = false;
     783             : 
     784             :     // return success
     785         552 :     return true;
     786             : }
     787             : 
     788             : // arming.disarm - disarm motors
     789         648 : bool AP_Arming_Copter::disarm(const AP_Arming::Method method, bool do_disarm_checks)
     790             : {
     791             :     // return immediately if we are already disarmed
     792         648 :     if (!copter.motors->armed()) {
     793          74 :         return true;
     794             :     }
     795             : 
     796             :     // do not allow disarm via mavlink if we think we are flying:
     797         523 :     if (do_disarm_checks &&
     798        1097 :         AP_Arming::method_is_GCS(method) &&
     799          74 :         !copter.ap.land_complete) {
     800           0 :         return false;
     801             :     }
     802             : 
     803         574 :     if (method == AP_Arming::Method::RUDDER) {
     804          27 :         if (!copter.flightmode->has_manual_throttle() && !copter.ap.land_complete) {
     805          20 :             return false;
     806             :         }
     807             :     }
     808             : 
     809         554 :     if (!AP_Arming::disarm(method, do_disarm_checks)) {
     810           4 :         return false;
     811             :     }
     812             : 
     813             : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
     814         550 :     send_arm_disarm_statustext("Disarming motors");
     815             : #endif
     816             : 
     817             :     // we are not in the air
     818         550 :     copter.set_land_complete(true);
     819         550 :     copter.set_land_complete_maybe(true);
     820             : 
     821             :     // send disarm command to motors
     822         550 :     copter.motors->armed(false);
     823             : 
     824             : #if MODE_AUTO_ENABLED
     825             :     // reset the mission
     826         550 :     copter.mode_auto.mission.reset();
     827             : #endif
     828             : 
     829             : #if HAL_LOGGING_ENABLED
     830         550 :     AP::logger().set_vehicle_armed(false);
     831             : #endif
     832             : 
     833         550 :     hal.util->set_soft_armed(false);
     834             : 
     835         550 :     copter.ap.in_arming_delay = false;
     836             : 
     837             : #if AUTOTUNE_ENABLED
     838             :     // Possibly save auto tuned parameters
     839         550 :     copter.mode_autotune.autotune.disarmed(copter.flightmode == &copter.mode_autotune);
     840             : #endif
     841             : 
     842         550 :     return true;
     843             : }
     844             : 
     845             : #pragma GCC diagnostic pop

Generated by: LCOV version 1.14