635 lines
27 KiB
Lua

-- This script runs arming checks for validations that are important
-- to some, but might need to behave differently depending on the vehicle or use case
-- so we don't want to bake them into the firmware.
-- The severity of each check can be set by simply setting the value of parameters.
-- from warnings, which still allow arming to to errors failsafe which prevent arming
-- Set the value of the matching parameter to the MAV_SEVERITY of the error level you want.
--
-- New checks can optionally be added, by adding a true/false function (false = fail)
-- and adding a check to the arming_checks table.
--
-- Thanks to @yuri_rage and Peter Barker for help with the Lua and Autotests
SCRIPT_VERSION = "4.7.0-022"
SCRIPT_NAME = "Arming Checks"
SCRIPT_NAME_SHORT = "ArmCk"
REFRESH_DELAY = 500 -- wait this many milliseconds between each update loop (while not armed)
INITIAL_DELAY = 20000 -- wait 20 seconds for the AP to settle down before starting to show messages
GLOBAL_PARAM_TABLE_BASE = 170
VALUES_PARAM_TABLE_KEY = GLOBAL_PARAM_TABLE_BASE + 9
VALUES_PARAM_TABLE_PREFIX = "ARM_V_"
FIRMWARE = {GLOBAL = 0, ROVER = 1, COPTER = 2, PLANE = 3, ANTENNA = 4, SUB = 7, BLIMP = 12, HELI = 13 }
VEHICLE = {
-- these table key comments are so that a grep on PARAM_TABLE_KEY will find all the keys used by this script
-- PARAM_TABLE_KEY = 170
[FIRMWARE.GLOBAL] = {prefix = "ARM_", key = GLOBAL_PARAM_TABLE_BASE},
-- PARAM_TABLE_KEY = 171
[FIRMWARE.ROVER] = {prefix = "ARM_R_", key = GLOBAL_PARAM_TABLE_BASE + 1},
-- PARAM_TABLE_KEY = 172
[FIRMWARE.COPTER] = {prefix = "ARM_C_", key = GLOBAL_PARAM_TABLE_BASE + 2},
-- PARAM_TABLE_KEY = 173
[FIRMWARE.PLANE] = {prefix = "ARM_P_", key = GLOBAL_PARAM_TABLE_BASE + 3},
-- PARAM_TABLE_KEY = 174
[FIRMWARE.ANTENNA] = {prefix = "ARM_A_", key = GLOBAL_PARAM_TABLE_BASE + 4},
-- PARAM_TABLE_KEY = 175
[FIRMWARE.BLIMP] = {prefix = "ARM_B_", key = GLOBAL_PARAM_TABLE_BASE + 5},
-- PARAM_TABLE_KEY = 176
[FIRMWARE.HELI] = {prefix = "ARM_H_", key = GLOBAL_PARAM_TABLE_BASE + 6},
}
MAV_SEVERITY = {EMERGENCY=0, ALERT=1, CRITICAL=2, ERROR=3, WARNING=4, NOTICE=5, INFO=6, DEBUG=7, NONE=-1}
MAV_SEVERITY_TEXT = {[MAV_SEVERITY.EMERGENCY] = "emrg",
[MAV_SEVERITY.ALERT] = "alrt",
[MAV_SEVERITY.CRITICAL] = "crit",
[MAV_SEVERITY.ERROR] = "fail",
[MAV_SEVERITY.WARNING] = "warn",
[MAV_SEVERITY.NOTICE] = "note",
[MAV_SEVERITY.INFO] = "info",
[MAV_SEVERITY.DEBUG] = "debg",
}
PLANE_MODE = { AUTO = 10, TAKEOFF = 13}
-- create parameter table for general/global ARM_ parameters that will be created below.
assert(param:add_table(VEHICLE[FIRMWARE.GLOBAL].key, VEHICLE[FIRMWARE.GLOBAL].prefix, 20), string.format('could not add param table: (%d) %s ', VEHICLE[FIRMWARE.GLOBAL].key, VEHICLE[FIRMWARE.GLOBAL].prefix))
-- create parameter table for ARM_C_ parameters for copter that will be created below.
assert(param:add_table(VEHICLE[FIRMWARE.COPTER].key, VEHICLE[FIRMWARE.COPTER].prefix, 10), string.format('could not add param table: (%d) %s ', VEHICLE[FIRMWARE.COPTER].key, VEHICLE[FIRMWARE.COPTER].prefix))
-- create parameter table for ARM_P_ parameters for plane that will be created below.
assert(param:add_table(VEHICLE[FIRMWARE.PLANE].key, VEHICLE[FIRMWARE.PLANE].prefix, 10), string.format('could not add param table: (%d) %s ', VEHICLE[FIRMWARE.PLANE].key, VEHICLE[FIRMWARE.PLANE].prefix))
-- create parameter table for ARM_V_ VALUE parameters which are "reference values" used by the parameter methods
assert(param:add_table(VALUES_PARAM_TABLE_KEY, VALUES_PARAM_TABLE_PREFIX, 15), string.format('could not add param table: (%d) %s ', VALUES_PARAM_TABLE_KEY, VALUES_PARAM_TABLE_PREFIX))
local arm_auth_id = arming:get_aux_auth_id()
if arm_auth_id == nil then
gcs:send_text(MAV_SEVERITY.ERROR, string.format("%s %s Failed", SCRIPT_NAME, SCRIPT_VERSION) )
gcs:send_text(MAV_SEVERITY.ERROR, string.format("%s: get_aux_auth_id failed", SCRIPT_NAME_SHORT))
error(SCRIPT_NAME_SHORT .. ": failed to get arm auth ID")
return -- don't execute the script if we can't get an auth_id
end
---- CLASS: Arming_Check ----
local Arming_Check = {}
Arming_Check.__index = Arming_Check
setmetatable(Arming_Check, {
__call = function(cls, firmware, index, func, param_name, text, severity, passed, changed) -- constructor
local self = setmetatable({}, cls)
self.firmware = firmware -- which firmware type is this check for (0 for general/global)
self.index = index -- the index of the ardupilot parameter
self.func = func -- func() is called to validate arming
self.param_name = param_name -- the name of a ARM_ parameter overriding the severity
self.text = text -- the message on failure
self.severity = severity -- is failure a warning or hard stop?
self.passed = passed or nil
self.changed = changed or true
return self
end
})
function Arming_Check:state()
local passed_new = self.func()
if self.passed == passed_new then
self.changed = false
else
self.passed = passed_new
self.changed = true
end
return passed_new
end
-- parameters that store values used by the validation methods, rather than severities of the individual arming checks
ARM_V_ALT_LEGAL = Parameter()
local alt_legal_max = 120.0
FENCE_TYPE = Parameter("FENCE_TYPE")
FENCE_TOTAL = Parameter("FENCE_TOTAL")
FENCE_ENABLE = Parameter("FENCE_ENABLE")
FENCE_AUTOENABLE = Parameter("FENCE_AUTOENABLE")
FOLL_ENABLE = Parameter("FOLL_ENABLE")
FOLL_SYSID = Parameter("FOLL_SYSID")
RALLY_LIMIT_KM = Parameter("RALLY_LIMIT_KM")
if FWVersion:type() == FIRMWARE.COPTER then -- Copter specific paramters
RTL_ALTITUDE = Parameter("RTL_ALT_M")
RTL_CLIMB_MIN = Parameter("RTL_CLIMB_MIN_M")
elseif FWVersion:type() == FIRMWARE.PLANE then -- Plane specific Parameters
RTL_ALTITUDE = Parameter("RTL_ALTITUDE")
RTL_CLIMB_MIN = Parameter("RTL_CLIMB_MIN")
Q_ENABLE = Parameter("Q_ENABLE")
AIRSPEED_STALL = Parameter("AIRSPEED_STALL")
AIRSPEED_MIN = Parameter("AIRSPEED_MIN")
AIRSPEED_CRUISE = Parameter("AIRSPEED_CRUISE")
AIRSPEED_MAX = Parameter("AIRSPEED_MAX")
SCALING_SPEED = Parameter("SCALING_SPEED")
end
-- this is the pre 4.7 name for the SYSID parameter
---@diagnostic disable-next-line:lowercase-global
function try_sysid_thismav()
MAV_SYSID = Parameter("SYSID_THISMAV")
end
if not pcall(try_sysid_thismav) then
MAV_SYSID = Parameter("MAV_SYSID")
end
local validate -- forward definition of the validate() function
-- check that the MAVLink SYSID has been set for this vehicle
local function sysid_set()
local mav_sysid = MAV_SYSID:get()
if mav_sysid == nil or mav_sysid <= 1 then
return false
end
return true
end
-- check that if Follow is enabled, the FOLL_SYSID is set
local function foll_sysid_set()
local foll_enable = FOLL_ENABLE:get()
if foll_enable ~= 1 then
return true
end
local foll_sysid = FOLL_SYSID:get()
if foll_sysid == 0 then
return false
end
return true
end
-- check that if the FOLL_SYSID is set it's not pointing to itself (the test returns true if "good" - hence the _not_)
local function foll_sysid_not_thismav()
local foll_enable = FOLL_ENABLE:get()
if foll_enable ~= 1 then
return true
end
local mav_sysid = MAV_SYSID:get()
local foll_sysid = FOLL_SYSID:get()
if foll_sysid > 0 and mav_sysid == foll_sysid then
return false
end
return true
end
-- if FOLLOW is enabled make sure the XYZ offsets have been set
local function foll_ofs_default()
local foll_enable = FOLL_ENABLE:get()
if foll_enable ~= 1 then
return true
end
local foll_ofs_x = Parameter("FOLL_OFS_X"):get()
local foll_ofs_y = Parameter("FOLL_OFS_Y"):get()
local foll_ofs_z = Parameter("FOLL_OFS_Z"):get()
if foll_ofs_x == 0 and foll_ofs_y == 0 and foll_ofs_z == 0 then
return false
end
return true
end
-- for many use cases if MNTx_SYSID_DEFLT is set is should match the FOLL_SYSID (remember set ARM_MNTX_SYSID severity to NONE to disable this check)
local function mntx_sysid_match()
local foll_enable = FOLL_ENABLE:get()
local mnt1_enable = (Parameter("MNT1_TYPE"):get() ~= 0)
local mnt2_enable = (Parameter("MNT2_TYPE"):get() ~= 0)
if foll_enable ~= 1 then
return true
end
local foll_sysid = FOLL_SYSID:get()
if mnt1_enable then
local mnt1_sysid = Parameter("MNT1_SYSID_DFLT"):get()
if mnt1_sysid > 0 and foll_sysid ~= mnt1_sysid then
return false
end
end
if mnt2_enable then
local mnt2_sysid = Parameter("MNT2_SYSID_DFLT"):get()
if mnt2_sysid > 0 and foll_sysid ~= mnt2_sysid then
return false
end
end
return true
end
-- is the RTL altitude legal? This defaults to 120m (400') as set in ARM_ALT_LEGAL
local function rtl_altitude_legal()
-- plane uses RTL_ALTITUDE in meters, copter uses RTL_ALT_M also in meters
-- RTL_ALTITUDE will be correct for either plane or copter
if (RTL_ALTITUDE ~= nil and (RTL_ALTITUDE:get()) > alt_legal_max) then
return false
end
return true
end
-- is the Q RTL altitude (quadplane) legal? This defaults to 120m (400') as set in ARM_ALT_LEGAL
local function q_rtl_alt_legal()
-- Plane specific Parameters
if Q_ENABLE:get() ~= 1 then
return true
end
-- QuadPlane specific Paraemters
local q_rtl_alt = Parameter("Q_RTL_ALT"):get()
if q_rtl_alt > alt_legal_max then
return false
end
return true
end
-- is the RTL climb minimum legal? This defaults to 120m (400')
local function rtl_climb_legal()
if (RTL_CLIMB_MIN ~= nil) and (RTL_CLIMB_MIN:get() > alt_legal_max) then
return false
end
return true
end
-- Display a message as to whether failsafe will QLand or QRTL
local function qland_warning()
if Q_ENABLE:get() ~= 1 then
return true
end
-- Only applies to QuadPlane
local q_options = Parameter("Q_OPTIONS"):get()
local q_land_option = ((q_options & (1 << 5)) > 0)
local q_RTL_option = ((q_options & (1 << 20)) > 0)
if (not q_land_option) and (not q_RTL_option) then
return false
end
return true -- always ok if not a QuadPlane
end
local function qrtl_warning()
if Q_ENABLE:get() ~= 1 then
return true -- always ok if not a QuadPlane
end
local q_options = Parameter("Q_OPTIONS"):get()
local q_land_option = ((q_options & (1 << 5)) > 0)
local q_RTL_option = ((q_options & (1 << 20)) > 0)
if q_land_option or q_RTL_option then
return false
end
return true
end
-- A single check to ensure AIRSPEED_STALL < AIRSPEED_MIN < AIRSPEED_CRUISE < AIRSPEED_MAX
local function airspeed_check()
local airspeed_stall = AIRSPEED_STALL:get()
local airspeed_min = AIRSPEED_MIN:get()
local airspeed_cruise = AIRSPEED_CRUISE:get()
local airspeed_max = AIRSPEED_MAX:get()
if airspeed_stall > 0 then
if airspeed_min < airspeed_stall then
return false
end
end
if airspeed_cruise < airspeed_min then
return false
end
if airspeed_max < airspeed_cruise then
return false
end
return true
end
-- If AIRSPEED_STALL is set AIRSPEED_MIN should be at least 25% higher than AIRSPEED_STALL
local function stallspeed_check()
local airspeed_stall = AIRSPEED_STALL:get()
local airspeed_min = AIRSPEED_MIN:get()
if airspeed_stall > 0 then
if airspeed_min > (airspeed_stall * 1.25) then
return false
end
end
return true
end
-- From the wiki: This is the center of the speed scaling range and should be set close to the normal cruising speed of the vehicle.
local function scaling_speed_check()
local scaling_speed = SCALING_SPEED:get()
local airspeed_cruise = AIRSPEED_CRUISE:get()
if scaling_speed <= 0 or airspeed_cruise <= 0 then
return false
end
-- use a 20% range for "close"
if scaling_speed < (airspeed_cruise * 0.8) or scaling_speed > (airspeed_cruise * 1.2) then
return false
end
-- if either are not set then
return true
end
-- want to be notified if motors are emergency stopped or not
local function motors_emergency_stopped()
return not SRV_Channels:get_emergency_stop()
end
-- Logic provided by Peter Barker
-- Fences present if
-- a. there are basic fences (assume this includes min(8)/max(1) and home circle(2) fences) or
-- b. there are polygon_fences (fence type bit 2 = 4) with at least one side
local function fence_present()
local enabled_fences = FENCE_TYPE:get()
local basic_fence = (enabled_fences & (8+2+1)) ~= 0
local polygon_fence = ((enabled_fences & 4) ~= 0) and FENCE_TOTAL:get() > 0
return basic_fence or polygon_fence
end
-- check if any rally point is too far away aka further than RALLY_LIMIT_KM away from home
local function rally_ok()
if rally == nil then
return true -- rally library not loaded so we pass
end
local home_location = ahrs:get_home()
local rally_distance_max_m = RALLY_LIMIT_KM:get() * 1000.0
if rally_distance_max_m <= 0 then
return true
end
local i = 0
repeat
local rally_location = rally:get_rally_location(i)
if rally_location then
local rally_distance_m = home_location:get_distance(rally_location)
if rally_distance_m > rally_distance_max_m then
return false
end
end
i = i + 1
until rally_location == nil
return true
end
-- fail if there is a fence on the board, it must be enabled
local function geofence_present_enabled()
if not fence_present() then
return true
end
if (FENCE_ENABLE:get() == 1) or (FENCE_AUTOENABLE:get() > 0) then
return true
end
return false
end
-- Arming checks can be deleted if not required, or set the parameter to MAV_SEVERITY.NONE to avoid changing the script
-- or new arming checks added. The position/number of the check should not be changed. If you delete a check, do not renumber the rest.
-- First add checks that can apply to all vehicles.
local arming_checks = {}
--[[
// @Param: ARM_SYSID
// @DisplayName: MAV_SYSID must be set
// @Description: Check that MAV_SYSID (or SYDID_THISMAV) has been set. 3 or less to prevent arming. -1 to disable.
// @Values: -1:Disabled,0:Emergency(PreArm),1:Alert(PreArm),2:Critical(PreArm),3:Error(PreArm),4:Warning,5:Notice,6:Info,7:Debug
// @User: Standard
--]]
table.insert(arming_checks, Arming_Check(FIRMWARE.GLOBAL, 1,
sysid_set, "SYSID", "MAV_SYSID not set", MAV_SEVERITY.WARNING, false, false))
--[[
// @Param: ARM_FOLL_SYSID
// @DisplayName: FOLL_SYSID must be set
// @Description: If FOLL_ENABLE = 1, check that FOLL_SYSID has been set. 3 or less to prevent arming. -1 to disable.
// @Values: -1:Disabled,0:Emergency(PreArm),1:Alert(PreArm),2:Critical(PreArm),3:Error(PreArm),4:Warning,5:Notice,6:Info,7:Debug
// @User: Standard
--]]
table.insert(arming_checks, Arming_Check(FIRMWARE.GLOBAL, 2,
foll_sysid_set, "FOLL_SYSID", "FOLL_SYSID not set", MAV_SEVERITY.ERROR, true, false ))
--[[
// @Param: ARM_FOLL_SYSID_X
// @DisplayName: Vehicle should not follow itself
// @Description: If FOLL_ENABLE = 1, check that FOLL_SYSID is different to MAV_SYSID. 3 or less to prevent arming. -1 to disable.
// @Values: -1:Disabled,0:Emergency(PreArm),1:Alert(PreArm),2:Critical(PreArm),3:Error(PreArm),4:Warning,5:Notice,6:Info,7:Debug
// @User: Standard
--]]
table.insert(arming_checks, Arming_Check(FIRMWARE.GLOBAL, 3,
foll_sysid_not_thismav, "FOLL_SYSID_X", "FOLL_SYSID == MAV_SYSID", MAV_SEVERITY.ERROR, true, false))
--[[
// @Param: ARM_FOLL_OFS_DEF
// @DisplayName: Follow Offsets defaulted
// @Description: Follow offsets should not be left as default (zero) if FOLL_ENABLE = 1. 3 or less to prevent arming. -1 to disable.
// @Values: -1:Disabled,0:Emergency(PreArm),1:Alert(PreArm),2:Critical(PreArm),3:Error(PreArm),4:Warning,5:Notice,6:Info,7:Debug
// @User: Standard
--]]
table.insert(arming_checks, Arming_Check(FIRMWARE.GLOBAL, 4,
foll_ofs_default, "FOLL_OFS_DEF", "FOLL_OFS_[XYZ] = 0", MAV_SEVERITY.ERROR, true, false))
--[[
// @Param: ARM_MNTX_SYSID
// @DisplayName: Follow and Mount should follow the same vehicle
// @Description: If FOLL_ENABLE = 1 and MNTx_SYSID_DEFLT is set, check that FOLL_SYSID is equal MNTx. 3 or less to prevent arming. -1 to disable.
// @Values: -1:Disabled,0:Emergency(PreArm),1:Alert(PreArm),2:Critical(PreArm),3:Error(PreArm),4:Warning,5:Notice,6:Info,7:Debug
// @User: Standard
--]]
table.insert(arming_checks, Arming_Check(FIRMWARE.GLOBAL, 5,
mntx_sysid_match, "MNTX_SYSID", "MNTx_SYSID != FOLL", MAV_SEVERITY.WARNING, true, false))
--[[
// @Param: ARM_RTL_CLIMB
// @DisplayName: RTL_CLIMB_MIN should be a valid value
// @Description: RTL_CLIMB_MIN should be < 120m (400ft). 3 or less to prevent arming. -1 to disable.
// @Values: -1:Disabled,0:Emergency(PreArm),1:Alert(PreArm),2:Critical(PreArm),3:Error(PreArm),4:Warning,5:Notice,6:Info,7:Debug
// @User: Standard
--]]
table.insert(arming_checks, Arming_Check(FIRMWARE.GLOBAL, 6,
rtl_climb_legal, "RTL_CLIMB", "RTL_CLIMB_MIN too high", MAV_SEVERITY.WARNING, true, false))
--[[
// @Param: ARM_ESTOP
// @DisplayName: Motors EStopped
// @Description: Emergency Stop disables arming. 3 or less to prevent arming. -1 to disable.
// @Values: -1:Disabled,0:Emergency(PreArm),1:Alert(PreArm),2:Critical(PreArm),3:Error(PreArm),4:Warning,5:Notice,6:Info,7:Debug
// @User: Standard
--]]
table.insert(arming_checks, Arming_Check(FIRMWARE.GLOBAL, 7,
motors_emergency_stopped, "ESTOP", "Motors EStopped", MAV_SEVERITY.ERROR, true, false))
--[[
// @Param: ARM_FENCE
// @DisplayName: Fence not enabled
// @Description: Fences loaded but no fence enabled. 3 or less to prevent arming. -1 to disable.
// @Values: -1:Disabled,0:Emergency(PreArm),1:Alert(PreArm),2:Critical(PreArm),3:Error(PreArm),4:Warning,5:Notice,6:Info,7:Debug
// @User: Standard
--]]
table.insert(arming_checks, Arming_Check(FIRMWARE.GLOBAL, 8,
geofence_present_enabled, "FENCE", "Fence not enabled", MAV_SEVERITY.WARNING, true, false))
--[[
// @Param: ARM_RALLY
// @DisplayName: Rally too far
// @Description: Rally Point more than RALLY_LIMIT_KM kilometers away. 3 or less to prevent arming. -1 to disable.
// @Values: -1:Disabled,0:Emergency(PreArm),1:Alert(PreArm),2:Critical(PreArm),3:Error(PreArm),4:Warning,5:Notice,6:Info,7:Debug
// @User: Standard
--]]
table.insert(arming_checks, Arming_Check(FIRMWARE.GLOBAL, 9,
rally_ok, "RALLY", "Rally too far", MAV_SEVERITY.WARNING, true, false))
-- ArduCopter specific checks. Note that the index number starts from 1 for FIRMWARE.COPTER (ARM_C_ parameters)
if FWVersion:type() == FIRMWARE.COPTER then -- add Copter specific Parameters
--[[
// @Param: ARM_C_RTL_ALT_M
// @DisplayName: RTL_ALT_M should be a valid value
// @Description: RTL_ALT_M should be < 120m (400ft). 3 or less to prevent arming. -1 to disable.
// @Values: -1:Disabled,0:Emergency(PreArm),1:Alert(PreArm),2:Critical(PreArm),3:Error(PreArm),4:Warning,5:Notice,6:Info,7:Debug
// @User: Standard
--]]
table.insert(arming_checks, Arming_Check(FIRMWARE.COPTER, 1,
rtl_altitude_legal, "RTL_ALT", "RTL_ALT_M too high", MAV_SEVERITY.ERROR, false, false))
elseif FWVersion:type() == FIRMWARE.PLANE then -- add Plane specific Parameters
-- ArduPlane specific checks. Note that the index number starts from 1 for FIRMWARE.PLANE (ARM_P_ parameters)
--[[
// @Param: ARM_P_Q_FS_LAND
// @DisplayName: Warn if Q failsafe will land
// @Description: Notify the user that on failsafe a QuadPlan will land. 3 or less to prevent arming. -1 to disable.
// @Values: -1:Disabled,0:Emergency(PreArm),1:Alert(PreArm),2:Critical(PreArm),3:Error(PreArm),4:Warning,5:Notice,6:Info,7:Debug
// @User: Standard
--]]
table.insert(arming_checks, Arming_Check(FIRMWARE.PLANE, 1,
qland_warning, "Q_FS_LAND", "Q will land on failsafe", MAV_SEVERITY.NOTICE, true, false))
--[[
// @Param: ARM_P_Q_FS_RTL
// @DisplayName: Warn if Q failsafe will QRTL
// @Description: Notify the user that on failsafe a QuadPlan will QRTL. 3 or less to prevent arming. -1 to disable.
// @Values: -1:Disabled,0:Emergency(PreArm),1:Alert(PreArm),2:Critical(PreArm),3:Error(PreArm),4:Warning,5:Notice,6:Info,7:Debug
// @User: Standard
--]]
table.insert(arming_checks, Arming_Check(FIRMWARE.PLANE, 2,
qrtl_warning, "Q_FS_RTL", "Q will RTL on failsafe", MAV_SEVERITY.NOTICE, true, false))
--[[
// @Param: ARM_P_AIRSPEED
// @DisplayName: Check AIRSPEED_ parameters
// @Description: Validate that AIRSPEED_STALL(if set) < MIN < CRUISE < MAX d. 3 or less to prevent arming. -1 to disable.
// @Values: -1:Disabled,0:Emergency(PreArm),1:Alert(PreArm),2:Critical(PreArm),3:Error(PreArm),4:Warning,5:Notice,6:Info,7:Debug
// @User: Standard
--]]
table.insert(arming_checks, Arming_Check(FIRMWARE.PLANE, 3,
airspeed_check, "AIRSPEED", "stall < min < crs < max", MAV_SEVERITY.ERROR, true, false))
--[[
// @Param: ARM_P_STALL
// @DisplayName: AIRSPEED_MIN should be 25% above STALL
// @Description: Validate that AIRSPEED_MIN is at least 25% above AIRSPEED_STALL(if set). 3 or less to prevent arming. -1 to disable.
// @Values: -1:Disabled,0:Emergency(PreArm),1:Alert(PreArm),2:Critical(PreArm),3:Error(PreArm),4:Warning,5:Notice,6:Info,7:Debug
// @User: Standard
--]]
table.insert(arming_checks, Arming_Check(FIRMWARE.PLANE, 4,
stallspeed_check, "STALL", "Min speed not 25% above stall", MAV_SEVERITY.INFO, true, false))
--[[
// @Param: ARM_P_SCALING
// @DisplayName: SCALING_SPEED valid
// @Description: Validate that SCALING_SPEED is within 20% of AIRSPEED_CRUISE. If SCALING_SPEED changes the vehicle may need to be retuned. 3 or less to prevent arming. -1 to disable.
// @Values: -1:Disabled,0:Emergency(PreArm),1:Alert(PreArm),2:Critical(PreArm),3:Error(PreArm),4:Warning,5:Notice,6:Info,7:Debug
// @User: Standard
--]]
table.insert(arming_checks, Arming_Check(FIRMWARE.PLANE, 5,
scaling_speed_check, "SCALING", "Scaling spd >< 20% of CRUISE", MAV_SEVERITY.ERROR, true, false))
--[[
// @Param: ARM_P_RTL_ALT
// @DisplayName: RTL_ALTITUDE should be a valid value
// @Description: RTL_ALTITITUDE should be < 120m (400ft). 3 or less to prevent arming. -1 to disable.
// @Values: -1:Disabled,0:Emergency(PreArm),1:Alert(PreArm),2:Critical(PreArm),3:Error(PreArm),4:Warning,5:Notice,6:Info,7:Debug
// @User: Standard
--]]
table.insert(arming_checks, Arming_Check(FIRMWARE.PLANE, 6,
rtl_altitude_legal, "RTL_ALT", "RTL_ALTITUDE too high", MAV_SEVERITY.ERROR, false, false))
--[[
// @Param: ARM_P_QRTL_ALT
// @DisplayName: Q_RTL_ALT should be a valid value
// @Description: Q_RTL_ALT should be < 120m (400ft). 3 or less to prevent arming. -1 to disable.
// @Values: -1:Disabled,0:Emergency(PreArm),1:Alert(PreArm),2:Critical(PreArm),3:Error(PreArm),4:Warning,5:Notice,6:Info,7:Debug
// @User: Standard
--]]
table.insert(arming_checks, Arming_Check(FIRMWARE.PLANE, 7,
q_rtl_alt_legal, "QRTL_ALT", "Q_RTL_ALT too high", MAV_SEVERITY.ERROR, false, false))
end
-- No need to actively check while armed.
local function idle_while_armed()
if not arming:is_armed() then return validate, REFRESH_DELAY end
return idle_while_armed, REFRESH_DELAY * 20
end
-- initialize all the parameters based on the data in the arming_checks table
local function initialize()
-- special case for the ARV_ALT_LEGAL parameter which ideally should be a "regular" parameter
--[[
// @Param: ARM_V_ALT_LEGAL
// @DisplayName: Legal max altitude
// @Description: Legal max altitude for UAV/RPAS/drones in your jurisdiction
// @Units: m
// @User: Standard
--]]
assert(param:add_param(VALUES_PARAM_TABLE_KEY, 1, "ALT_LEGAL", 120.0),
string.format('could not add param %s', VALUES_PARAM_TABLE_PREFIX.."ALT_LEGAL"))
ARM_V_ALT_LEGAL:init("ARM_V_ALT_LEGAL")
for _, check in ipairs(arming_checks) do
local param_name = VEHICLE[check.firmware].prefix..check.param_name
assert(param:add_param(VEHICLE[check.firmware].key , check.index, check.param_name, check.severity),
string.format('could not add param %s', param_name))
end
end
-- run an indivual check
local function run_check(check, severity, validated)
local check_passed = check:state() -- sets check.passed and check.changed
local raise_prearm = not check_passed
if check.changed then
if check_passed then
-- it's passed now, but changed, so previously failed. Let the user know its all clear
gcs:send_text(MAV_SEVERITY.INFO, string.format('%s: clear: %s', SCRIPT_NAME_SHORT, check.text))
elseif severity > MAV_SEVERITY.ERROR then
-- deal with warning messages (MAV_SEVERITY > ERROR), we display a message but no prearm
gcs:send_text(severity, string.format('%s: %s: %s', SCRIPT_NAME_SHORT,
MAV_SEVERITY_TEXT[severity], check.text))
-- display the message, but this should not block arming so is actually "passed"
raise_prearm = false
end
elseif not check_passed and severity > MAV_SEVERITY.ERROR then
-- if nothing changed we only want to fail on errors
raise_prearm = false
end
if raise_prearm then
validated = false
arming:set_aux_auth_failed(arm_auth_id, string.format('%s: %s: %s', SCRIPT_NAME_SHORT,
MAV_SEVERITY_TEXT[severity], check.text))
end
return validated
end
validate = function() -- this is the loop which periodically runs to do the validations
if arming:is_armed() then return idle_while_armed() end
alt_legal_max = ARM_V_ALT_LEGAL:get() or 120.0 -- used Parameter():init() to initialize this one.
local validated = true
for _, check in ipairs(arming_checks) do
local param_name = VEHICLE[check.firmware].prefix..check.param_name
local parameter = Parameter(param_name)
local param_severity = parameter:get()
if param_severity ~= MAV_SEVERITY.NONE then -- ignore if NONE
validated = run_check(check, param_severity, validated)
end
end
if validated then
arming:set_aux_auth_passed(arm_auth_id)
end
return validate, REFRESH_DELAY
end
local function delayed_startup()
gcs:send_text(MAV_SEVERITY.INFO, string.format("%s %s loaded", SCRIPT_NAME, SCRIPT_VERSION) )
return validate, REFRESH_DELAY
end
initialize()
-- start running update loop - waiting 20s for the AP to initialize so we don't spam the user with spurios notices
return delayed_startup, INITIAL_DELAY