LCOV - code coverage report
Current view: top level - AP_Arming - AP_Arming.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 707 882 80.2 %
Date: 2026-09-09 11:02:08 Functions: 62 63 98.4 %

          Line data    Source code
       1             : /*
       2             :    This program is free software: you can redistribute it and/or modify
       3             :    it under the terms of the GNU General Public License as published by
       4             :    the Free Software Foundation, either version 3 of the License, or
       5             :    (at your option) any later version.
       6             : 
       7             :    This program is distributed in the hope that it will be useful,
       8             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
       9             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      10             :    GNU General Public License for more details.
      11             : 
      12             :    You should have received a copy of the GNU General Public License
      13             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      14             :  */
      15             : 
      16             : #include "AP_Arming_config.h"
      17             : 
      18             : #if AP_ARMING_ENABLED
      19             : 
      20             : #include "AP_Arming.h"
      21             : #include <AP_HAL/AP_HAL.h>
      22             : #include <AP_BoardConfig/AP_BoardConfig.h>
      23             : #include <AP_BattMonitor/AP_BattMonitor.h>
      24             : #include <AP_Compass/AP_Compass.h>
      25             : #include <AP_Notify/AP_Notify.h>
      26             : #include <GCS_MAVLink/GCS.h>
      27             : #include <GCS_MAVLink/GCS_MAVLink.h>
      28             : #include <AP_Mission/AP_Mission.h>
      29             : #include <AP_Proximity/AP_Proximity.h>
      30             : #include <AP_Rally/AP_Rally.h>
      31             : #include <SRV_Channel/SRV_Channel.h>
      32             : #include <AC_Fence/AC_Fence.h>
      33             : #include <AP_InertialSensor/AP_InertialSensor.h>
      34             : #include <AP_InternalError/AP_InternalError.h>
      35             : #include <AP_GPS/AP_GPS.h>
      36             : #include <AP_Declination/AP_Declination.h>
      37             : #include <AP_Airspeed/AP_Airspeed.h>
      38             : #include <AP_AHRS/AP_AHRS.h>
      39             : #include <AP_Baro/AP_Baro.h>
      40             : #include <AP_RangeFinder/AP_RangeFinder.h>
      41             : #include <AP_Generator/AP_Generator.h>
      42             : #include <AP_Terrain/AP_Terrain.h>
      43             : #include <AP_ADSB/AP_ADSB.h>
      44             : #include <AP_Scripting/AP_Scripting.h>
      45             : #include <AP_Camera/AP_RunCam.h>
      46             : #include <AP_GyroFFT/AP_GyroFFT.h>
      47             : #include <AP_VisualOdom/AP_VisualOdom.h>
      48             : #include <AP_Parachute/AP_Parachute.h>
      49             : #include <AP_OSD/AP_OSD.h>
      50             : #include <AP_Relay/AP_Relay.h>
      51             : #include <RC_Channel/RC_Channel.h>
      52             : #include <AP_Button/AP_Button.h>
      53             : #include <AP_FETtecOneWire/AP_FETtecOneWire.h>
      54             : #include <AP_RPM/AP_RPM.h>
      55             : #include <AP_Mount/AP_Mount.h>
      56             : #include <AP_OpenDroneID/AP_OpenDroneID.h>
      57             : #include <AP_SerialManager/AP_SerialManager.h>
      58             : #include <AP_Vehicle/AP_Vehicle_Type.h>
      59             : #include <AP_Scheduler/AP_Scheduler.h>
      60             : #include <AP_KDECAN/AP_KDECAN.h>
      61             : #include <AP_Vehicle/AP_Vehicle.h>
      62             : #include <AP_ICEngine/AP_ICEngine.h>
      63             : 
      64             : #if HAL_MAX_CAN_PROTOCOL_DRIVERS
      65             :   #include <AP_CANManager/AP_CANManager.h>
      66             :   #include <AP_Common/AP_Common.h>
      67             :   #include <AP_Vehicle/AP_Vehicle_Type.h>
      68             : 
      69             :   #include <AP_PiccoloCAN/AP_PiccoloCAN.h>
      70             :   #include <AP_DroneCAN/AP_DroneCAN.h>
      71             : #endif
      72             : 
      73             : #include <AP_Logger/AP_Logger.h>
      74             : 
      75             : #define AP_ARMING_COMPASS_MAGFIELD_EXPECTED 530
      76             : #define AP_ARMING_COMPASS_MAGFIELD_MIN  185     // 0.35 * 530 milligauss
      77             : #define AP_ARMING_COMPASS_MAGFIELD_MAX  875     // 1.65 * 530 milligauss
      78             : #define AP_ARMING_BOARD_VOLTAGE_MAX     5.8f
      79             : #define AP_ARMING_ACCEL_ERROR_THRESHOLD 0.75f
      80             : #define AP_ARMING_MAGFIELD_ERROR_THRESHOLD 100
      81             : #define AP_ARMING_AHRS_GPS_ERROR_MAX    10      // accept up to 10m difference between AHRS and GPS
      82             : 
      83             : #if APM_BUILD_TYPE(APM_BUILD_ArduPlane)
      84             :   #define ARMING_RUDDER_DEFAULT         (uint8_t)RudderArming::ARMONLY
      85             : #else
      86             :   #define ARMING_RUDDER_DEFAULT         (uint8_t)RudderArming::ARMDISARM
      87             : #endif
      88             : 
      89             : // find a default value for ARMING_NEED_POS parameter, and determine
      90             : // whether the parameter should be shown:
      91             : #ifndef AP_ARMING_NEED_LOC_PARAMETER_ENABLED
      92             : // determine whether ARMING_NEED_POS is shown:
      93             : #if APM_BUILD_COPTER_OR_HELI || APM_BUILD_TYPE(APM_BUILD_Rover)
      94             : #define AP_ARMING_NEED_LOC_PARAMETER_ENABLED 1
      95             : #else
      96             : #define AP_ARMING_NEED_LOC_PARAMETER_ENABLED 0
      97             : #endif  // build types
      98             : #endif  // AP_ARMING_NEED_LOC_PARAMETER_ENABLED
      99             : 
     100             : // if ARMING_NEED_POS is shown, determine what its default should be:
     101             : #if AP_ARMING_NEED_LOC_PARAMETER_ENABLED
     102             : #if APM_BUILD_COPTER_OR_HELI || APM_BUILD_TYPE(APM_BUILD_Rover)
     103             : #define AP_ARMING_NEED_LOC_DEFAULT 0
     104             : #else
     105             : #error "Unable to find value for AP_ARMING_NEED_LOC_DEFAULT"
     106             : #endif  // APM_BUILD_TYPE
     107             : #endif  // AP_ARMING_NEED_LOC_PARAMETER_ENABLED
     108             : 
     109             : #ifndef PREARM_DISPLAY_PERIOD
     110             : # define PREARM_DISPLAY_PERIOD 30
     111             : #endif
     112             : 
     113             : #ifndef AP_ARMING_IMU_CONSISTENCY_CHECK_TIME_MS
     114             :   #define AP_ARMING_IMU_CONSISTENCY_CHECK_TIME_MS 10000
     115             : #endif
     116             : 
     117             : extern const AP_HAL::HAL& hal;
     118             : 
     119             : const AP_Param::GroupInfo AP_Arming::var_info[] = {
     120             : 
     121             :     // @Param{Plane, Rover}: REQUIRE
     122             :     // @DisplayName: Require Arming Motors 
     123             :     // @Description{Plane}: Arming disabled until some requirements are met. If 0, there are no requirements (arm immediately).  If 1, sends the minimum throttle PWM value to the throttle channel when disarmed. If 2, send 0 PWM (no signal) to throttle channel when disarmed. On planes with ICE enabled and the throttle while disarmed option set in ICE_OPTIONS, the motor will always get THR_MIN when disarmed. Arming will be blocked until all mandatory and non-ARMING_SKIPCHK items are satisfied; arming can then be accomplished via (eg.) rudder gesture or GCS command.
     124             :     // @Description{Rover}: Arming disabled until some requirements are met. If 0, there are no requirements (arm immediately).  If 1, all checks not skipped by ARMING_SKIPCHK must pass before the vehicle can be armed (for example, via rudder stick or GCS command).  If 3, Arm immediately once pre-arm/arm checks are satisfied, but only one time per boot up.  Note that a reboot is NOT required when setting to 0 but IS require when setting to 3.
     125             :     // @Values{Plane}: 0:Disabled,1:Yes(minimum PWM when disarmed),2:Yes(0 PWM when disarmed)
     126             :     // @Values{Rover}: 0:No,1:Yes(minimum PWM when disarmed),3:No(AutoArmOnce after checks are passed)
     127             :     // @User: Advanced
     128             :     AP_GROUPINFO_FLAGS_FRAME("REQUIRE",     0,      AP_Arming,  require, float(Required::YES_MIN_PWM),
     129             :                              AP_PARAM_FLAG_NO_SHIFT,
     130             :                              AP_PARAM_FRAME_PLANE | AP_PARAM_FRAME_ROVER),
     131             : 
     132             :     // 2 was the CHECK paramter stored in a AP_Int16
     133             : 
     134             :     // @Param: ACCTHRESH
     135             :     // @DisplayName: Accelerometer error threshold
     136             :     // @Description: Accelerometer error threshold used to determine inconsistent accelerometers. Compares this error range to other accelerometers to detect a hardware or calibration error. Lower value means tighter check and harder to pass arming check. Not all accelerometers are created equal.
     137             :     // @Units: m/s/s
     138             :     // @Range: 0.25 3.0
     139             :     // @User: Advanced
     140             :     AP_GROUPINFO("ACCTHRESH",    3,     AP_Arming,  accel_error_threshold,  AP_ARMING_ACCEL_ERROR_THRESHOLD),
     141             : 
     142             :     // index 4 was VOLT_MIN, moved to AP_BattMonitor
     143             :     // index 5 was VOLT2_MIN, moved to AP_BattMonitor
     144             : 
     145             :     // @Param{Plane,Rover,Copter,Blimp}: RUDDER
     146             :     // @DisplayName: Arming with Rudder enable/disable
     147             :     // @Description: Allow arm/disarm by rudder input. When enabled arming can be done with right rudder, disarming with left rudder. Rudder arming only works with throttle at zero +- deadzone (RCx_DZ). Depending on vehicle type, arming in certain modes is prevented. See the wiki for each vehicle. Caution is recommended when arming if it is allowed in an auto-throttle mode!
     148             :     // @Values: 0:Disabled,1:ArmingOnly,2:ArmOrDisarm
     149             :     // @User: Advanced
     150             :     AP_GROUPINFO_FRAME("RUDDER",  6,     AP_Arming, _rudder_arming, ARMING_RUDDER_DEFAULT, AP_PARAM_FRAME_PLANE |
     151             :                                                                                            AP_PARAM_FRAME_ROVER |
     152             :                                                                                            AP_PARAM_FRAME_COPTER |
     153             :                                                                                            AP_PARAM_FRAME_TRICOPTER |
     154             :                                                                                            AP_PARAM_FRAME_HELI |
     155             :                                                                                            AP_PARAM_FRAME_BLIMP),
     156             : 
     157             :     // @Param: MIS_ITEMS
     158             :     // @DisplayName: Required mission items
     159             :     // @Description: Bitmask of mission items that are required to be planned in order to arm the aircraft
     160             :     // @Bitmask: 0:Land,1:VTOL Land,2:DO_LAND_START,3:Takeoff,4:VTOL Takeoff,5:Rallypoint,6:RTL
     161             :     // @User: Advanced
     162             :     AP_GROUPINFO("MIS_ITEMS",    7,     AP_Arming, _required_mission_items, 0),
     163             : 
     164             :     // index 8 was CHECK stored as AP_Int32, became SKIPCHK
     165             : 
     166             :     // @Param: OPTIONS
     167             :     // @DisplayName: Arming options
     168             :     // @Description: Options that can be applied to change arming behaviour
     169             :     // @Bitmask: 0:Disable prearm display,1:Do not send status text on state change,2:Skip IMU consistency checks when ICE motor running
     170             :     // @User: Advanced
     171             :     AP_GROUPINFO("OPTIONS", 9,   AP_Arming, _arming_options, 0),
     172             : 
     173             :     // @Param: MAGTHRESH
     174             :     // @DisplayName: Compass magnetic field strength error threshold vs earth magnetic model
     175             :     // @Description: Compass magnetic field strength error threshold vs earth magnetic model.  X and y axis are compared using this threhold, Z axis uses 2x this threshold.  0 to disable check
     176             :     // @Units: mGauss
     177             :     // @Range: 0 500
     178             :     // @User: Advanced
     179             :     AP_GROUPINFO("MAGTHRESH", 10, AP_Arming, magfield_error_threshold,  AP_ARMING_MAGFIELD_ERROR_THRESHOLD),
     180             : 
     181             : #if AP_ARMING_CRASHDUMP_ACK_ENABLED
     182             :     // @Param: CRSDP_IGN
     183             :     // @DisplayName: Disable CrashDump Arming check
     184             :     // @Description: Must have value "1" if crashdump data is present on the system, or a prearm failure will be raised.  Do not set this parameter unless the risks of doing so are fully understood.  The presence of a crash dump means that the firmware currently installed has suffered a critical software failure which resulted in the autopilot immediately rebooting.  The crashdump file gives diagnostic information which can help in finding the issue, please contact the ArduPIlot support team.  If this crashdump data is present, the vehicle is likely unsafe to fly.  Check the ArduPilot documentation for more details.
     185             :     // @Values: 0:Crash Dump arming check active, 1:Crash Dump arming check deactivated
     186             :     // @User: Advanced
     187             :     AP_GROUPINFO("CRSDP_IGN", 11, AP_Arming, crashdump_ack.acked, 0),
     188             : #endif  // AP_ARMING_CRASHDUMP_ACK_ENABLED
     189             : 
     190             : #if AP_ARMING_NEED_LOC_PARAMETER_ENABLED
     191             :     // @Param: NEED_LOC
     192             :     // @DisplayName: Require vehicle location
     193             :     // @Description: Require that the vehicle have an absolute position before it arms.  This can help ensure that the vehicle can Return To Launch.
     194             :     // @User: Advanced
     195             :     // @Values{Copter,Rover}: 0:Do not require location,1:Require Location
     196             :     AP_GROUPINFO("NEED_LOC", 12, AP_Arming, require_location, float(AP_ARMING_NEED_LOC_DEFAULT)),
     197             : #endif  // AP_ARMING_NEED_LOC_PARAMETER_ENABLED
     198             : 
     199             :     // @Param: SKIPCHK
     200             :     // @DisplayName: Arm Checks to Skip (bitmask)
     201             :     // @Description: Checks to skip prior to arming motor. This is a bitmask of checks before allowing arming that will be skipped. For most users it is recommended to leave this at the default of 0 (no checks skipped). In extreme circumstances, a value of -1 can be used to skip all non-mandatory current and future checks.
     202             :     // @Bitmask: 1:Barometer,2:Compass,3:GPS lock,4:INS,5:Parameters,6:RC Channels,7:Board voltage,8:Battery Level,10:Logging Available,11:Hardware safety switch,12:GPS Configuration,13:System,14:Mission,15:Rangefinder,16:Camera,17:AuxAuth,18:VisualOdometry,19:FFT
     203             :     // @Bitmask{Plane}: 1:Barometer,2:Compass,3:GPS lock,4:INS,5:Parameters,6:RC Channels,7:Board voltage,8:Battery Level,9:Airspeed,10:Logging Available,11:Hardware safety switch,12:GPS Configuration,13:System,14:Mission,15:Rangefinder,16:Camera,17:AuxAuth,19:FFT
     204             :     // @User: Standard
     205             :     AP_GROUPINFO("SKIPCHK", 13, AP_Arming, checks_to_skip, 0),
     206             : 
     207             :     AP_GROUPEND
     208             : };
     209             : 
     210             : #if HAL_WITH_IO_MCU
     211             : #include <AP_IOMCU/AP_IOMCU.h>
     212             : extern AP_IOMCU iomcu;
     213             : #endif
     214             : 
     215             : #pragma GCC diagnostic push
     216             : #if defined(__clang_major__) && __clang_major__ >= 14
     217             : #pragma GCC diagnostic ignored "-Wbitwise-instead-of-logical"
     218             : #endif
     219             : 
     220        2094 : AP_Arming::AP_Arming()
     221             : {
     222        2094 :     if (_singleton) {
     223           0 :         AP_HAL::panic("Too many AP_Arming instances");
     224             :     }
     225        2094 :     _singleton = this;
     226             : 
     227        2094 :     AP_Param::setup_object_defaults(this, var_info);
     228        2094 : }
     229             : 
     230        2086 : __INITFUNC__ void AP_Arming::init(void)
     231             : {
     232             :     // PARAMETER_CONVERSION - Added: Dec-2025 for ArduPilot-4.7
     233             :     // ARMING_CHECK -> ARMING_SKIPCHK
     234             : 
     235        2086 :     if (!checks_to_skip.configured()) {
     236             :         // new parameter is not configured (though it may be set non-zero in a
     237             :         // defaults table e.g. on Sub)
     238         323 :         int32_t skipchk_new = checks_to_skip.get(); // get param default
     239             : 
     240         323 :         uint32_t idx = 8;
     241             : #if APM_BUILD_TYPE(APM_BUILD_ArduPlane)
     242         123 :         idx <<= 6; // the old index is shifted due to AP_NESTEDGROUPINFO in AP_Arming_Plane.cpp
     243             : #endif
     244             : 
     245             :         int32_t checks_old;
     246         323 :         if (AP_Param::get_param_by_index(this, idx, AP_PARAM_INT32, &checks_old)) {
     247             :             // the old parameter was customized
     248           0 :             if (checks_old == 0) { // all checks disabled?
     249           0 :                 skipchk_new = -1; // skip all current and future checks
     250           0 :             } else if (!(checks_old & 1)) { // ALL flag not set?
     251             :                 // mask of known checks at the time the conversion was written
     252           0 :                 uint32_t check_mask = ((1U << 21) - 1) & (~1); // remove ALL bit
     253             :                 // invert bits to get checks to skip
     254           0 :                 skipchk_new = (~checks_old) & check_mask;
     255             :             } else {
     256           0 :                 skipchk_new = 0; // specifically enable all checks, ignoring param default
     257             :             }
     258             :         }
     259             : 
     260         323 :         checks_to_skip.set_and_save(skipchk_new); // set and save to finish migration
     261             :     }
     262        2086 : }
     263             : 
     264             : // performs pre-arm checks. expects to be called at 1hz.
     265      149217 : void AP_Arming::update(void)
     266             : {
     267             : #if AP_ARMING_CRASHDUMP_ACK_ENABLED
     268             :     // if we boot with no crashdump data present, reset the "ignore"
     269             :     // parameter so the user will need to acknowledge future crashes
     270             :     // too:
     271             :     crashdump_ack.check_reset();
     272             : #endif
     273             : 
     274      149217 :     const uint32_t now_ms = AP_HAL::millis();
     275             :     // perform pre-arm checks & display failures every 30 seconds
     276      149217 :     bool display_fail = false;
     277      149217 :     if ((report_immediately && (now_ms - last_prearm_display_ms > 4000)) ||
     278      148858 :         (now_ms - last_prearm_display_ms > PREARM_DISPLAY_PERIOD*1000)) {
     279        4599 :         report_immediately = false;
     280        4599 :         display_fail = true;
     281        4599 :         last_prearm_display_ms = now_ms;
     282             :     }
     283             :     // OTOH, the user may never want to display them:
     284      149217 :     if (option_enabled(Option::DISABLE_PREARM_DISPLAY)) {
     285           0 :         display_fail = false;
     286             :     }
     287             : 
     288      149217 :     pre_arm_checks(display_fail);
     289      149217 : }
     290             : 
     291             : #if AP_ARMING_CRASHDUMP_ACK_ENABLED
     292             : void AP_Arming::CrashDump::check_reset()
     293             : {
     294             :     // if there is no crash dump data then clear the crash dump ack.
     295             :     // This means on subsequent crash-dumps appearing the user must
     296             :     // re-acknowledge.
     297             :     if (hal.util->last_crash_dump_size() == 0) {
     298             :         // no crash dump data
     299             :         acked.set_and_save_ifchanged(0);
     300             :     }
     301             : }
     302             : #endif  // AP_ARMING_CRASHDUMP_ACK_ENABLED
     303             : 
     304        6015 : uint16_t AP_Arming::compass_magfield_expected() const
     305             : {
     306        6015 :     return AP_ARMING_COMPASS_MAGFIELD_EXPECTED;
     307             : }
     308             : 
     309    76192184 : bool AP_Arming::is_armed() const
     310             : {
     311    76192184 :     return armed || arming_required() == Required::NO;
     312             : }
     313             : 
     314             : /*
     315             :   true if armed and safety is off
     316             :  */
     317    50621943 : bool AP_Arming::is_armed_and_safety_off() const
     318             : {
     319    50621943 :     return is_armed() && hal.util->safety_switch_state() != AP_HAL::Util::SAFETY_DISARMED;
     320             : }
     321             : 
     322     1258475 : uint32_t AP_Arming::get_enabled_checks() const
     323             : {
     324             :     // ignore unknown checks
     325     1258475 :     uint32_t check_mask = (uint32_t(Check::CHECK_LAST)-1) & (~1); // remove former ALL bit
     326     1258475 :     return (~checks_to_skip) & check_mask;
     327             : }
     328             : 
     329     4003307 : bool AP_Arming::check_enabled(const AP_Arming::Check check) const
     330             : {
     331     4003307 :     return (checks_to_skip & uint32_t(check)) == 0;
     332             : }
     333             : 
     334           0 : bool AP_Arming::all_checks_enabled() const
     335             : {
     336             :     // ignore unknown checks
     337           0 :     uint32_t check_mask = (uint32_t(Check::CHECK_LAST)-1) & (~1); // remove former ALL bit
     338           0 :     return (checks_to_skip & check_mask) == 0;
     339             : }
     340             : 
     341      243025 : void AP_Arming::check_failed(const AP_Arming::Check check, bool report, const char *fmt, ...) const
     342             : {
     343      243025 :     if (!report) {
     344       43055 :         return;
     345             :     }
     346             :     char taggedfmt[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN+1];
     347             : 
     348             :     // metafmt is wrapped around the passed-in format string to
     349             :     // prepend "PreArm" or "Arm", depending on what sorts of checks
     350             :     // we're currently doing.
     351      199970 :     const char *metafmt = "PreArm: %s";  // it's formats all the way down
     352      199970 :     if (running_arming_checks) {
     353          43 :         metafmt = "Arm: %s";
     354             :     }
     355      199970 :     hal.util->snprintf(taggedfmt, sizeof(taggedfmt), metafmt, fmt);
     356             : 
     357             : #if HAL_GCS_ENABLED
     358      199970 :     MAV_SEVERITY severity = MAV_SEVERITY_CRITICAL;
     359      199970 :     if (!check_enabled(check)) {
     360             :         // technically should be NOTICE, but will annoy users at that level:
     361           0 :         severity = MAV_SEVERITY_DEBUG;
     362             :     }
     363             :     va_list arg_list;
     364      199970 :     va_start(arg_list, fmt);
     365      199970 :     gcs().send_textv(severity, taggedfmt, arg_list);
     366      199970 :     va_end(arg_list);
     367             : #endif  // HAL_GCS_ENABLED
     368             : }
     369             : 
     370       13070 : void AP_Arming::check_failed(bool report, const char *fmt, ...) const
     371             : {
     372             : #if HAL_GCS_ENABLED
     373       13070 :     if (!report) {
     374       12462 :         return;
     375             :     }
     376             :     char taggedfmt[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN+1];
     377             : 
     378             :     // metafmt is wrapped around the passed-in format string to
     379             :     // prepend "PreArm" or "Arm", depending on what sorts of checks
     380             :     // we're currently doing.
     381         608 :     const char *metafmt = "PreArm: %s";  // it's formats all the way down
     382         608 :     if (running_arming_checks) {
     383          79 :         metafmt = "Arm: %s";
     384             :     }
     385         608 :     hal.util->snprintf(taggedfmt, sizeof(taggedfmt), metafmt, fmt);
     386             : 
     387             :     va_list arg_list;
     388         608 :     va_start(arg_list, fmt);
     389         608 :     gcs().send_textv(MAV_SEVERITY_CRITICAL, taggedfmt, arg_list);
     390         608 :     va_end(arg_list);
     391             : #endif  // HAL_GCS_ENABLED
     392             : }
     393             : 
     394      140495 : bool AP_Arming::barometer_checks(bool report)
     395             : {
     396             : #ifdef HAL_BARO_ALLOW_INIT_NO_BARO
     397             :     return true;
     398             : #endif
     399             : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
     400      140495 :     if (AP::sitl()->baro_count == 0) {
     401             :         // simulate no baro boards
     402           0 :         return true;
     403             :     }
     404             : #endif
     405      140495 :     if (check_enabled(Check::BARO)) {
     406      133080 :         char buffer[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN+1] {};
     407      133080 :         if (!AP::baro().arming_checks(sizeof(buffer), buffer)) {
     408          34 :             check_failed(Check::BARO, report, "Baro: %s", buffer);
     409          34 :             return false;
     410             :         }
     411             :     }
     412             : 
     413      140461 :     return true;
     414             : }
     415             : 
     416             : #if AP_AIRSPEED_ENABLED
     417      118879 : bool AP_Arming::airspeed_checks(bool report)
     418             : {
     419      118879 :     if (check_enabled(Check::AIRSPEED)) {
     420      116152 :         const AP_Airspeed *airspeed = AP_Airspeed::get_singleton();
     421      116152 :         if (airspeed == nullptr) {
     422             :             // not an airspeed capable vehicle
     423        1405 :             return true;
     424             :         }
     425      116152 :         char buffer[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN+1] {};
     426      116152 :         if (!airspeed->arming_checks(sizeof(buffer), buffer)) {
     427        1405 :             check_failed(Check::AIRSPEED, report, "Airspeed: %s", buffer);
     428        1405 :             return false;
     429             :         }
     430             :     }
     431             : 
     432      117474 :     return true;
     433             : }
     434             : #endif  // AP_AIRSPEED_ENABLED
     435             : 
     436             : #if HAL_LOGGING_ENABLED
     437      140495 : bool AP_Arming::logging_checks(bool report)
     438             : {
     439      140495 :     if (check_enabled(Check::LOGGING)) {
     440      133080 :         if (!AP::logger().logging_present()) {
     441             :             // Logging is disabled, so nothing to check.
     442           0 :             return true;
     443             :         }
     444      133080 :         if (AP::logger().logging_failed()) {
     445         138 :             check_failed(Check::LOGGING, report, "Logging failed");
     446         138 :             return false;
     447             :         }
     448      132942 :         if (!AP::logger().CardInserted()) {
     449           0 :             check_failed(Check::LOGGING, report, "No SD card");
     450           0 :             return false;
     451             :         }
     452      132942 :         if (AP::logger().in_log_download()) {
     453           2 :             check_failed(Check::LOGGING, report, "Downloading logs");
     454           2 :             return false;
     455             :         }
     456             :     }
     457      140355 :     return true;
     458             : }
     459             : #endif  // HAL_LOGGING_ENABLED
     460             : 
     461             : #if AP_INERTIALSENSOR_ENABLED
     462       64769 : bool AP_Arming::ins_accels_consistent(const AP_InertialSensor &ins)
     463             : {
     464       64769 :     const uint32_t now = AP_HAL::millis();
     465       64769 :     if (!ins.accels_consistent(accel_error_threshold)) {
     466             :         // accels are inconsistent:
     467         565 :         last_accel_pass_ms = 0;
     468         565 :         return false;
     469             :     }
     470             : 
     471       64204 :     if (last_accel_pass_ms == 0) {
     472             :         // we didn't return false above, so sensors are
     473             :         // consistent right now:
     474        1884 :         last_accel_pass_ms = now;
     475             :     }
     476             : 
     477             :     // if accels can in theory be inconsistent,
     478             :     // must pass for at least AP_ARMING_IMU_CONSISTENCY_CHECK_TIME_MS ms before we're considered consistent:
     479             : #if AP_ARMING_IMU_CONSISTENCY_CHECK_TIME_MS > 0
     480       64204 :     if (ins.get_accel_count() > 1 && now - last_accel_pass_ms < AP_ARMING_IMU_CONSISTENCY_CHECK_TIME_MS) {
     481       18360 :         return false;
     482             :     }
     483             : #endif
     484             : 
     485       45844 :     return true;
     486             : }
     487             : 
     488       64769 : bool AP_Arming::ins_gyros_consistent(const AP_InertialSensor &ins)
     489             : {
     490       64769 :     const uint32_t now = AP_HAL::millis();
     491             :     // allow for up to 5 degrees/s difference
     492       64769 :     if (!ins.gyros_consistent(5)) {
     493             :         // gyros are inconsistent:
     494         356 :         last_gyro_pass_ms = 0;
     495         356 :         return false;
     496             :     }
     497             : 
     498             :     // we didn't return false above, so sensors are
     499             :     // consistent right now:
     500       64413 :     if (last_gyro_pass_ms == 0) {
     501        1886 :         last_gyro_pass_ms = now;
     502             :     }
     503             : 
     504             :     // if gyros can in theory be inconsistent,
     505             :     // must pass for at least AP_ARMING_IMU_CONSISTENCY_CHECK_TIME_MS ms before we're considered consistent:
     506             : #if AP_ARMING_IMU_CONSISTENCY_CHECK_TIME_MS > 0
     507       64413 :     if (ins.get_gyro_count() > 1 && now - last_gyro_pass_ms < AP_ARMING_IMU_CONSISTENCY_CHECK_TIME_MS) {
     508       18402 :         return false;
     509             :     }
     510             : #endif
     511             : 
     512       46011 :     return true;
     513             : }
     514             : 
     515      140495 : bool AP_Arming::ins_checks(bool report)
     516             : {
     517      140495 :     if (check_enabled(Check::INS)) {
     518      133080 :         const AP_InertialSensor &ins = AP::ins();
     519      133080 :         if (!ins.get_gyro_health_all()) {
     520           0 :             check_failed(Check::INS, report, "Gyros not healthy");
     521       87255 :             return false;
     522             :         }
     523      133080 :         if (!ins.gyro_calibrated_ok_all()) {
     524          30 :             check_failed(Check::INS, report, "Gyros not calibrated");
     525          30 :             return false;
     526             :         }
     527      133050 :         if (!ins.get_accel_health_all()) {
     528           0 :             check_failed(Check::INS, report, "Accels not healthy");
     529           0 :             return false;
     530             :         }
     531      133050 :         if (!ins.accel_calibrated_ok_all()) {
     532       68237 :             check_failed(Check::INS, report, "3D Accel calibration needed");
     533       68237 :             return false;
     534             :         }
     535             :         
     536             :         //check if accelerometers have calibrated and require reboot
     537       64813 :         if (ins.accel_cal_requires_reboot()) {
     538          44 :             check_failed(Check::INS, report, "Accels calibrated requires reboot");
     539          44 :             return false;
     540             :         }
     541             : 
     542       64769 :         bool run_imu_consistency_check = true;
     543             : #if AP_ICENGINE_ENABLED
     544       64769 :         if (option_enabled(Option::SKIP_IMU_CONSISTENCY_ICE_RUNNING)) {
     545             :             // ICE motors can greatly disturb the IMU, so we get arming failures
     546             :             // due to gyro (and sometimes accel) inconsistency. Allow this check to be
     547             :             // disabled while the motor is running
     548           0 :             auto ice = AP::ice();
     549           0 :             if (ice != nullptr) {
     550           0 :                 const auto ice_state = ice->get_state();
     551           0 :                 if (ice_state == AP_ICEngine::ICE_STARTING || ice_state == AP_ICEngine::ICE_RUNNING) {
     552           0 :                     run_imu_consistency_check = false;
     553             :                 }
     554             :             }
     555             :         }
     556             : #endif
     557             : 
     558       64769 :         if (run_imu_consistency_check && AP_ARMING_IMU_CONSISTENCY_CHECK_TIME_MS > 0) {
     559             :             // check all accelerometers point in roughly same direction
     560       64769 :             const bool accels_consistent = ins_accels_consistent(ins);
     561             : 
     562             :             // check all gyros are giving consistent readings
     563       64769 :             const bool gyros_consistent = ins_gyros_consistent(ins);
     564             : 
     565       64769 :             if (!accels_consistent) {
     566       18925 :                 check_failed(Check::INS, report, "Accels inconsistent");
     567       18925 :                 return false;
     568             :             }
     569             : 
     570       45844 :             if (!gyros_consistent) {
     571          12 :                 check_failed(Check::INS, report, "Gyros inconsistent");
     572          12 :                 return false;
     573             :             }
     574             :         }
     575             : 
     576             :         // no arming while doing temp cal
     577       45832 :         if (ins.temperature_cal_running()) {
     578           0 :             check_failed(Check::INS, report, "temperature cal running");
     579           0 :             return false;
     580             :         }
     581             : 
     582             : #if AP_INERTIALSENSOR_BATCHSAMPLER_ENABLED
     583             :         // If Batch sampling enabled it must be initialized
     584       45832 :         if (ins.batchsampler.enabled() && !ins.batchsampler.is_initialised()) {
     585           7 :             check_failed(Check::INS, report, "Batch sampling requires reboot");
     586           7 :             return false;
     587             :         }
     588             : #endif
     589             : 
     590             :         // check if IMU gyro updates are greater than or equal to Ardupilot Loop rate
     591             :         char fail_msg[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN+1];
     592       45825 :         if (!ins.pre_arm_check_gyro_backend_rate_hz(fail_msg, ARRAY_SIZE(fail_msg))) {
     593           0 :             check_failed(Check::INS, report, "%s", fail_msg);
     594           0 :             return false;
     595             :         }
     596             :     }
     597             : 
     598             : #if HAL_GYROFFT_ENABLED
     599             :     // gyros are healthy so check the FFT
     600       53240 :     if (check_enabled(Check::FFT)) {
     601             :         // Check that the noise analyser works
     602       45825 :         AP_GyroFFT *fft = AP::fft();
     603             : 
     604             :         char fail_msg[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN+1];
     605       45825 :         if (fft != nullptr && !fft->pre_arm_check(fail_msg, ARRAY_SIZE(fail_msg))) {
     606           0 :             check_failed(Check::INS, report, "%s", fail_msg);
     607           0 :             return false;
     608             :         }
     609             :     }
     610             : #endif
     611             : 
     612       53240 :     return true;
     613             : }
     614             : #endif // AP_INERTIALSENSOR_ENABLED
     615             : 
     616      140495 : bool AP_Arming::compass_checks(bool report)
     617             : {
     618      140495 :     Compass &_compass = AP::compass();
     619             : 
     620             : #if COMPASS_CAL_ENABLED
     621             :     // check if compass is calibrating
     622      140495 :     if (_compass.is_calibrating()) {
     623        1414 :         check_failed(report, "Compass calibration running");
     624        1414 :         return false;
     625             :     }
     626             : 
     627             :     // check if compass has calibrated and requires reboot
     628      139081 :     if (_compass.compass_cal_requires_reboot()) {
     629        1024 :         check_failed(report, "Compass calibrated requires reboot");
     630        1024 :         return false;
     631             :     }
     632             : #endif
     633             : 
     634      138057 :     if (check_enabled(Check::COMPASS)) {
     635             : 
     636             :         // avoid Compass::use_for_yaw(void) as it implicitly calls healthy() which can
     637             :         // incorrectly skip the remaining checks, pass the primary instance directly
     638      133325 :         if (!_compass.use_for_yaw(0)) {
     639             :             // compass use is disabled
     640         620 :             return true;
     641             :         }
     642             : 
     643      133276 :         if (!_compass.healthy()) {
     644           3 :             check_failed(Check::COMPASS, report, "Compass %d not healthy",  _compass.get_first_usable() + 1);
     645           3 :             return false;
     646             :         }
     647             :         // check compass learning is on or offsets have been set
     648             : #if !APM_BUILD_COPTER_OR_HELI && !APM_BUILD_TYPE(APM_BUILD_Blimp)
     649             :         // check compass offsets have been set if learning is off
     650             :         // copter and blimp always require configured compasses
     651       43005 :         if (!_compass.learn_offsets_enabled())
     652             : #endif
     653             :         {
     654      133273 :             char failure_msg[100] = {};
     655      133273 :             if (!_compass.configured(failure_msg, ARRAY_SIZE(failure_msg))) {
     656         117 :                 check_failed(Check::COMPASS, report, "%s", failure_msg);
     657         117 :                 return false;
     658             :             }
     659             :         }
     660             : 
     661             :         // check for unreasonable compass offsets
     662      133156 :         const Vector3f offsets = _compass.get_offsets();
     663      133156 :         if (offsets.length() > _compass.get_offsets_max()) {
     664           0 :             check_failed(Check::COMPASS, report, "Compass offsets too high");
     665           0 :             return false;
     666             :         }
     667             : 
     668             :         // check for unreasonable mag field length
     669      133156 :         const float mag_field = _compass.get_field().length();
     670      133156 :         if (mag_field > AP_ARMING_COMPASS_MAGFIELD_MAX || mag_field < AP_ARMING_COMPASS_MAGFIELD_MIN) {
     671          49 :             check_failed(Check::COMPASS, report, "Check mag field: %4.0f, max %d, min %d", (double)mag_field, AP_ARMING_COMPASS_MAGFIELD_MAX, AP_ARMING_COMPASS_MAGFIELD_MIN);
     672          49 :             return false;
     673             :         }
     674             : 
     675             :         // check all compasses point in roughly same direction
     676      133107 :         if (!_compass.consistent()) {
     677          43 :             check_failed(Check::COMPASS, report, "Compasses inconsistent");
     678          43 :             return false;
     679             :         }
     680             : 
     681             : #if AP_AHRS_ENABLED
     682             :         // if ahrs is using compass and we have location, check mag field versus expected earth magnetic model
     683      133064 :         Location ahrs_loc;
     684      133064 :         AP_AHRS &ahrs = AP::ahrs();
     685      133064 :         if ((magfield_error_threshold > 0) && ahrs.use_compass() && ahrs.get_location(ahrs_loc)) {
     686       53058 :             const Vector3f veh_mag_field_ef = ahrs.get_rotation_body_to_ned() * _compass.get_field();
     687       53058 :             const Vector3f earth_field_mgauss = AP_Declination::get_earth_field_ga(ahrs_loc) * 1000.0;
     688       53058 :             const Vector3f diff_mgauss = veh_mag_field_ef - earth_field_mgauss;
     689       53058 :             if (MAX(fabsf(diff_mgauss.x), fabsf(diff_mgauss.y)) > magfield_error_threshold) {
     690         355 :                 check_failed(Check::COMPASS, report, "Check mag field (xy diff:%.0f>%d)",
     691         355 :                              (double)MAX(fabsf(diff_mgauss.x), (double)fabsf(diff_mgauss.y)), (int)magfield_error_threshold);
     692         359 :                 return false;
     693             :             }
     694       52703 :             if (fabsf(diff_mgauss.z) > magfield_error_threshold*2.0) {
     695           4 :                 check_failed(Check::COMPASS, report, "Check mag field (z diff:%.0f>%d)",
     696           4 :                              (double)fabsf(diff_mgauss.z), (int)magfield_error_threshold*2);
     697           4 :                 return false;
     698             :             }
     699             :         }
     700             : #endif  // AP_AHRS_ENABLED
     701             :     }
     702             : 
     703      137437 :     return true;
     704             : }
     705             : 
     706             : #if AP_GPS_ENABLED
     707       43144 : bool AP_Arming::gps_checks(bool report)
     708             : {
     709       43144 :     const AP_GPS &gps = AP::gps();
     710       43144 :     if (check_enabled(Check::GPS)) {
     711             : 
     712             :         // Any failure messages from GPS backends
     713       38456 :         char failure_msg[100] = {};
     714       38456 :         if (!AP::gps().pre_arm_checks(failure_msg, ARRAY_SIZE(failure_msg))) {
     715          31 :             if (failure_msg[0] != '\0') {
     716          31 :                 check_failed(Check::GPS, report, "%s", failure_msg);
     717             :             }
     718        9712 :             return false;
     719             :         }
     720             : 
     721       69841 :         for (uint8_t i = 0; i < gps.num_sensors(); i++) {
     722             : #if AP_GPS_BLENDED_ENABLED
     723       63896 :             if ((i != GPS_BLENDED_INSTANCE) &&
     724             : #else
     725             :             if (
     726             : #endif
     727       31879 :                     (gps.get_type(i) == AP_GPS::GPS_Type::GPS_TYPE_NONE)) {
     728          13 :                 if (gps.primary_sensor() == i) {
     729           6 :                     check_failed(Check::GPS, report, "GPS %i: primary but TYPE 0", i+1);
     730           6 :                     return false;
     731             :                 }
     732           7 :                 continue;
     733             :             }
     734             : 
     735             :             //GPS OK?
     736       32004 :             if (gps.status(i) < AP_GPS_FixType::FIX_3D) {
     737         390 :                 check_failed(Check::GPS, report, "GPS %i: Bad fix", i+1);
     738         390 :                 return false;
     739             :             }
     740             : 
     741             :             //GPS update rate acceptable
     742       31614 :             if (!gps.is_healthy(i)) {
     743         205 :                 check_failed(Check::GPS, report, "GPS %i: not healthy", i+1);
     744         205 :                 return false;
     745             :             }
     746             :         }
     747             : 
     748       37824 :         if (!AP::ahrs().home_is_set()) {
     749        9063 :             check_failed(Check::GPS, report, "AHRS: waiting for home");
     750        9063 :             return false;
     751             :         }
     752             : 
     753             :         // check GPSs are within 50m of each other and that blending is healthy
     754             :         float distance_m;
     755       28761 :         if (!gps.all_consistent(distance_m)) {
     756           0 :             check_failed(Check::GPS, report, "GPS positions differ by %4.1fm",
     757             :                          (double)distance_m);
     758           0 :             return false;
     759             :         }
     760             : 
     761             :         // check AHRS and GPS are within 10m of each other
     762       28761 :         if (gps.num_sensors() > 0) {
     763       28738 :             const Location gps_loc = gps.location();
     764       28738 :             Location ahrs_loc;
     765       28738 :             if (AP::ahrs().get_location(ahrs_loc)) {
     766       28738 :                 const float distance = gps_loc.get_distance(ahrs_loc);
     767       28738 :                 if (distance > AP_ARMING_AHRS_GPS_ERROR_MAX) {
     768          17 :                     check_failed(Check::GPS, report, "GPS and AHRS differ by %4.1fm", (double)distance);
     769          17 :                     return false;
     770             :                 }
     771             :             }
     772             :         }
     773             :     }
     774             : 
     775       33432 :     if (check_enabled(Check::GPS_CONFIG)) {
     776             :         uint8_t first_unconfigured;
     777       28744 :         if (gps.first_unconfigured_gps(first_unconfigured)) {
     778          26 :             check_failed(Check::GPS_CONFIG,
     779             :                          report,
     780             :                          "GPS %d still configuring this GPS",
     781          26 :                          first_unconfigured + 1);
     782          26 :             if (report) {
     783           1 :                 gps.broadcast_first_configuration_failure_reason();
     784             :             }
     785          26 :             return false;
     786             :         }
     787             :     }
     788             : 
     789       33406 :     return true;
     790             : }
     791             : #endif  // AP_GPS_ENABLED
     792             : 
     793             : #if AP_BATTERY_ENABLED
     794      140495 : bool AP_Arming::battery_checks(bool report)
     795             : {
     796      140495 :     if (check_enabled(Check::BATTERY)) {
     797             : 
     798      137768 :         char buffer[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN+1] {};
     799      137768 :         if (!AP::battery().arming_checks(sizeof(buffer), buffer)) {
     800         465 :             check_failed(Check::BATTERY, report, "%s", buffer);
     801         465 :             return false;
     802             :         }
     803             :      }
     804      140030 :     return true;
     805             : }
     806             : #endif  // AP_BATTERY_ENABLED
     807             : 
     808      140495 : bool AP_Arming::hardware_safety_check(bool report) 
     809             : {
     810      140495 :     if (check_enabled(Check::SWITCH)) {
     811             : 
     812             :       // check if safety switch has been pushed
     813      133080 :       if (hal.util->safety_switch_state() == AP_HAL::Util::SAFETY_DISARMED) {
     814           9 :           check_failed(Check::SWITCH, report, "Hardware safety switch");
     815           9 :           return false;
     816             :       }
     817             :     }
     818             : 
     819      140486 :     return true;
     820             : }
     821             : 
     822             : #if AP_RC_CHANNEL_ENABLED
     823        1030 : bool AP_Arming::rc_arm_checks(AP_Arming::Method method)
     824             : {
     825             :     // don't check the trims if we are in a failsafe
     826        1030 :     if (!rc().has_valid_input()) {
     827           2 :         return true;
     828             :     }
     829             : 
     830             :     // only check if we've received some form of input within the last second
     831             :     // this is a protection against a vehicle having never enabled an input
     832        1028 :     uint32_t last_input_ms = rc().last_input_ms();
     833        1028 :     if ((last_input_ms == 0) || ((AP_HAL::millis() - last_input_ms) > 1000)) {
     834           0 :         return true;
     835             :     }
     836             : 
     837        1028 :     bool check_passed = true;
     838             :     // ensure all rc channels have different functions
     839        1028 :     if (rc().duplicate_options_exist()) {
     840           1 :         check_failed(Check::PARAMETERS, true, "Duplicate Aux Switch Options");
     841           1 :         check_passed = false;
     842             :     }
     843        1028 :     if (rc().flight_mode_channel_conflicts_with_rc_option()) {
     844           0 :         check_failed(Check::PARAMETERS, true, "Mode channel and RC%d_OPTION conflict", rc().flight_mode_channel_number());
     845           0 :         check_passed = false;
     846             :     }
     847             :     {
     848        1028 :         if (!rc().option_is_enabled(RC_Channels::Option::ARMING_SKIP_CHECK_RPY)) {
     849             :             const struct {
     850             :                 const char *name;
     851             :                 const RC_Channel *channel;
     852        1028 :             } channels_to_check[] {
     853        1028 :                 { "Roll", &rc().get_roll_channel(), },
     854        1028 :                 { "Pitch", &rc().get_pitch_channel(), },
     855        1028 :                 { "Yaw", &rc().get_yaw_channel(), },
     856        2056 :             };
     857        4112 :             for (const auto &channel_to_check : channels_to_check) {
     858        3084 :                 const auto *c = channel_to_check.channel;
     859        3084 :                 if (c->get_control_in() != 0) {
     860          13 :                     if ((method != Method::RUDDER) || (c != rc().get_arming_channel())) { // ignore the yaw input channel if rudder arming
     861           0 :                         check_failed(Check::RC, true, "%s (RC%d) is not neutral", channel_to_check.name, c->ch());
     862           0 :                         check_passed = false;
     863             :                     }
     864             :                 }
     865             :             }
     866             :         }
     867             : 
     868             :         // if throttle check is enabled, require zero input
     869        1028 :         if (rc().arming_check_throttle()) {
     870         977 :             const RC_Channel *c = &rc().get_throttle_channel();
     871         977 :                 if (c->get_control_in() != 0) {
     872           0 :                     check_failed(Check::RC, true, "%s (RC%d) is not neutral", "Throttle", c->ch());
     873           0 :                     check_passed = false;
     874             :                 }
     875         977 :             c = rc().find_channel_for_option(RC_Channel::AUX_FUNC::FWD_THR);
     876         977 :             if (c != nullptr) {
     877           0 :                 uint8_t fwd_thr = c->percent_input();
     878             :                 // require channel input within 2% of minimum
     879           0 :                 if (fwd_thr > 2) {
     880           0 :                     check_failed(Check::RC, true, "VTOL Fwd Throttle is not zero");
     881           0 :                     check_passed = false;
     882             :                 }
     883             :             }
     884             :         }
     885             :     }
     886        1028 :     return check_passed;
     887             : }
     888             : 
     889      133868 : bool AP_Arming::rc_calibration_checks(bool report)
     890             : {
     891      133868 :     bool check_passed = true;
     892      133868 :     const uint8_t num_channels = RC_Channels::get_valid_channel_count();
     893     2275756 :     for (uint8_t i = 0; i < NUM_RC_CHANNELS; i++) {
     894     2141888 :         const RC_Channel *c = rc().channel(i);
     895     2141888 :         if (c == nullptr) {
     896           0 :             continue;
     897             :         }
     898     2141888 :         if (i >= num_channels && !(c->has_override())) {
     899        4732 :             continue;
     900             :         }
     901     2137156 :         const uint16_t trim = c->get_radio_trim();
     902     2137156 :         if (c->get_radio_min() > trim) {
     903           0 :             check_failed(Check::RC, report, "RC%d_MIN is greater than RC%d_TRIM", i + 1, i + 1);
     904           0 :             check_passed = false;
     905             :         }
     906     2137156 :         if (c->get_radio_max() < trim) {
     907          11 :             check_failed(Check::RC, report, "RC%d_MAX is less than RC%d_TRIM", i + 1, i + 1);
     908          11 :             check_passed = false;
     909             :         }
     910             :     }
     911             : 
     912      133868 :     return check_passed;
     913             : }
     914             : 
     915      140537 : bool AP_Arming::rc_in_calibration_check(bool report)
     916             : {
     917      140537 :     if (rc().calibrating()) {
     918           0 :         check_failed(Check::RC, report, "RC calibrating");
     919           0 :         return false;
     920             :     }
     921      140537 :     return true;
     922             : }
     923             : 
     924      140495 : bool AP_Arming::manual_transmitter_checks(bool report)
     925             : {
     926      140495 :     if (check_enabled(Check::RC)) {
     927             : 
     928      137768 :         if (AP_Notify::flags.failsafe_radio) {
     929         137 :             check_failed(Check::RC, report, "Radio failsafe on");
     930         137 :             return false;
     931             :         }
     932             : 
     933      137631 :         if (!rc_calibration_checks(report)) {
     934          10 :             return false;
     935             :         }
     936             :     }
     937             : 
     938      140348 :     return rc_in_calibration_check(report);
     939             : }
     940             : #endif  // AP_RC_CHANNEL_ENABLED
     941             : 
     942             : #if AP_MISSION_ENABLED
     943      140495 : bool AP_Arming::mission_checks(bool report)
     944             : {
     945      140495 :     AP_Mission &mission = AP::mission();
     946      140495 :     if (check_enabled(Check::MISSION) && _required_mission_items) {
     947             :         const struct MisItemTable {
     948             :           MIS_ITEM_CHECK check;
     949             :           MAV_CMD mis_item_type;
     950             :           const char *type;
     951           0 :         } misChecks[] = {
     952             :           {MIS_ITEM_CHECK_LAND,          MAV_CMD_NAV_LAND,           "land"},
     953             :           {MIS_ITEM_CHECK_VTOL_LAND,     MAV_CMD_NAV_VTOL_LAND,      "vtol land"},
     954             :           {MIS_ITEM_CHECK_DO_LAND_START, MAV_CMD_DO_LAND_START,      "do land start"},
     955             :           {MIS_ITEM_CHECK_TAKEOFF,       MAV_CMD_NAV_TAKEOFF,        "takeoff"},
     956             :           {MIS_ITEM_CHECK_VTOL_TAKEOFF,  MAV_CMD_NAV_VTOL_TAKEOFF,   "vtol takeoff"},
     957             :           {MIS_ITEM_CHECK_RETURN_TO_LAUNCH,  MAV_CMD_NAV_RETURN_TO_LAUNCH,   "RTL"},
     958             :         };
     959           0 :         for (uint8_t i = 0; i < ARRAY_SIZE(misChecks); i++) {
     960           0 :             if (_required_mission_items & misChecks[i].check) {
     961           0 :                 if (!mission.contains_item(misChecks[i].mis_item_type)) {
     962           0 :                     check_failed(Check::MISSION, report, "Missing mission item: %s", misChecks[i].type);
     963           0 :                     return false;
     964             :                 }
     965             :             }
     966             :         }
     967           0 :         if (_required_mission_items & MIS_ITEM_CHECK_RALLY) {
     968             : #if HAL_RALLY_ENABLED
     969           0 :             AP_Rally *rally = AP::rally();
     970           0 :             if (rally == nullptr) {
     971           0 :                 check_failed(Check::MISSION, report, "No rally library present");
     972           0 :                 return false;
     973             :             }
     974           0 :             Location ahrs_loc;
     975           0 :             if (!AP::ahrs().get_location(ahrs_loc)) {
     976           0 :                 check_failed(Check::MISSION, report, "Can't check rally without position");
     977           0 :                 return false;
     978             :             }
     979           0 :             RallyLocation rally_loc = {};
     980           0 :             if (!rally->find_nearest_rally_point(ahrs_loc, rally_loc)) {
     981           0 :                 check_failed(Check::MISSION, report, "No sufficiently close rally point located");
     982           0 :                 return false;
     983             :             }
     984             : #else
     985             :             check_failed(Check::MISSION, report, "No rally library present");
     986             :             return false;
     987             : #endif
     988             :         }
     989             :     }
     990             : 
     991             :     // Check there are no zero altitude takeoffs
     992             :     // Although technically valid in some very rare cases it's most likely that the user simply forgot to enter an altitude.
     993      140495 :     if (check_enabled(Check::MISSION)) {
     994      133080 :         const uint16_t num_commands = mission.num_commands();
     995      438521 :         for (uint16_t i = 1; i < num_commands; i++) {
     996      305441 :             if (!mission.is_takeoff_type_cmd(mission.get_command_id(i))) {
     997      300534 :                 continue;
     998             :             }
     999        4907 :             AP_Mission::Mission_Command cmd;
    1000        4907 :             if (!mission.read_cmd_from_storage(i, cmd)) {
    1001           0 :                 continue;
    1002             :             }
    1003        4907 :             if (cmd.content.location.alt == 0) {
    1004           0 :                 check_failed(Check::MISSION, report, "Mission: Zero takeoff altitude");
    1005           0 :                 return false;
    1006             :             }
    1007             :         }
    1008             :     }
    1009             : 
    1010             : #if AP_SDCARD_STORAGE_ENABLED
    1011      406655 :     if (check_enabled(Check::MISSION) &&
    1012      266160 :         (mission.failed_sdcard_storage() || StorageManager::storage_failed())) {
    1013           0 :         check_failed(Check::MISSION, report, "Failed to open %s", AP_MISSION_SDCARD_FILENAME);
    1014           0 :         return false;
    1015             :     }
    1016             : #endif
    1017             : 
    1018             : #if AP_VEHICLE_ENABLED
    1019             :     // do not allow arming if there are no mission items and we are in
    1020             :     // (e.g.) AUTO mode
    1021      143690 :     if (AP::vehicle()->current_mode_requires_mission() &&
    1022        3195 :         !mission.present()) {
    1023          68 :         check_failed(Check::MISSION, report, "Mode requires mission");
    1024          68 :         return false;
    1025             :     }
    1026             : #endif
    1027             : 
    1028      140427 :     return true;
    1029             : }
    1030             : #endif  // AP_MISSION_ENABLED
    1031             : 
    1032      140495 : bool AP_Arming::rangefinder_checks(bool report)
    1033             : {
    1034             : #if AP_RANGEFINDER_ENABLED
    1035      140495 :     if (check_enabled(Check::RANGEFINDER)) {
    1036      133080 :         RangeFinder *range = RangeFinder::get_singleton();
    1037      133080 :         if (range == nullptr) {
    1038          80 :             return true;
    1039             :         }
    1040             : 
    1041             :         char buffer[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN+1];
    1042      133080 :         if (!range->prearm_healthy(buffer, ARRAY_SIZE(buffer))) {
    1043          80 :             check_failed(Check::RANGEFINDER, report, "%s", buffer);
    1044          80 :             return false;
    1045             :         }
    1046             :     }
    1047             : #endif
    1048             : 
    1049      140415 :     return true;
    1050             : }
    1051             : 
    1052      140495 : bool AP_Arming::servo_checks(bool report) const
    1053             : {
    1054             : #if NUM_SERVO_CHANNELS
    1055      140495 :     bool check_passed = true;
    1056     4636335 :     for (uint8_t i = 0; i < NUM_SERVO_CHANNELS; i++) {
    1057     4495840 :         const SRV_Channel *c = SRV_Channels::srv_channel(i);
    1058     4495840 :         if (c == nullptr || c->get_function() <= SRV_Channel::k_none) {
    1059     3889595 :             continue;
    1060             :         }
    1061             : 
    1062      606245 :         const uint16_t trim = c->get_trim();
    1063      606245 :         if (c->get_output_min() > trim) {
    1064           0 :             check_failed(report, "SERVO%d_MIN is greater than SERVO%d_TRIM", i + 1, i + 1);
    1065           0 :             check_passed = false;
    1066             :         }
    1067      606245 :         if (c->get_output_max() < trim) {
    1068           0 :             check_failed(report, "SERVO%d_MAX is less than SERVO%d_TRIM", i + 1, i + 1);
    1069           0 :             check_passed = false;
    1070             :         }
    1071             : 
    1072             :         // check functions using PWM are enabled
    1073      606245 :         if (SRV_Channels::get_disabled_channel_mask() & 1U<<i) {
    1074           0 :             const SRV_Channel::Function ch_function = c->get_function();
    1075             : 
    1076             :             // motors, e-stoppable functions, neopixels and ProfiLEDs may be digital outputs and thus can be disabled
    1077             :             // scripting can use its functions as labels for LED setup
    1078           0 :             const bool disabled_ok = SRV_Channel::is_motor(ch_function) ||
    1079           0 :                                      SRV_Channel::should_e_stop(ch_function) ||
    1080           0 :                                      (ch_function >= SRV_Channel::k_LED_neopixel1 && ch_function <= SRV_Channel::k_LED_neopixel4) ||
    1081           0 :                                      (ch_function >= SRV_Channel::k_ProfiLED_1 && ch_function <= SRV_Channel::k_ProfiLED_Clock) ||
    1082           0 :                                      (ch_function >= SRV_Channel::k_scripting1 && ch_function <= SRV_Channel::k_scripting16);
    1083             : 
    1084             :             // for all other functions raise a pre-arm failure
    1085           0 :             if (!disabled_ok) {
    1086           0 :                 check_failed(report, "SERVO%u_FUNCTION=%u on disabled channel", i + 1, (unsigned)ch_function);
    1087           0 :                 check_passed = false;
    1088             :             }
    1089             :         }
    1090             :     }
    1091             : 
    1092             : #if HAL_WITH_IO_MCU
    1093             :     if (!iomcu.healthy() && AP_BoardConfig::io_enabled()) {
    1094             :         check_failed(report, "IOMCU is unhealthy");
    1095             :         check_passed = false;
    1096             :     }
    1097             : #endif
    1098             : 
    1099      140495 :     return check_passed;
    1100             : #else
    1101             :     return false;
    1102             : #endif
    1103             : }
    1104             : 
    1105      140495 : bool AP_Arming::board_voltage_checks(bool report)
    1106             : {
    1107             :     // check board voltage
    1108      140495 :     if (check_enabled(Check::VOLTAGE)) {
    1109             : #if HAL_HAVE_BOARD_VOLTAGE
    1110      137768 :         const float bus_voltage =  hal.analogin->board_voltage();
    1111      137768 :         const float vbus_min = AP_BoardConfig::get_minimum_board_voltage();
    1112      137768 :         if(((bus_voltage < vbus_min) || (bus_voltage > AP_ARMING_BOARD_VOLTAGE_MAX))) {
    1113           0 :             check_failed(Check::VOLTAGE, report, "Board (%1.1fv) out of range %1.1f-%1.1fv", (double)bus_voltage, (double)vbus_min, (double)AP_ARMING_BOARD_VOLTAGE_MAX);
    1114           0 :             return false;
    1115             :         }
    1116             : #endif // HAL_HAVE_BOARD_VOLTAGE
    1117             : 
    1118             : #if HAL_HAVE_SERVO_VOLTAGE
    1119      137768 :        const float vservo_min = AP_BoardConfig::get_minimum_servo_voltage();
    1120      137768 :         if (is_positive(vservo_min)) {
    1121           0 :             const float servo_voltage =  hal.analogin->servorail_voltage();
    1122           0 :             if (servo_voltage < vservo_min) {
    1123           0 :                 check_failed(Check::VOLTAGE, report, "Servo voltage to low (%1.2fv < %1.2fv)", (double)servo_voltage, (double)vservo_min);
    1124           0 :                 return false;
    1125             :             }
    1126             :         }
    1127             : #endif // HAL_HAVE_SERVO_VOLTAGE
    1128             :     }
    1129             : 
    1130      140495 :     return true;
    1131             : }
    1132             : 
    1133             : #if HAL_HAVE_IMU_HEATER
    1134             : bool AP_Arming::heater_min_temperature_checks(bool report)
    1135             : {
    1136             :     if (all_checks_enabled()) {
    1137             :         AP_BoardConfig *board = AP::boardConfig();
    1138             :         if (board) {
    1139             :             float temperature;
    1140             :             int8_t min_temperature;
    1141             :             if (board->get_board_heater_temperature(temperature) &&
    1142             :                 board->get_board_heater_arming_temperature(min_temperature) &&
    1143             :                 (temperature < min_temperature)) {
    1144             :                 check_failed(Check::SYSTEM, report, "heater temp low (%0.1f < %i)", temperature, min_temperature);
    1145             :                 return false;
    1146             :             }
    1147             :         }
    1148             :     }
    1149             :     return true;
    1150             : }
    1151             : #endif // HAL_HAVE_IMU_HEATER
    1152             : 
    1153             : /*
    1154             :   check base system operations
    1155             :  */
    1156      140495 : bool AP_Arming::system_checks(bool report)
    1157             : {
    1158      140495 :     char buffer[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN+1] {};
    1159             : 
    1160      140495 :     if (check_enabled(Check::SYSTEM)) {
    1161      133080 :         if (!hal.storage->healthy()) {
    1162           0 :             check_failed(Check::SYSTEM, report, "Param storage failed");
    1163           0 :             return false;
    1164             :         }
    1165             : 
    1166      133080 :         if (AP_Param::get_eeprom_full()) {
    1167           0 :             check_failed(Check::PARAMETERS, report, "parameter storage full");
    1168           0 :             return false;
    1169             :         }
    1170             :         
    1171             :         // check main loop rate is at least 90% of expected value
    1172      133080 :         const float actual_loop_rate = AP::scheduler().get_filtered_loop_rate_hz();
    1173      133080 :         const uint16_t expected_loop_rate = AP::scheduler().get_loop_rate_hz();
    1174      133080 :         const float loop_rate_pct =  actual_loop_rate / expected_loop_rate;
    1175      133080 :         if (loop_rate_pct < 0.90) {
    1176       66473 :             check_failed(Check::SYSTEM, report, "Main loop slow (%uHz < %uHz)", (unsigned)actual_loop_rate, (unsigned)expected_loop_rate);
    1177       66473 :             return false;
    1178             :         }
    1179             : 
    1180             : #if AP_TERRAIN_AVAILABLE
    1181       66607 :         const AP_Terrain *terrain = AP_Terrain::get_singleton();
    1182       66607 :         if ((terrain != nullptr) && terrain->init_failed()) {
    1183           0 :             check_failed(Check::SYSTEM, report, "Terrain out of memory");
    1184           0 :             return false;
    1185             :         }
    1186             : #endif
    1187             : #if AP_SCRIPTING_ENABLED
    1188       66607 :         const AP_Scripting *scripting = AP_Scripting::get_singleton();
    1189       66607 :         if ((scripting != nullptr) && !scripting->arming_checks(sizeof(buffer), buffer)) {
    1190          48 :             check_failed(Check::SYSTEM, report, "%s", buffer);
    1191          48 :             return false;
    1192             :         }
    1193             : #endif
    1194             : #if HAL_ADSB_ENABLED
    1195       66559 :         AP_ADSB *adsb = AP::ADSB();
    1196       66559 :         if ((adsb != nullptr) && adsb->enabled() && adsb->init_failed()) {
    1197           0 :             check_failed(Check::SYSTEM, report, "ADSB out of memory");
    1198           0 :             return false;
    1199             :         }
    1200             : #endif
    1201             :     }
    1202       73974 :     if (AP::internalerror().errors() != 0) {
    1203          40 :         AP::internalerror().errors_as_string((uint8_t*)buffer, ARRAY_SIZE(buffer));
    1204          40 :         check_failed(report, "Internal errors 0x%x l:%u %s", (unsigned int)AP::internalerror().errors(), AP::internalerror().last_error_line(), buffer);
    1205          40 :         return false;
    1206             :     }
    1207             : 
    1208       73934 :     if (!hal.gpio->arming_checks(sizeof(buffer), buffer)) {
    1209           0 :         check_failed(report, "%s", buffer);
    1210           0 :         return false;
    1211             :     }
    1212             : 
    1213       73934 :     if (check_enabled(Check::PARAMETERS)) {
    1214             : #if !AP_GPS_BLENDED_ENABLED
    1215             :         if (!blending_auto_switch_checks(report)) {
    1216             :             return false;
    1217             :         }
    1218             : #endif
    1219             : #if AP_RPM_ENABLED
    1220       66519 :         auto *rpm = AP::rpm();
    1221       66519 :         if (rpm && !rpm->arming_checks(sizeof(buffer), buffer)) {
    1222           0 :             check_failed(Check::PARAMETERS, report, "%s", buffer);
    1223           0 :             return false;
    1224             :         }
    1225             : #endif
    1226             : #if AP_RELAY_ENABLED
    1227       66519 :         auto *relay = AP::relay();
    1228       66519 :         if (relay && !relay->arming_checks(sizeof(buffer), buffer)) {
    1229           0 :             check_failed(Check::PARAMETERS, report, "%s", buffer);
    1230           0 :             return false;
    1231             :         }
    1232             : #endif
    1233             : #if HAL_PARACHUTE_ENABLED
    1234       66519 :         auto *chute = AP::parachute();
    1235       66519 :         if (chute && !chute->arming_checks(sizeof(buffer), buffer)) {
    1236          38 :             check_failed(Check::PARAMETERS, report, "%s", buffer);
    1237          38 :             return false;
    1238             :         }
    1239             : #endif
    1240             : #if HAL_BUTTON_ENABLED
    1241       66481 :         const auto &button = AP::button();
    1242       66481 :         if (!button.arming_checks(sizeof(buffer), buffer)) {
    1243           0 :             check_failed(Check::PARAMETERS, report, "%s", buffer);
    1244           0 :             return false;
    1245             :         }
    1246             : #endif
    1247             :     }
    1248             : 
    1249       73896 :     return true;
    1250             : }
    1251             : 
    1252      130880 : bool AP_Arming::terrain_database_required() const
    1253             : {
    1254             : #if AP_MISSION_ENABLED
    1255      130880 :     if (AP::mission().contains_terrain_alt_items()) {
    1256         310 :         return true;
    1257             :     }
    1258             : #endif
    1259             : #if AP_FENCE_ENABLED
    1260      130570 :     const AC_Fence* fence = AP::fence();
    1261      130570 :     if (fence != nullptr && fence->terrain_database_required()) {
    1262         189 :         return true;
    1263             :     }
    1264             : #endif
    1265      130381 :     return false;
    1266             : }
    1267             : 
    1268             : // check terrain database is fit-for-purpose
    1269      140495 : bool AP_Arming::terrain_checks(bool report) const
    1270             : {
    1271      140495 :     if (!check_enabled(Check::PARAMETERS)) {
    1272        7415 :         return true;
    1273             :     }
    1274             : 
    1275      133080 :     if (!terrain_database_required()) {
    1276      132170 :         return true;
    1277             :     }
    1278             : 
    1279             : #if AP_TERRAIN_AVAILABLE
    1280             : 
    1281         910 :     const AP_Terrain *terrain = AP_Terrain::get_singleton();
    1282         910 :     if (terrain == nullptr) {
    1283           0 :         check_failed(Check::PARAMETERS, report, "terrain disabled");
    1284           0 :         return false;
    1285             :     }
    1286             : 
    1287         910 :     if (!terrain->enabled()) {
    1288           4 :         check_failed(Check::PARAMETERS, report, "terrain disabled");
    1289           4 :         return false;
    1290             :     }
    1291             : 
    1292             :     char fail_msg[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN+1];
    1293         906 :     if (!terrain->pre_arm_checks(fail_msg, sizeof(fail_msg))) {
    1294         351 :         check_failed(Check::PARAMETERS, report, "%s", fail_msg);
    1295         351 :         return false;
    1296             :     }
    1297             : 
    1298         555 :     return true;
    1299             : 
    1300             : #else
    1301             :     check_failed(Check::PARAMETERS, report, "terrain required but disabled");
    1302             :     return false;
    1303             : #endif
    1304             : }
    1305             : 
    1306             : 
    1307             : #if HAL_PROXIMITY_ENABLED
    1308             : // check nothing is too close to vehicle
    1309      140495 : bool AP_Arming::proximity_checks(bool report) const
    1310             : {
    1311      140495 :     const AP_Proximity *proximity = AP::proximity();
    1312             :     // return true immediately if no sensor present
    1313      140495 :     if (proximity == nullptr) {
    1314       27865 :         return true;
    1315             :     }
    1316             :     char buffer[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN+1];
    1317      112630 :     if (!proximity->prearm_healthy(buffer, ARRAY_SIZE(buffer))) {
    1318         235 :         check_failed(report, "%s", buffer);
    1319         235 :         return false;
    1320             :     }
    1321      112395 :     return true;
    1322             : }
    1323             : #endif  // HAL_PROXIMITY_ENABLED
    1324             : 
    1325             : #if HAL_MAX_CAN_PROTOCOL_DRIVERS && HAL_CANMANAGER_ENABLED
    1326      140495 : bool AP_Arming::can_checks(bool report)
    1327             : {
    1328      140495 :     if (check_enabled(Check::SYSTEM)) {
    1329      133080 :         char fail_msg[100] = {};
    1330             :         (void)fail_msg; // might be left unused
    1331      133080 :         uint8_t num_drivers = AP::can().get_num_drivers();
    1332             : 
    1333      399240 :         for (uint8_t i = 0; i < num_drivers; i++) {
    1334      266160 :             switch (AP::can().get_driver_type(i)) {
    1335           0 :                 case AP_CAN::Protocol::PiccoloCAN: {
    1336             : #if AP_PICCOLOCAN_ENABLED
    1337           0 :                     AP_PiccoloCAN *ap_pcan = AP_PiccoloCAN::get_pcan(i);
    1338             : 
    1339           0 :                     if (ap_pcan != nullptr && !ap_pcan->pre_arm_check(fail_msg, ARRAY_SIZE(fail_msg))) {
    1340           0 :                         check_failed(Check::SYSTEM, report, "PiccoloCAN: %s", fail_msg);
    1341           0 :                         return false;
    1342             :                     }
    1343             : 
    1344             : #else
    1345             :                     check_failed(Check::SYSTEM, report, "PiccoloCAN not enabled");
    1346             :                     return false;
    1347             : #endif
    1348           0 :                     break;
    1349             :                 }
    1350         200 :                 case AP_CAN::Protocol::DroneCAN:
    1351             :                 {
    1352             : #if HAL_ENABLE_DRONECAN_DRIVERS
    1353         200 :                     AP_DroneCAN *ap_dronecan = AP_DroneCAN::get_dronecan(i);
    1354         200 :                     if (ap_dronecan != nullptr && !ap_dronecan->prearm_check(fail_msg, ARRAY_SIZE(fail_msg))) {
    1355           0 :                         check_failed(Check::SYSTEM, report, "DroneCAN: %s", fail_msg);
    1356           0 :                         return false;
    1357             :                     }
    1358             : #endif
    1359         200 :                     break;
    1360             :                 }
    1361           0 :                 case AP_CAN::Protocol::USD1:
    1362             :                 case AP_CAN::Protocol::TOFSenseP:
    1363             :                 case AP_CAN::Protocol::RadarCAN:
    1364             :                 case AP_CAN::Protocol::Benewake:
    1365             :                 {
    1366           0 :                     for (uint8_t j = i; j; j--) {
    1367           0 :                         if (AP::can().get_driver_type(i) == AP::can().get_driver_type(j-1)) {
    1368           0 :                             check_failed(Check::SYSTEM, report, "Same rfnd on different CAN ports");
    1369           0 :                             return false;
    1370             :                         }
    1371             :                     }
    1372           0 :                     break;
    1373             :                 }
    1374      265960 :                 case AP_CAN::Protocol::EFI_NWPMU:
    1375             :                 case AP_CAN::Protocol::None:
    1376             :                 case AP_CAN::Protocol::Scripting:
    1377             :                 case AP_CAN::Protocol::Scripting2:
    1378             :                 case AP_CAN::Protocol::KDECAN:
    1379             : 
    1380      265960 :                     break;
    1381             :             }
    1382             :         }
    1383             :     }
    1384      140495 :     return true;
    1385             : }
    1386             : #endif  // HAL_MAX_CAN_PROTOCOL_DRIVERS && HAL_CANMANAGER_ENABLED
    1387             : 
    1388             : 
    1389             : #if AP_FENCE_ENABLED
    1390      140495 : bool AP_Arming::fence_checks(bool display_failure)
    1391             : {
    1392      140495 :     const AC_Fence *fence = AP::fence();
    1393      140495 :     if (fence == nullptr) {
    1394        1702 :         return true;
    1395             :     }
    1396             : 
    1397             :     // check fence is ready
    1398             :     char fail_msg[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN+1];
    1399      138793 :     if (fence->pre_arm_check(fail_msg, ARRAY_SIZE(fail_msg))) {
    1400      138112 :         return true;
    1401             :     }
    1402             : 
    1403         681 :     check_failed(display_failure, "%s", fail_msg);
    1404             : 
    1405             : #if AP_SDCARD_STORAGE_ENABLED
    1406         681 :     if (fence->failed_sdcard_storage() || StorageManager::storage_failed()) {
    1407           0 :         check_failed(display_failure, "Failed to open fence storage");
    1408           0 :         return false;
    1409             :     }
    1410             : #endif
    1411             :     
    1412         681 :     return false;
    1413             : }
    1414             : #endif  // AP_FENCE_ENABLED
    1415             : 
    1416             : #if AP_CAMERA_RUNCAM_ENABLED
    1417      140495 : bool AP_Arming::camera_checks(bool display_failure)
    1418             : {
    1419      140495 :     if (check_enabled(Check::CAMERA)) {
    1420      133080 :         AP_RunCam *runcam = AP::runcam();
    1421      133080 :         if (runcam == nullptr) {
    1422      133064 :             return true;
    1423             :         }
    1424             : 
    1425             :         // check camera is ready
    1426             :         char fail_msg[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN+1];
    1427          16 :         if (!runcam->pre_arm_check(fail_msg, ARRAY_SIZE(fail_msg))) {
    1428           0 :             check_failed(Check::CAMERA, display_failure, "%s", fail_msg);
    1429           0 :             return false;
    1430             :         }
    1431             :     }
    1432        7431 :     return true;
    1433             : }
    1434             : #endif  // AP_CAMERA_RUNCAM_ENABLED
    1435             : 
    1436             : #if OSD_ENABLED
    1437      140495 : bool AP_Arming::osd_checks(bool display_failure) const
    1438             : {
    1439      140495 :     if (check_enabled(Check::OSD)) {
    1440             :         // if no OSD then pass
    1441      133080 :         const AP_OSD *osd = AP::osd();
    1442      133080 :         if (osd == nullptr) {
    1443        1702 :             return true;
    1444             :         }
    1445             :         // do osd checks for configuration
    1446             :         char fail_msg[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN+1];
    1447      131378 :         if (!osd->pre_arm_check(fail_msg, ARRAY_SIZE(fail_msg))) {
    1448           0 :             check_failed(Check::OSD, display_failure, "%s", fail_msg);
    1449           0 :             return false;
    1450             :         }
    1451             :    }
    1452      138793 :     return true;
    1453             : }
    1454             : #endif  // OSD_ENABLED
    1455             : 
    1456             : #if HAL_MOUNT_ENABLED
    1457      140495 : bool AP_Arming::mount_checks(bool display_failure) const
    1458             : {
    1459      140495 :     if (check_enabled(Check::CAMERA)) {
    1460      133080 :         AP_Mount *mount = AP::mount();
    1461      133080 :         if (mount == nullptr) {
    1462        1741 :             return true;
    1463             :         }
    1464      131378 :         char fail_msg[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN+1] = {};
    1465      131378 :         if (!mount->pre_arm_checks(fail_msg, sizeof(fail_msg))) {
    1466          39 :             check_failed(Check::CAMERA, display_failure, "Mount: %s", fail_msg);
    1467          39 :             return false;
    1468             :         }
    1469             :     }
    1470      138754 :     return true;
    1471             : }
    1472             : #endif  // HAL_MOUNT_ENABLED
    1473             : 
    1474             : #if AP_FETTEC_ONEWIRE_ENABLED
    1475      140495 : bool AP_Arming::fettec_checks(bool display_failure) const
    1476             : {
    1477      140495 :     const AP_FETtecOneWire *f = AP_FETtecOneWire::get_singleton();
    1478      140495 :     if (f == nullptr) {
    1479           0 :         return true;
    1480             :     }
    1481             : 
    1482             :     // check ESCs are ready
    1483             :     char fail_msg[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN+1];
    1484      140495 :     if (!f->pre_arm_check(fail_msg, ARRAY_SIZE(fail_msg))) {
    1485          44 :         check_failed(display_failure, "FETtec: %s", fail_msg);
    1486          44 :         return false;
    1487             :     }
    1488      140451 :     return true;
    1489             : }
    1490             : #endif  // AP_FETTEC_ONEWIRE_ENABLED
    1491             : 
    1492             : #if AP_ARMING_AUX_AUTH_ENABLED
    1493             : // request an auxiliary authorisation id.  This id should be used in subsequent calls to set_aux_auth_passed/failed
    1494             : // returns true on success
    1495          22 : bool AP_Arming::get_aux_auth_id(uint8_t& auth_id)
    1496             : {
    1497          22 :     WITH_SEMAPHORE(aux_auth_sem);
    1498             : 
    1499             :     // check we have enough room to allocate another id
    1500          22 :     if (aux_auth_count >= aux_auth_count_max) {
    1501           0 :         aux_auth_error = true;
    1502           0 :         return false;
    1503             :     }
    1504             : 
    1505             :     // allocate buffer for failure message
    1506          22 :     if (aux_auth_fail_msg == nullptr) {
    1507          22 :         aux_auth_fail_msg = (char *)calloc(aux_auth_str_len, sizeof(char));
    1508          22 :         if (aux_auth_fail_msg == nullptr) {
    1509           0 :             aux_auth_error = true;
    1510           0 :             return false;
    1511             :         }
    1512             :     }
    1513          22 :     auth_id = aux_auth_count;
    1514          22 :     aux_auth_count++;
    1515          22 :     return true;
    1516          22 : }
    1517             : 
    1518             : // set auxiliary authorisation passed
    1519          85 : void AP_Arming::set_aux_auth_passed(uint8_t auth_id)
    1520             : {
    1521          85 :     WITH_SEMAPHORE(aux_auth_sem);
    1522             : 
    1523             :     // sanity check auth_id
    1524          85 :     if (auth_id >= aux_auth_count) {
    1525           0 :         return;
    1526             :     }
    1527             : 
    1528          85 :     aux_auth_state[auth_id] = AuxAuthStates::AUTH_PASSED;
    1529          85 : }
    1530             : 
    1531             : // set auxiliary authorisation failed and provide failure message
    1532          67 : void AP_Arming::set_aux_auth_failed(uint8_t auth_id, const char* fail_msg)
    1533             : {
    1534          67 :     WITH_SEMAPHORE(aux_auth_sem);
    1535             : 
    1536             :     // sanity check auth_id
    1537          67 :     if (auth_id >= aux_auth_count) {
    1538           0 :         return;
    1539             :     }
    1540             : 
    1541             :     // update state
    1542          67 :     aux_auth_state[auth_id] = AuxAuthStates::AUTH_FAILED;
    1543             : 
    1544             :     // store failure message if this authoriser has the lowest auth_id
    1545          67 :     for (uint8_t i = 0; i < auth_id; i++) {
    1546           0 :         if (aux_auth_state[i] == AuxAuthStates::AUTH_FAILED) {
    1547           0 :             return;
    1548             :         }
    1549             :     }
    1550          67 :     if (aux_auth_fail_msg != nullptr) {
    1551          67 :         if (fail_msg == nullptr) {
    1552           0 :             strncpy(aux_auth_fail_msg, "Auxiliary authorisation refused", aux_auth_str_len);
    1553             :         } else {
    1554          67 :             strncpy(aux_auth_fail_msg, fail_msg, aux_auth_str_len);
    1555             :         }
    1556          67 :         aux_auth_fail_msg_source = auth_id;
    1557             :     }
    1558          67 : }
    1559             : 
    1560          97 : void AP_Arming::reset_all_aux_auths()
    1561             : {
    1562          97 :     WITH_SEMAPHORE(aux_auth_sem);
    1563             : 
    1564             :     // clear all auxiliary authorisation ids
    1565          97 :     aux_auth_count = 0;
    1566             :     // clear any previous allocation errors
    1567          97 :     aux_auth_error = false;
    1568             : 
    1569             :     // reset states for all auxiliary authorisation ids
    1570         388 :     for (uint8_t i = 0; i < aux_auth_count_max; i++) {
    1571         291 :         aux_auth_state[i] = AuxAuthStates::NO_RESPONSE;
    1572             :     }
    1573             : 
    1574             :     // free up the failure message buffer
    1575          97 :     if (aux_auth_fail_msg != nullptr) {
    1576           1 :         free(aux_auth_fail_msg);
    1577           1 :         aux_auth_fail_msg = nullptr;
    1578             :     }
    1579          97 : }
    1580             : 
    1581      140495 : bool AP_Arming::aux_auth_checks(bool display_failure)
    1582             : {
    1583             :     // handle error cases
    1584      140495 :     if (aux_auth_error) {
    1585           0 :         if (aux_auth_fail_msg == nullptr) {
    1586           0 :             check_failed(Check::AUX_AUTH, display_failure, "memory low for auxiliary authorisation");
    1587             :         } else {
    1588           0 :             check_failed(Check::AUX_AUTH, display_failure, "Too many auxiliary authorisers");
    1589             :         }
    1590           0 :         return false;
    1591             :     }
    1592             : 
    1593      140495 :     WITH_SEMAPHORE(aux_auth_sem);
    1594             : 
    1595             :     // check results for each auxiliary authorisation id
    1596      140495 :     bool some_failures = false;
    1597      140495 :     bool failure_msg_sent = false;
    1598      140495 :     bool waiting_for_responses = false;
    1599      141030 :     for (uint8_t i = 0; i < aux_auth_count; i++) {
    1600         535 :         switch (aux_auth_state[i]) {
    1601         282 :         case AuxAuthStates::NO_RESPONSE:
    1602         282 :             waiting_for_responses = true;
    1603         282 :             break;
    1604         170 :         case AuxAuthStates::AUTH_FAILED:
    1605         170 :             some_failures = true;
    1606         170 :             if (i == aux_auth_fail_msg_source) {
    1607         170 :                 check_failed(Check::AUX_AUTH, display_failure, "%s", aux_auth_fail_msg);
    1608         170 :                 failure_msg_sent = true;
    1609             :             }
    1610         170 :             break;
    1611          83 :         case AuxAuthStates::AUTH_PASSED:
    1612          83 :             break;
    1613             :         }
    1614             :     }
    1615             : 
    1616             :     // send failure or waiting message
    1617      140495 :     if (some_failures) {
    1618         170 :         if (!failure_msg_sent) {
    1619           0 :             check_failed(Check::AUX_AUTH, display_failure, "Auxiliary authorisation refused");
    1620             :         }
    1621         170 :         return false;
    1622      140325 :     } else if (waiting_for_responses) {
    1623         282 :         check_failed(Check::AUX_AUTH, display_failure, "Waiting for auxiliary authorisation");
    1624         282 :         return false;
    1625             :     }
    1626             : 
    1627             :     // if we got this far all auxiliary checks must have passed
    1628      140043 :     return true;
    1629      140495 : }
    1630             : #endif  // AP_ARMING_AUX_AUTH_ENABLED
    1631             : 
    1632             : #if HAL_GENERATOR_ENABLED
    1633      140495 : bool AP_Arming::generator_checks(bool display_failure) const
    1634             : {
    1635      140495 :     const AP_Generator *generator = AP::generator();
    1636      140495 :     if (generator == nullptr) {
    1637           0 :         return true;
    1638             :     }
    1639      140495 :     char failure_msg[100] = {};
    1640      140495 :     if (!generator->pre_arm_check(failure_msg, sizeof(failure_msg))) {
    1641         374 :         check_failed(display_failure, "Generator: %s", failure_msg);
    1642         374 :         return false;
    1643             :     }
    1644      140121 :     return true;
    1645             : }
    1646             : #endif  // HAL_GENERATOR_ENABLED
    1647             : 
    1648             : #if AP_OPENDRONEID_ENABLED
    1649             : // OpenDroneID Checks
    1650      140684 : bool AP_Arming::opendroneid_checks(bool display_failure)
    1651             : {
    1652      140684 :     auto &opendroneid = AP::opendroneid();
    1653             : 
    1654      140684 :     char failure_msg[100] {};
    1655      140684 :     if (!opendroneid.pre_arm_check(failure_msg, sizeof(failure_msg))) {
    1656           0 :         check_failed(display_failure, "OpenDroneID: %s", failure_msg);
    1657           0 :         return false;
    1658             :     }
    1659      140684 :     return true;
    1660             : }
    1661             : #endif  // AP_OPENDRONEID_ENABLED
    1662             : 
    1663             : //Check for multiple RC in serial protocols
    1664      140684 : bool AP_Arming::serial_protocol_checks(bool display_failure)
    1665             : {
    1666      140684 :     if (AP::serialmanager().have_serial(AP_SerialManager::SerialProtocol_RCIN, 1)) {
    1667           0 :        check_failed(display_failure, "Multiple SERIAL ports configured for RC input");
    1668           0 :        return false;
    1669             :     }
    1670      140684 :     char failure_msg[100] = {};
    1671      140684 :     if (!AP::serialmanager().pre_arm_checks(failure_msg, ARRAY_SIZE(failure_msg))) {
    1672           0 :         check_failed(display_failure, "%s", failure_msg);
    1673           0 :         return false;
    1674             :     }
    1675      140684 :     return true;
    1676             : }
    1677             : 
    1678             : //Check for estop
    1679      140495 : bool AP_Arming::estop_checks(bool display_failure)
    1680             : {
    1681      140495 :     if (!SRV_Channels::get_emergency_stop()) {
    1682             :        // not emergency-stopped, so no prearm failure:
    1683      140428 :        return true;
    1684             :     }
    1685             : #if AP_RC_CHANNEL_ENABLED
    1686             :     // vehicle is emergency-stopped; if this *appears* to have been done via switch then we do not fail prearms:
    1687          67 :     const RC_Channel *chan = rc().find_channel_for_option(RC_Channel::AUX_FUNC::ARM_EMERGENCY_STOP);
    1688          67 :     if (chan != nullptr) {
    1689             :         // an RC channel is configured for arm_emergency_stop option, so estop maybe activated via this switch
    1690          44 :         if (chan->get_aux_switch_pos() == RC_Channel::AuxSwitchPos::LOW) {
    1691             :             // switch is configured and is in estop position, so likely the reason we are estopped, so no prearm failure
    1692           4 :             return true;  // no prearm failure
    1693             :         }
    1694             :     }
    1695             : #endif  // AP_RC_CHANNEL_ENABLED
    1696          63 :     check_failed(display_failure,"Motors Emergency Stopped");
    1697          63 :     return false;
    1698             : }
    1699             : 
    1700      140495 : bool AP_Arming::pre_arm_checks(bool report)
    1701             : {
    1702             : #if !APM_BUILD_COPTER_OR_HELI
    1703       47779 :     if (armed || arming_required() == Required::NO) {
    1704             :         // if we are already armed or don't need any arming checks
    1705             :         // then skip the checks
    1706           0 :         return true;
    1707             :     }
    1708             : #endif
    1709             : 
    1710      140495 :     bool checks_result = hardware_safety_check(report)
    1711             : #if HAL_HAVE_IMU_HEATER
    1712             :         &  heater_min_temperature_checks(report)
    1713             : #endif
    1714             : #if AP_BARO_ENABLED
    1715      140495 :         &  barometer_checks(report)
    1716             : #endif
    1717             : #if AP_INERTIALSENSOR_ENABLED
    1718      140495 :         &  ins_checks(report)
    1719             : #endif
    1720             : #if AP_COMPASS_ENABLED
    1721      140495 :         &  compass_checks(report)
    1722             : #endif
    1723             : #if AP_GPS_ENABLED
    1724      140495 :         &  gps_checks(report)
    1725             : #endif
    1726             : #if AP_BATTERY_ENABLED
    1727      140495 :         &  battery_checks(report)
    1728             : #endif
    1729             : #if HAL_LOGGING_ENABLED
    1730      140495 :         &  logging_checks(report)
    1731             : #endif
    1732             : #if AP_RC_CHANNEL_ENABLED
    1733      140495 :         &  manual_transmitter_checks(report)
    1734             : #endif
    1735             : #if AP_MISSION_ENABLED
    1736      140495 :         &  mission_checks(report)
    1737             : #endif
    1738             : #if AP_RANGEFINDER_ENABLED
    1739      140495 :         &  rangefinder_checks(report)
    1740             : #endif
    1741      140495 :         &  servo_checks(report)
    1742      140495 :         &  board_voltage_checks(report)
    1743      140495 :         &  system_checks(report)
    1744      140495 :         &  terrain_checks(report)
    1745             : #if HAL_MAX_CAN_PROTOCOL_DRIVERS && HAL_CANMANAGER_ENABLED
    1746      140495 :         &  can_checks(report)
    1747             : #endif
    1748             : #if HAL_GENERATOR_ENABLED
    1749      140495 :         &  generator_checks(report)
    1750             : #endif
    1751             : #if HAL_PROXIMITY_ENABLED
    1752      140495 :         &  proximity_checks(report)
    1753             : #endif
    1754             : #if AP_CAMERA_RUNCAM_ENABLED
    1755      140495 :         &  camera_checks(report)
    1756             : #endif
    1757             : #if OSD_ENABLED
    1758      140495 :         &  osd_checks(report)
    1759             : #endif
    1760             : #if HAL_MOUNT_ENABLED
    1761      140495 :         &  mount_checks(report)
    1762             : #endif
    1763             : #if AP_FETTEC_ONEWIRE_ENABLED
    1764      140495 :         &  fettec_checks(report)
    1765             : #endif
    1766             : #if HAL_VISUALODOM_ENABLED
    1767      140495 :         &  visodom_checks(report)
    1768             : #endif
    1769             : #if AP_ARMING_AUX_AUTH_ENABLED
    1770      140495 :         &  aux_auth_checks(report)
    1771             : #endif
    1772             : #if AP_RC_CHANNEL_ENABLED
    1773      140495 :         &  disarm_switch_checks(report)
    1774             : #endif
    1775             : #if AP_FENCE_ENABLED
    1776      140495 :         &  fence_checks(report)
    1777             : #endif
    1778             : #if AP_OPENDRONEID_ENABLED
    1779      140495 :         &  opendroneid_checks(report)
    1780             : #endif
    1781             : #if AP_ARMING_CRASHDUMP_ACK_ENABLED
    1782             :         & crashdump_checks(report)
    1783             : #endif
    1784      140495 :         &  serial_protocol_checks(report)
    1785      140495 :         &  estop_checks(report);
    1786             : 
    1787      140495 :     if (!checks_result && last_prearm_checks_result) { // check went from true to false
    1788         430 :         report_immediately = true;
    1789             :     }
    1790      140495 :     last_prearm_checks_result = checks_result;
    1791             : 
    1792      140495 :     return checks_result;
    1793             : }
    1794             : 
    1795        1034 : bool AP_Arming::arm_checks(AP_Arming::Method method)
    1796             : {
    1797             : #if AP_RC_CHANNEL_ENABLED
    1798        1034 :     if (check_enabled(Check::RC)) {
    1799        1030 :         if (!rc_arm_checks(method)) {
    1800           1 :             return false;
    1801             :         }
    1802             :     }
    1803             : #endif
    1804             : 
    1805             :     // Run estop check again, here in the arm checks there is no need
    1806             :     // bypass the check if arm emergency stop aux function is setup
    1807        1033 :     if (SRV_Channels::get_emergency_stop()) {
    1808           0 :         check_failed(true, "Motors Emergency Stopped");
    1809           0 :         return false;
    1810             :     }
    1811             : 
    1812             :     // ensure the GPS drivers are ready on any final changes
    1813        1033 :     if (check_enabled(Check::GPS_CONFIG)) {
    1814         983 :         if (!AP::gps().prepare_for_arming()) {
    1815           0 :             return false;
    1816             :         }
    1817             :     }
    1818             : 
    1819             :     // note that this will prepare AP_Logger to start logging
    1820             :     // so should be the last check to be done before arming
    1821             : 
    1822             :     // Note also that we need to PrepForArming() regardless of whether
    1823             :     // the arming check flag is set - disabling the arming check
    1824             :     // should not stop logging from working.
    1825             : 
    1826             : #if HAL_LOGGING_ENABLED
    1827        1033 :     AP_Logger *logger = AP_Logger::get_singleton();
    1828        1033 :     if (logger->logging_present()) {
    1829             :         // If we're configured to log, prep it
    1830        1033 :         logger->PrepForArming();
    1831        1033 :         if (!logger->logging_started() &&
    1832           0 :             check_enabled(Check::LOGGING)) {
    1833           0 :             check_failed(Check::LOGGING, true, "Logging not started");
    1834           0 :             return false;
    1835             :         }
    1836             :     }
    1837             : #endif  // HAL_LOGGING_ENABLED
    1838             : 
    1839        1033 :     return true;
    1840             : }
    1841             : 
    1842             : #if !AP_GPS_BLENDED_ENABLED
    1843             : bool AP_Arming::blending_auto_switch_checks(bool report)
    1844             : {
    1845             :     if (AP::gps().get_auto_switch_type() == 2) {
    1846             :         if (report) {
    1847             :             check_failed(Check::GPS, true, "GPS_AUTO_SWITCH==2 but no blending");
    1848             :         }
    1849             :         return false;
    1850             :     }
    1851             :     return true;
    1852             : }
    1853             : #endif
    1854             : 
    1855             : #if AP_ARMING_CRASHDUMP_ACK_ENABLED
    1856             : bool AP_Arming::crashdump_checks(bool report)
    1857             : {
    1858             :     if (hal.util->last_crash_dump_size() == 0) {
    1859             :         // no crash dump data
    1860             :         return true;
    1861             :     }
    1862             : 
    1863             :     // see if the user has acknowledged the failure and wants to fly anyway:
    1864             :     if (crashdump_ack.acked) {
    1865             :         // they may have acked the problem, that doesn't mean we don't
    1866             :         // continue to warn them they're on thin ice:
    1867             :         if (report) {
    1868             :             GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "CrashDump data detected");
    1869             :         }
    1870             :         return true;
    1871             :     }
    1872             : 
    1873             :     check_failed(Check::PARAMETERS, report, "CrashDump data detected");
    1874             : 
    1875             :     return false;
    1876             : }
    1877             : #endif  // AP_ARMING_CRASHDUMP_ACK_ENABLED
    1878             : 
    1879         189 : bool AP_Arming::mandatory_checks(bool report)
    1880             : {
    1881         189 :     bool ret = true;
    1882             : #if AP_OPENDRONEID_ENABLED
    1883         189 :     ret &= opendroneid_checks(report);
    1884             : #endif
    1885         189 :     ret &= rc_in_calibration_check(report);
    1886         189 :     ret &= serial_protocol_checks(report);
    1887         189 :     return ret;
    1888             : }
    1889             : 
    1890             : //returns true if arming occurred successfully
    1891        1247 : bool AP_Arming::arm(AP_Arming::Method method, const bool do_arming_checks)
    1892             : {
    1893        1247 :     if (armed) { //already armed
    1894           6 :         return false;
    1895             :     }
    1896             : 
    1897        1241 :     if (method == Method::RUDDER) {
    1898          54 :         switch (get_rudder_arming_type()) {
    1899           0 :         case AP_Arming::RudderArming::IS_DISABLED:
    1900             :             //parameter disallows rudder arming/disabling
    1901           0 :             return false;
    1902          54 :         case AP_Arming::RudderArming::ARMONLY:
    1903             :         case AP_Arming::RudderArming::ARMDISARM:
    1904          54 :             break;
    1905             :         }
    1906             :     }
    1907             : 
    1908        1241 :     running_arming_checks = true;  // so we show Arm: rather than Disarm: in messages
    1909             : 
    1910        1241 :     if ((!do_arming_checks && mandatory_checks(true)) || (pre_arm_checks(true) && arm_checks(method))) {
    1911        1127 :         armed = true;
    1912        1127 :         last_arm_time_us = AP_HAL::micros64();
    1913             : 
    1914        1127 :         _last_arm_method = method;
    1915             : 
    1916             : #if HAL_LOGGING_ENABLED
    1917        1127 :         Log_Write_Arm(!do_arming_checks, method); // note Log_Write_Armed takes forced not do_arming_checks
    1918             : #endif
    1919             : 
    1920             :     } else {
    1921             : #if HAL_LOGGING_ENABLED
    1922         114 :         AP::logger().arming_failure();
    1923             : #endif
    1924         114 :         armed = false;
    1925             :     }
    1926             : 
    1927        1241 :     running_arming_checks = false;
    1928             : 
    1929        1241 :     if (armed && do_arming_checks && should_skip_all_checks()) {
    1930           1 :         GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Warning: Arming Checks Disabled");
    1931             :     }
    1932             :     
    1933             : #if HAL_GYROFFT_ENABLED
    1934             :     // make sure the FFT subsystem is enabled if arming checks have been disabled
    1935        1241 :     AP_GyroFFT *fft = AP::fft();
    1936        1241 :     if (fft != nullptr) {
    1937        1241 :         fft->prepare_for_arming();
    1938             :     }
    1939             : #endif
    1940             : 
    1941             : #if AP_TERRAIN_AVAILABLE
    1942        1241 :     if (armed) {
    1943             :         // tell terrain we have just armed, so it can setup
    1944             :         // a reference location for terrain adjustment
    1945        1127 :         auto *terrain = AP::terrain();
    1946        1127 :         if (terrain != nullptr) {
    1947        1014 :             terrain->set_reference_location();
    1948             :         }
    1949             :     }
    1950             : #endif
    1951             : 
    1952             : #if AP_FENCE_ENABLED
    1953        1241 :     if (armed) {
    1954        1127 :         auto *fence = AP::fence();
    1955        1127 :         if (fence != nullptr) {
    1956        1127 :             fence->auto_enable_fence_on_arming();
    1957             :         }
    1958             :     }
    1959             : #endif
    1960             : #if defined(HAL_ARM_GPIO_PIN)
    1961             :     update_arm_gpio();
    1962             : #endif
    1963        1241 :     return armed;
    1964             : }
    1965             : 
    1966             : //returns true if disarming occurred successfully
    1967        1417 : bool AP_Arming::disarm(const AP_Arming::Method method, bool do_disarm_checks)
    1968             : {
    1969        1417 :     if (!armed) { // already disarmed
    1970         310 :         return false;
    1971             :     }
    1972        1107 :     if (method == AP_Arming::Method::RUDDER) {
    1973             :         // if throttle is not down, then pilot cannot rudder arm/disarm
    1974          10 :         if (rc().get_throttle_channel().get_control_in() > 0) {
    1975           4 :             return false;
    1976             :         }
    1977             :         // option must be enabled:
    1978           6 :         if (get_rudder_arming_type() != AP_Arming::RudderArming::ARMDISARM) {
    1979           0 :             gcs().send_text(MAV_SEVERITY_INFO, "Disarm: rudder disarm disabled");
    1980           0 :             return false;
    1981             :         }
    1982             :     }
    1983        1103 :     armed = false;
    1984        1103 :     _last_disarm_method = method;
    1985             : 
    1986             : #if HAL_LOGGING_ENABLED
    1987        1103 :     Log_Write_Disarm(!do_disarm_checks, method);  // Log_Write_Disarm takes "force"
    1988             : 
    1989        1103 :     check_forced_logging(method);
    1990             : #endif
    1991             : 
    1992             : #if HAL_HAVE_SAFETY_SWITCH
    1993        1103 :     AP_BoardConfig *board_cfg = AP_BoardConfig::get_singleton();
    1994        2206 :     if ((board_cfg != nullptr) &&
    1995        1103 :         (board_cfg->get_safety_button_options() & AP_BoardConfig::BOARD_SAFETY_OPTION_SAFETY_ON_DISARM)) {
    1996           0 :         hal.rcout->force_safety_on();
    1997             :     }
    1998             : #endif // HAL_HAVE_SAFETY_SWITCH
    1999             : 
    2000             : #if AP_COMPASS_LEARN_COPY_FROM_EKF_ENABLED
    2001             :     // save any compass offsets the EKF has learned.  This must be done
    2002             :     // before the vehicle calls hal.util->set_soft_armed(false); once the
    2003             :     // EKF sees onGround it clears finalInflightMagInit and will no
    2004             :     // longer hand out learned offsets.
    2005        1103 :     AP::compass().save_ekf_learned_offsets();
    2006             : #endif  // AP_COMPASS_LEARN_COPY_FROM_EKF_ENABLED
    2007             : 
    2008             : #if HAL_GYROFFT_ENABLED
    2009        1103 :     AP_GyroFFT *fft = AP::fft();
    2010        1103 :     if (fft != nullptr) {
    2011        1103 :         fft->save_params_on_disarm();
    2012             :     }
    2013             : #endif
    2014             : 
    2015             : #if AP_FENCE_ENABLED
    2016        1103 :     AC_Fence *fence = AP::fence();
    2017        1103 :     if (fence != nullptr) {
    2018        1103 :         fence->auto_disable_fence_on_disarming();
    2019             :     }
    2020             : #endif
    2021             : #if defined(HAL_ARM_GPIO_PIN)
    2022             :     update_arm_gpio();
    2023             : #endif
    2024        1103 :     return true;
    2025             : }
    2026             : 
    2027             : #if defined(HAL_ARM_GPIO_PIN)
    2028             : void AP_Arming::update_arm_gpio()
    2029             : {
    2030             :     if (!AP_BoardConfig::arming_gpio_disabled()) {
    2031             :         hal.gpio->write(HAL_ARM_GPIO_PIN, HAL_ARM_GPIO_POL_INVERT ? !armed : armed);
    2032             :     }
    2033             : }
    2034             : #endif
    2035             : 
    2036        2230 : void AP_Arming::send_arm_disarm_statustext(const char *str) const
    2037             : {
    2038        2230 :     if (option_enabled(AP_Arming::Option::DISABLE_STATUSTEXT_ON_STATE_CHANGE)) {
    2039           0 :         return;
    2040             :     }
    2041        2230 :     GCS_SEND_TEXT(MAV_SEVERITY_INFO, "%s", str);
    2042             : }
    2043             : 
    2044    24742314 : AP_Arming::Required AP_Arming::arming_required() const
    2045             : {
    2046             : #if AP_OPENDRONEID_ENABLED
    2047             :     // cannot be disabled if OpenDroneID is present
    2048    24742314 :     if (AP_OpenDroneID::get_singleton() != nullptr && AP::opendroneid().enabled()) {
    2049           0 :         if (require != Required::YES_MIN_PWM && require != Required::YES_ZERO_PWM) {
    2050           0 :             return Required::YES_MIN_PWM;
    2051             :         }
    2052             :     }
    2053             : #endif
    2054    24742303 :     return require;
    2055             : }
    2056             : 
    2057             : #if AP_RC_CHANNEL_ENABLED
    2058             : // Copter and sub share the same RC input limits
    2059             : // Copter checks that min and max have been configured by default, Sub does not
    2060       95630 : bool AP_Arming::rc_checks_copter_sub(const bool display_failure, const RC_Channel *channels[4]) const
    2061             : {
    2062             :     // set rc-checks to success if RC checks are disabled
    2063       95630 :     if (!check_enabled(Check::RC)) {
    2064           9 :         return true;
    2065             :     }
    2066             : 
    2067       95621 :     bool ret = true;
    2068             : 
    2069       95621 :     const char *channel_names[] = { "Roll", "Pitch", "Throttle", "Yaw" };
    2070             : 
    2071      478105 :     for (uint8_t i=0; i<ARRAY_SIZE(channel_names);i++) {
    2072      382484 :         const RC_Channel *channel = channels[i];
    2073      382484 :         const char *channel_name = channel_names[i];
    2074             :         // check if radio has been calibrated
    2075      382484 :         if (channel->get_radio_min() > RC_Channel::RC_CALIB_MIN_LIMIT_PWM) {
    2076           0 :             check_failed(Check::RC, display_failure, "%s radio min too high", channel_name);
    2077           0 :             ret = false;
    2078             :         }
    2079      382484 :         if (channel->get_radio_max() < RC_Channel::RC_CALIB_MAX_LIMIT_PWM) {
    2080           0 :             check_failed(Check::RC, display_failure, "%s radio max too low", channel_name);
    2081           0 :             ret = false;
    2082             :         }
    2083             :     }
    2084       95621 :     return ret;
    2085             : }
    2086             : #endif  // AP_RC_CHANNEL_ENABLED
    2087             : 
    2088             : #if HAL_VISUALODOM_ENABLED
    2089             : // check visual odometry is working
    2090      140495 : bool AP_Arming::visodom_checks(bool display_failure) const
    2091             : {
    2092      140495 :     if (!check_enabled(Check::VISION)) {
    2093        7415 :         return true;
    2094             :     }
    2095             : 
    2096      133080 :     AP_VisualOdom *visual_odom = AP::visualodom();
    2097      133080 :     if (visual_odom != nullptr) {
    2098             :         char fail_msg[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN+1];
    2099      133080 :         if (!visual_odom->pre_arm_check(fail_msg, ARRAY_SIZE(fail_msg))) {
    2100          39 :             check_failed(Check::VISION, display_failure, "VisOdom: %s", fail_msg);
    2101          39 :             return false;
    2102             :         }
    2103             :     }
    2104             : 
    2105      133041 :     return true;
    2106             : }
    2107             : #endif
    2108             : 
    2109             : #if AP_RC_CHANNEL_ENABLED
    2110             : // check disarm switch is asserted
    2111      233536 : bool AP_Arming::disarm_switch_checks(bool display_failure) const
    2112             : {
    2113      233536 :     const RC_Channel *chan = rc().find_channel_for_option(RC_Channel::AUX_FUNC::DISARM);
    2114      233536 :     if (chan != nullptr &&
    2115           0 :         chan->get_aux_switch_pos() == RC_Channel::AuxSwitchPos::HIGH) {
    2116           0 :         check_failed(display_failure, "Disarm Switch on");
    2117           0 :         return false;
    2118             :     }
    2119             : 
    2120      233536 :     return true;
    2121             : }
    2122             : #endif  // AP_RC_CHANNEL_ENABLED
    2123             : 
    2124             : #if HAL_LOGGING_ENABLED
    2125        1127 : void AP_Arming::Log_Write_Arm(const bool forced, const AP_Arming::Method method)
    2126             : {
    2127        1127 :     const struct log_Arm_Disarm pkt {
    2128             :         LOG_PACKET_HEADER_INIT(LOG_ARM_DISARM_MSG),
    2129        1127 :         time_us                 : AP_HAL::micros64(),
    2130        2254 :         arm_state               : is_armed(),
    2131        1127 :         arm_checks              : get_enabled_checks(),
    2132        1127 :         forced                  : forced,
    2133             :         method                  : (uint8_t)method,
    2134        1127 :     };
    2135        1127 :     AP::logger().WriteCriticalBlock(&pkt, sizeof(pkt));
    2136        1127 :     AP::logger().Write_Event(LogEvent::ARMED);
    2137        1127 : }
    2138             : 
    2139        1103 : void AP_Arming::Log_Write_Disarm(const bool forced, const AP_Arming::Method method)
    2140             : {
    2141        1103 :     const struct log_Arm_Disarm pkt {
    2142             :         LOG_PACKET_HEADER_INIT(LOG_ARM_DISARM_MSG),
    2143        1103 :         time_us                 : AP_HAL::micros64(),
    2144        2206 :         arm_state               : is_armed(),
    2145             :         arm_checks              : 0,
    2146        1103 :         forced                  : forced,
    2147             :         method                  : (uint8_t)method
    2148        1103 :     };
    2149        1103 :     AP::logger().WriteCriticalBlock(&pkt, sizeof(pkt));
    2150        1103 :     AP::logger().Write_Event(LogEvent::DISARMED);
    2151        1103 : }
    2152             : 
    2153             : // check if we should keep logging after disarming
    2154        1103 : void AP_Arming::check_forced_logging(const AP_Arming::Method method)
    2155             : {
    2156             :     // keep logging if disarmed for a bad reason
    2157        1103 :     switch(method) {
    2158          26 :         case Method::TERMINATION:
    2159             :         case Method::CPUFAILSAFE:
    2160             :         case Method::BATTERYFAILSAFE:
    2161             :         case Method::AFS:
    2162             :         case Method::ADSBCOLLISIONACTION:
    2163             :         case Method::PARACHUTE_RELEASE:
    2164             :         case Method::CRASH:
    2165             :         case Method::FENCEBREACH:
    2166             :         case Method::RADIOFAILSAFE:
    2167             :         case Method::GCSFAILSAFE:
    2168             :         case Method::TERRRAINFAILSAFE:
    2169             :         case Method::FAILSAFE_ACTION_TERMINATE:
    2170             :         case Method::TERRAINFAILSAFE:
    2171             :         case Method::BADFLOWOFCONTROL:
    2172             :         case Method::EKFFAILSAFE:
    2173             :         case Method::GCS_FAILSAFE_SURFACEFAILED:
    2174             :         case Method::GCS_FAILSAFE_HOLDFAILED:
    2175             :         case Method::PILOT_INPUT_FAILSAFE:
    2176             :         case Method::DEADRECKON_FAILSAFE:
    2177             :         case Method::BLACKBOX:
    2178             :             // keep logging for longer if disarmed for a bad reason
    2179          26 :             AP::logger().set_long_log_persist(true);
    2180          26 :             return;
    2181             : 
    2182        1077 :         case Method::RUDDER:
    2183             :         case Method::TOYMODE:
    2184             :         case Method::MAVLINK:
    2185             :         case Method::AUXSWITCH:
    2186             :         case Method::MOTORTEST:
    2187             :         case Method::SCRIPTING:
    2188             :         case Method::SOLOPAUSEWHENLANDED:
    2189             :         case Method::LANDED:
    2190             :         case Method::MISSIONEXIT:
    2191             :         case Method::DISARMDELAY:
    2192             :         case Method::MOTORDETECTDONE:
    2193             :         case Method::TAKEOFFTIMEOUT:
    2194             :         case Method::AUTOLANDED:
    2195             :         case Method::TOYMODELANDTHROTTLE:
    2196             :         case Method::TOYMODELANDFORCE:
    2197             :         case Method::LANDING:
    2198             :         case Method::DDS:
    2199             :         case Method::AUTO_ARM_ONCE:
    2200             :         case Method::TURTLE_MODE:
    2201             :         case Method::UNKNOWN:
    2202        1077 :             AP::logger().set_long_log_persist(false);
    2203        1077 :             return;
    2204             :     }
    2205             : }
    2206             : #endif  // HAL_LOGGING_ENABLED
    2207             : 
    2208             : AP_Arming *AP_Arming::_singleton = nullptr;
    2209             : 
    2210             : /*
    2211             :  * Get the AP_Arming singleton
    2212             :  */
    2213     8916140 : AP_Arming *AP_Arming::get_singleton()
    2214             : {
    2215     8916140 :     return AP_Arming::_singleton;
    2216             : }
    2217             : 
    2218             : namespace AP {
    2219             : 
    2220     8855185 : AP_Arming &arming()
    2221             : {
    2222     8855185 :     return *AP_Arming::get_singleton();
    2223             : }
    2224             : 
    2225             : };
    2226             : 
    2227             : #pragma GCC diagnostic pop
    2228             : 
    2229             : #endif  // AP_ARMING_ENABLED

Generated by: LCOV version 1.14