Path: blob/master/libraries/AP_Compass/AP_Compass.cpp
9362 views
#include "AP_Compass_config.h"12#if AP_COMPASS_ENABLED34#include <AP_HAL/AP_HAL.h>5#include <AP_HAL/I2CDevice.h>6#if CONFIG_HAL_BOARD == HAL_BOARD_LINUX7#include <AP_HAL_Linux/I2CDevice.h>8#endif9#include <AP_Vehicle/AP_Vehicle_Type.h>10#include <AP_BoardConfig/AP_BoardConfig.h>11#include <AP_Logger/AP_Logger.h>12#include <AP_Vehicle/AP_Vehicle_Type.h>13#include <AP_ExternalAHRS/AP_ExternalAHRS.h>14#include <AP_CustomRotations/AP_CustomRotations.h>15#include <GCS_MAVLink/GCS.h>16#include <AP_AHRS/AP_AHRS.h>1718#include "AP_Compass_config.h"1920#include "AP_Compass_SITL.h"21#include "AP_Compass_AK8963.h"22#include "AP_Compass_Backend.h"23#include "AP_Compass_BMM150.h"24#include "AP_Compass_BMM350.h"25#include "AP_Compass_HMC5843.h"26#include "AP_Compass_IIS2MDC.h"27#include "AP_Compass_IST8308.h"28#include "AP_Compass_IST8310.h"29#include "AP_Compass_LSM303D.h"30#include "AP_Compass_LSM9DS1.h"31#include "AP_Compass_LIS3MDL.h"32#include "AP_Compass_LIS2MDL.h"33#include "AP_Compass_AK09916.h"34#include "AP_Compass_QMC5883L.h"35#if AP_COMPASS_DRONECAN_ENABLED36#include "AP_Compass_DroneCAN.h"37#endif38#include "AP_Compass_QMC5883P.h"39#include "AP_Compass_MMC3416.h"40#include "AP_Compass_MMC5xx3.h"41#include "AP_Compass_MAG3110.h"42#include "AP_Compass_RM3100.h"43#if AP_COMPASS_MSP_ENABLED44#include "AP_Compass_MSP.h"45#endif46#if AP_COMPASS_EXTERNALAHRS_ENABLED47#include "AP_Compass_ExternalAHRS.h"48#endif49#include "AP_Compass.h"50#include "Compass_learn.h"51#include <stdio.h>5253extern const AP_HAL::HAL& hal;5455#ifndef AP_COMPASS_ENABLE_DEFAULT56#define AP_COMPASS_ENABLE_DEFAULT 157#endif5859#ifndef COMPASS_LEARN_DEFAULT60#define COMPASS_LEARN_DEFAULT Compass::LearnType::NONE61#endif6263#ifndef AP_COMPASS_OFFSETS_MAX_DEFAULT64#define AP_COMPASS_OFFSETS_MAX_DEFAULT 180065#endif6667#ifndef HAL_COMPASS_FILTER_DEFAULT68#define HAL_COMPASS_FILTER_DEFAULT 0 // turned off by default69#endif7071#ifndef HAL_COMPASS_AUTO_ROT_DEFAULT72#define HAL_COMPASS_AUTO_ROT_DEFAULT 273#endif7475const AP_Param::GroupInfo Compass::var_info[] = {76// index 0 was used for the old orientation matrix7778#ifndef HAL_BUILD_AP_PERIPH79// @Param: OFS_X80// @DisplayName: Compass offsets in milligauss on the X axis81// @Description: Offset to be added to the compass x-axis values to compensate for metal in the frame82// @Range: -400 40083// @Units: mGauss84// @Increment: 185// @User: Advanced86// @Calibration: 18788// @Param: OFS_Y89// @DisplayName: Compass offsets in milligauss on the Y axis90// @Description: Offset to be added to the compass y-axis values to compensate for metal in the frame91// @Range: -400 40092// @Units: mGauss93// @Increment: 194// @User: Advanced95// @Calibration: 19697// @Param: OFS_Z98// @DisplayName: Compass offsets in milligauss on the Z axis99// @Description: Offset to be added to the compass z-axis values to compensate for metal in the frame100// @Range: -400 400101// @Units: mGauss102// @Increment: 1103// @User: Advanced104// @Calibration: 1105AP_GROUPINFO("OFS", 1, Compass, _state._priv_instance[0].offset, 0),106107// @Param: DEC108// @DisplayName: Compass declination109// @Description: An angle to compensate between the true north and magnetic north110// @Range: -3.142 3.142111// @Units: rad112// @Increment: 0.01113// @User: Standard114AP_GROUPINFO("DEC", 2, Compass, _declination, 0),115#endif // HAL_BUILD_AP_PERIPH116117#if COMPASS_LEARN_ENABLED118// @Param: LEARN119// @DisplayName: Learn compass offsets automatically120// @Description: Enable or disable the automatic learning of compass offsets. You can enable learning either using a compass-only method that is suitable only for fixed wing aircraft or using the offsets learnt by the active EKF state estimator. If this option is enabled then the learnt offsets are saved when you disarm the vehicle. If InFlight learning is enabled then the compass with automatically start learning once a flight starts (must be armed). While InFlight learning is running you cannot use position control modes.121// @Values: 0:Disabled,2:EKF-Learning,3:InFlight-Learning122// @User: Advanced123AP_GROUPINFO("LEARN", 3, Compass, _learn, float(COMPASS_LEARN_DEFAULT)),124#endif125126#ifndef HAL_BUILD_AP_PERIPH127// @Param: USE128// @DisplayName: Use compass for yaw129// @Description: Enable or disable the use of the compass (instead of the GPS) for determining heading130// @Values: 0:Disabled,1:Enabled131// @User: Advanced132AP_GROUPINFO("USE", 4, Compass, _use_for_yaw._priv_instance[0], 1), // true if used for DCM yaw133134// @Param: AUTODEC135// @DisplayName: Auto Declination136// @Description: Enable or disable the automatic calculation of the declination based on gps location137// @Values: 0:Disabled,1:Enabled138// @User: Advanced139AP_GROUPINFO("AUTODEC",5, Compass, _auto_declination, 1),140#endif141142#if COMPASS_MOT_ENABLED143// @Param: MOTCT144// @DisplayName: Motor interference compensation type145// @Description: Set motor interference compensation type to disabled, throttle or current. Do not change manually.146// @Values: 0:Disabled,1:Use Throttle,2:Use Current147// @User: Advanced148// @Calibration: 1149AP_GROUPINFO("MOTCT", 6, Compass, _motor_comp_type, AP_COMPASS_MOT_COMP_DISABLED),150151// @Param: MOT_X152// @DisplayName: Motor interference compensation for body frame X axis153// @Description: Multiplied by the current throttle and added to the compass's x-axis values to compensate for motor interference (Offset per Amp or at Full Throttle)154// @Range: -1000 1000155// @Units: mGauss/A156// @Increment: 1157// @User: Advanced158// @Calibration: 1159160// @Param: MOT_Y161// @DisplayName: Motor interference compensation for body frame Y axis162// @Description: Multiplied by the current throttle and added to the compass's y-axis values to compensate for motor interference (Offset per Amp or at Full Throttle)163// @Range: -1000 1000164// @Units: mGauss/A165// @Increment: 1166// @User: Advanced167// @Calibration: 1168169// @Param: MOT_Z170// @DisplayName: Motor interference compensation for body frame Z axis171// @Description: Multiplied by the current throttle and added to the compass's z-axis values to compensate for motor interference (Offset per Amp or at Full Throttle)172// @Range: -1000 1000173// @Units: mGauss/A174// @Increment: 1175// @User: Advanced176// @Calibration: 1177AP_GROUPINFO("MOT", 7, Compass, _state._priv_instance[0].motor_compensation, 0),178#endif179180#ifndef HAL_BUILD_AP_PERIPH181// @Param: ORIENT182// @DisplayName: Compass orientation183// @Description: The orientation of the first external compass relative to the vehicle frame. This value will be ignored unless this compass is set as an external compass. When set correctly in the northern hemisphere, pointing the nose and right side down should increase the MagX and MagY values respectively. Rolling the vehicle upside down should decrease the MagZ value. For southern hemisphere, switch increase and decrease. NOTE: For internal compasses, AHRS_ORIENT is used. The label for each option is specified in the order of rotations for that orientation. Firmware versions 4.2 and prior can use a CUSTOM (100) rotation to set the COMPASS_CUS_ROLL/PIT/YAW angles for Compass orientation. Later versions provide two general custom rotations which can be used, Custom 1 and Custom 2, with CUST_1_ROLL/PIT/YAW or CUST_2_ROLL/PIT/YAW angles.184// @Values: 0:None,1:Yaw45,2:Yaw90,3:Yaw135,4:Yaw180,5:Yaw225,6:Yaw270,7:Yaw315,8:Roll180,9:Yaw45Roll180,10:Yaw90Roll180,11:Yaw135Roll180,12:Pitch180,13:Yaw225Roll180,14:Yaw270Roll180,15:Yaw315Roll180,16:Roll90,17:Yaw45Roll90,18:Yaw90Roll90,19:Yaw135Roll90,20:Roll270,21:Yaw45Roll270,22:Yaw90Roll270,23:Yaw135Roll270,24:Pitch90,25:Pitch270,26:Yaw90Pitch180,27:Yaw270Pitch180,28:Pitch90Roll90,29:Pitch90Roll180,30:Pitch90Roll270,31:Pitch180Roll90,32:Pitch180Roll270,33:Pitch270Roll90,34:Pitch270Roll180,35:Pitch270Roll270,36:Yaw90Pitch180Roll90,37:Yaw270Roll90,38:Yaw293Pitch68Roll180,39:Pitch315,40:Pitch315Roll90,42:Roll45,43:Roll315,100:Custom 4.1 and older,101:Custom 1,102:Custom 2185// @User: Advanced186AP_GROUPINFO("ORIENT", 8, Compass, _state._priv_instance[0].orientation, ROTATION_NONE),187188// @Param: EXTERNAL189// @DisplayName: Compass is attached via an external cable190// @Description: Configure compass so it is attached externally. This is auto-detected on most boards. Set to 1 if the compass is externally connected. When externally connected the COMPASS_ORIENT option operates independently of the AHRS_ORIENTATION board orientation option. If set to 0 or 1 then auto-detection by bus connection can override the value. If set to 2 then auto-detection will be disabled.191// @Values: 0:Internal,1:External,2:ForcedExternal192// @User: Advanced193AP_GROUPINFO("EXTERNAL", 9, Compass, _state._priv_instance[0].external, 0),194#endif195196#if COMPASS_MAX_INSTANCES > 1197// @Param: OFS2_X198// @DisplayName: Compass2 offsets in milligauss on the X axis199// @Description: Offset to be added to compass2's x-axis values to compensate for metal in the frame200// @Range: -400 400201// @Units: mGauss202// @Increment: 1203// @User: Advanced204// @Calibration: 1205206// @Param: OFS2_Y207// @DisplayName: Compass2 offsets in milligauss on the Y axis208// @Description: Offset to be added to compass2's y-axis values to compensate for metal in the frame209// @Range: -400 400210// @Units: mGauss211// @Increment: 1212// @User: Advanced213// @Calibration: 1214215// @Param: OFS2_Z216// @DisplayName: Compass2 offsets in milligauss on the Z axis217// @Description: Offset to be added to compass2's z-axis values to compensate for metal in the frame218// @Range: -400 400219// @Units: mGauss220// @Increment: 1221// @User: Advanced222// @Calibration: 1223AP_GROUPINFO("OFS2", 10, Compass, _state._priv_instance[1].offset, 0),224225// @Param: MOT2_X226// @DisplayName: Motor interference compensation to compass2 for body frame X axis227// @Description: Multiplied by the current throttle and added to compass2's x-axis values to compensate for motor interference (Offset per Amp or at Full Throttle)228// @Range: -1000 1000229// @Units: mGauss/A230// @Increment: 1231// @User: Advanced232// @Calibration: 1233234// @Param: MOT2_Y235// @DisplayName: Motor interference compensation to compass2 for body frame Y axis236// @Description: Multiplied by the current throttle and added to compass2's y-axis values to compensate for motor interference (Offset per Amp or at Full Throttle)237// @Range: -1000 1000238// @Units: mGauss/A239// @Increment: 1240// @User: Advanced241// @Calibration: 1242243// @Param: MOT2_Z244// @DisplayName: Motor interference compensation to compass2 for body frame Z axis245// @Description: Multiplied by the current throttle and added to compass2's z-axis values to compensate for motor interference (Offset per Amp or at Full Throttle)246// @Range: -1000 1000247// @Units: mGauss/A248// @Increment: 1249// @User: Advanced250// @Calibration: 1251AP_GROUPINFO("MOT2", 11, Compass, _state._priv_instance[1].motor_compensation, 0),252253#endif // COMPASS_MAX_INSTANCES254255#if COMPASS_MAX_INSTANCES > 2256// @Param: OFS3_X257// @DisplayName: Compass3 offsets in milligauss on the X axis258// @Description: Offset to be added to compass3's x-axis values to compensate for metal in the frame259// @Range: -400 400260// @Units: mGauss261// @Increment: 1262// @User: Advanced263// @Calibration: 1264265// @Param: OFS3_Y266// @DisplayName: Compass3 offsets in milligauss on the Y axis267// @Description: Offset to be added to compass3's y-axis values to compensate for metal in the frame268// @Range: -400 400269// @Units: mGauss270// @Increment: 1271// @User: Advanced272// @Calibration: 1273274// @Param: OFS3_Z275// @DisplayName: Compass3 offsets in milligauss on the Z axis276// @Description: Offset to be added to compass3's z-axis values to compensate for metal in the frame277// @Range: -400 400278// @Units: mGauss279// @Increment: 1280// @User: Advanced281// @Calibration: 1282AP_GROUPINFO("OFS3", 13, Compass, _state._priv_instance[2].offset, 0),283284// @Param: MOT3_X285// @DisplayName: Motor interference compensation to compass3 for body frame X axis286// @Description: Multiplied by the current throttle and added to compass3's x-axis values to compensate for motor interference (Offset per Amp or at Full Throttle)287// @Range: -1000 1000288// @Units: mGauss/A289// @Increment: 1290// @User: Advanced291// @Calibration: 1292293// @Param: MOT3_Y294// @DisplayName: Motor interference compensation to compass3 for body frame Y axis295// @Description: Multiplied by the current throttle and added to compass3's y-axis values to compensate for motor interference (Offset per Amp or at Full Throttle)296// @Range: -1000 1000297// @Units: mGauss/A298// @Increment: 1299// @User: Advanced300// @Calibration: 1301302// @Param: MOT3_Z303// @DisplayName: Motor interference compensation to compass3 for body frame Z axis304// @Description: Multiplied by the current throttle and added to compass3's z-axis values to compensate for motor interference (Offset per Amp or at Full Throttle)305// @Range: -1000 1000306// @Units: mGauss/A307// @Increment: 1308// @User: Advanced309// @Calibration: 1310AP_GROUPINFO("MOT3", 14, Compass, _state._priv_instance[2].motor_compensation, 0),311#endif // COMPASS_MAX_INSTANCES312313// @Param: DEV_ID314// @DisplayName: Compass device id315// @Description: Compass device id. Automatically detected, do not set manually316// @ReadOnly: True317// @User: Advanced318AP_GROUPINFO("DEV_ID", 15, Compass, _state._priv_instance[0].dev_id, 0),319320#if COMPASS_MAX_INSTANCES > 1321// @Param: DEV_ID2322// @DisplayName: Compass2 device id323// @Description: Second compass's device id. Automatically detected, do not set manually324// @ReadOnly: True325// @User: Advanced326AP_GROUPINFO("DEV_ID2", 16, Compass, _state._priv_instance[1].dev_id, 0),327#endif // COMPASS_MAX_INSTANCES328329#if COMPASS_MAX_INSTANCES > 2330// @Param: DEV_ID3331// @DisplayName: Compass3 device id332// @Description: Third compass's device id. Automatically detected, do not set manually333// @ReadOnly: True334// @User: Advanced335AP_GROUPINFO("DEV_ID3", 17, Compass, _state._priv_instance[2].dev_id, 0),336#endif // COMPASS_MAX_INSTANCES337338#if COMPASS_MAX_INSTANCES > 1339// @Param: USE2340// @DisplayName: Compass2 used for yaw341// @Description: Enable or disable the secondary compass for determining heading.342// @Values: 0:Disabled,1:Enabled343// @User: Advanced344AP_GROUPINFO("USE2", 18, Compass, _use_for_yaw._priv_instance[1], 1),345346// @Param: ORIENT2347// @DisplayName: Compass2 orientation348// @Description: The orientation of a second external compass relative to the vehicle frame. This value will be ignored unless this compass is set as an external compass. When set correctly in the northern hemisphere, pointing the nose and right side down should increase the MagX and MagY values respectively. Rolling the vehicle upside down should decrease the MagZ value. For southern hemisphere, switch increase and decrease. NOTE: For internal compasses, AHRS_ORIENT is used. The label for each option is specified in the order of rotations for that orientation. Firmware versions 4.2 and prior can use a CUSTOM (100) rotation to set the COMPASS_CUS_ROLL/PIT/YAW angles for Compass orientation. Later versions provide two general custom rotations which can be used, Custom 1 and Custom 2, with CUST_1_ROLL/PIT/YAW or CUST_2_ROLL/PIT/YAW angles.349// @Values: 0:None,1:Yaw45,2:Yaw90,3:Yaw135,4:Yaw180,5:Yaw225,6:Yaw270,7:Yaw315,8:Roll180,9:Yaw45Roll180,10:Yaw90Roll180,11:Yaw135Roll180,12:Pitch180,13:Yaw225Roll180,14:Yaw270Roll180,15:Yaw315Roll180,16:Roll90,17:Yaw45Roll90,18:Yaw90Roll90,19:Yaw135Roll90,20:Roll270,21:Yaw45Roll270,22:Yaw90Roll270,23:Yaw135Roll270,24:Pitch90,25:Pitch270,26:Yaw90Pitch180,27:Yaw270Pitch180,28:Pitch90Roll90,29:Pitch90Roll180,30:Pitch90Roll270,31:Pitch180Roll90,32:Pitch180Roll270,33:Pitch270Roll90,34:Pitch270Roll180,35:Pitch270Roll270,36:Yaw90Pitch180Roll90,37:Yaw270Roll90,38:Yaw293Pitch68Roll180,39:Pitch315,40:Pitch315Roll90,42:Roll45,43:Roll315,100:Custom 4.1 and older,101:Custom 1,102:Custom 2350// @User: Advanced351AP_GROUPINFO("ORIENT2", 19, Compass, _state._priv_instance[1].orientation, ROTATION_NONE),352353// @Param: EXTERN2354// @DisplayName: Compass2 is attached via an external cable355// @Description: Configure second compass so it is attached externally. This is auto-detected on most boards. If set to 0 or 1 then auto-detection by bus connection can override the value. If set to 2 then auto-detection will be disabled.356// @Values: 0:Internal,1:External,2:ForcedExternal357// @User: Advanced358AP_GROUPINFO("EXTERN2",20, Compass, _state._priv_instance[1].external, 0),359#endif // COMPASS_MAX_INSTANCES360361#if COMPASS_MAX_INSTANCES > 2362// @Param: USE3363// @DisplayName: Compass3 used for yaw364// @Description: Enable or disable the tertiary compass for determining heading.365// @Values: 0:Disabled,1:Enabled366// @User: Advanced367AP_GROUPINFO("USE3", 21, Compass, _use_for_yaw._priv_instance[2], 1),368369// @Param: ORIENT3370// @DisplayName: Compass3 orientation371// @Description: The orientation of a third external compass relative to the vehicle frame. This value will be ignored unless this compass is set as an external compass. When set correctly in the northern hemisphere, pointing the nose and right side down should increase the MagX and MagY values respectively. Rolling the vehicle upside down should decrease the MagZ value. For southern hemisphere, switch increase and decrease. NOTE: For internal compasses, AHRS_ORIENT is used. The label for each option is specified in the order of rotations for that orientation. Firmware versions 4.2 and prior can use a CUSTOM (100) rotation to set the COMPASS_CUS_ROLL/PIT/YAW angles for Compass orientation. Later versions provide two general custom rotations which can be used, Custom 1 and Custom 2, with CUST_1_ROLL/PIT/YAW or CUST_2_ROLL/PIT/YAW angles.372// @Values: 0:None,1:Yaw45,2:Yaw90,3:Yaw135,4:Yaw180,5:Yaw225,6:Yaw270,7:Yaw315,8:Roll180,9:Yaw45Roll180,10:Yaw90Roll180,11:Yaw135Roll180,12:Pitch180,13:Yaw225Roll180,14:Yaw270Roll180,15:Yaw315Roll180,16:Roll90,17:Yaw45Roll90,18:Yaw90Roll90,19:Yaw135Roll90,20:Roll270,21:Yaw45Roll270,22:Yaw90Roll270,23:Yaw135Roll270,24:Pitch90,25:Pitch270,26:Yaw90Pitch180,27:Yaw270Pitch180,28:Pitch90Roll90,29:Pitch90Roll180,30:Pitch90Roll270,31:Pitch180Roll90,32:Pitch180Roll270,33:Pitch270Roll90,34:Pitch270Roll180,35:Pitch270Roll270,36:Yaw90Pitch180Roll90,37:Yaw270Roll90,38:Yaw293Pitch68Roll180,39:Pitch315,40:Pitch315Roll90,42:Roll45,43:Roll315,100:Custom 4.1 and older,101:Custom 1,102:Custom 2373// @User: Advanced374AP_GROUPINFO("ORIENT3", 22, Compass, _state._priv_instance[2].orientation, ROTATION_NONE),375376// @Param: EXTERN3377// @DisplayName: Compass3 is attached via an external cable378// @Description: Configure third compass so it is attached externally. This is auto-detected on most boards. If set to 0 or 1 then auto-detection by bus connection can override the value. If set to 2 then auto-detection will be disabled.379// @Values: 0:Internal,1:External,2:ForcedExternal380// @User: Advanced381AP_GROUPINFO("EXTERN3",23, Compass, _state._priv_instance[2].external, 0),382#endif // COMPASS_MAX_INSTANCES383384#if AP_COMPASS_DIAGONALS_ENABLED385// @Param: DIA_X386// @DisplayName: Compass soft-iron diagonal X component387// @Description: DIA_X in the compass soft-iron calibration matrix: [[DIA_X, ODI_X, ODI_Y], [ODI_X, DIA_Y, ODI_Z], [ODI_Y, ODI_Z, DIA_Z]]388// @User: Advanced389// @Calibration: 1390391// @Param: DIA_Y392// @DisplayName: Compass soft-iron diagonal Y component393// @Description: DIA_Y in the compass soft-iron calibration matrix: [[DIA_X, ODI_X, ODI_Y], [ODI_X, DIA_Y, ODI_Z], [ODI_Y, ODI_Z, DIA_Z]]394// @User: Advanced395// @Calibration: 1396397// @Param: DIA_Z398// @DisplayName: Compass soft-iron diagonal Z component399// @Description: DIA_Z in the compass soft-iron calibration matrix: [[DIA_X, ODI_X, ODI_Y], [ODI_X, DIA_Y, ODI_Z], [ODI_Y, ODI_Z, DIA_Z]]400// @User: Advanced401// @Calibration: 1402AP_GROUPINFO("DIA", 24, Compass, _state._priv_instance[0].diagonals, 1.0),403404// @Param: ODI_X405// @DisplayName: Compass soft-iron off-diagonal X component406// @Description: ODI_X in the compass soft-iron calibration matrix: [[DIA_X, ODI_X, ODI_Y], [ODI_X, DIA_Y, ODI_Z], [ODI_Y, ODI_Z, DIA_Z]]407// @User: Advanced408// @Calibration: 1409410// @Param: ODI_Y411// @DisplayName: Compass soft-iron off-diagonal Y component412// @Description: ODI_Y in the compass soft-iron calibration matrix: [[DIA_X, ODI_X, ODI_Y], [ODI_X, DIA_Y, ODI_Z], [ODI_Y, ODI_Z, DIA_Z]]413// @User: Advanced414// @Calibration: 1415416// @Param: ODI_Z417// @DisplayName: Compass soft-iron off-diagonal Z component418// @Description: ODI_Z in the compass soft-iron calibration matrix: [[DIA_X, ODI_X, ODI_Y], [ODI_X, DIA_Y, ODI_Z], [ODI_Y, ODI_Z, DIA_Z]]419// @User: Advanced420// @Calibration: 1421AP_GROUPINFO("ODI", 25, Compass, _state._priv_instance[0].offdiagonals, 0),422423#if COMPASS_MAX_INSTANCES > 1424// @Param: DIA2_X425// @DisplayName: Compass2 soft-iron diagonal X component426// @Description: DIA_X in the compass2 soft-iron calibration matrix: [[DIA_X, ODI_X, ODI_Y], [ODI_X, DIA_Y, ODI_Z], [ODI_Y, ODI_Z, DIA_Z]]427// @User: Advanced428// @Calibration: 1429430// @Param: DIA2_Y431// @DisplayName: Compass2 soft-iron diagonal Y component432// @Description: DIA_Y in the compass2 soft-iron calibration matrix: [[DIA_X, ODI_X, ODI_Y], [ODI_X, DIA_Y, ODI_Z], [ODI_Y, ODI_Z, DIA_Z]]433// @User: Advanced434// @Calibration: 1435436// @Param: DIA2_Z437// @DisplayName: Compass2 soft-iron diagonal Z component438// @Description: DIA_Z in the compass2 soft-iron calibration matrix: [[DIA_X, ODI_X, ODI_Y], [ODI_X, DIA_Y, ODI_Z], [ODI_Y, ODI_Z, DIA_Z]]439// @User: Advanced440// @Calibration: 1441AP_GROUPINFO("DIA2", 26, Compass, _state._priv_instance[1].diagonals, 1.0),442443// @Param: ODI2_X444// @DisplayName: Compass2 soft-iron off-diagonal X component445// @Description: ODI_X in the compass2 soft-iron calibration matrix: [[DIA_X, ODI_X, ODI_Y], [ODI_X, DIA_Y, ODI_Z], [ODI_Y, ODI_Z, DIA_Z]]446// @User: Advanced447// @Calibration: 1448449// @Param: ODI2_Y450// @DisplayName: Compass2 soft-iron off-diagonal Y component451// @Description: ODI_Y in the compass2 soft-iron calibration matrix: [[DIA_X, ODI_X, ODI_Y], [ODI_X, DIA_Y, ODI_Z], [ODI_Y, ODI_Z, DIA_Z]]452// @User: Advanced453// @Calibration: 1454455// @Param: ODI2_Z456// @DisplayName: Compass2 soft-iron off-diagonal Z component457// @Description: ODI_Z in the compass2 soft-iron calibration matrix: [[DIA_X, ODI_X, ODI_Y], [ODI_X, DIA_Y, ODI_Z], [ODI_Y, ODI_Z, DIA_Z]]458// @User: Advanced459// @Calibration: 1460AP_GROUPINFO("ODI2", 27, Compass, _state._priv_instance[1].offdiagonals, 0),461#endif // COMPASS_MAX_INSTANCES462463#if COMPASS_MAX_INSTANCES > 2464// @Param: DIA3_X465// @DisplayName: Compass3 soft-iron diagonal X component466// @Description: DIA_X in the compass3 soft-iron calibration matrix: [[DIA_X, ODI_X, ODI_Y], [ODI_X, DIA_Y, ODI_Z], [ODI_Y, ODI_Z, DIA_Z]]467// @User: Advanced468// @Calibration: 1469470// @Param: DIA3_Y471// @DisplayName: Compass3 soft-iron diagonal Y component472// @Description: DIA_Y in the compass3 soft-iron calibration matrix: [[DIA_X, ODI_X, ODI_Y], [ODI_X, DIA_Y, ODI_Z], [ODI_Y, ODI_Z, DIA_Z]]473// @User: Advanced474// @Calibration: 1475476// @Param: DIA3_Z477// @DisplayName: Compass3 soft-iron diagonal Z component478// @Description: DIA_Z in the compass3 soft-iron calibration matrix: [[DIA_X, ODI_X, ODI_Y], [ODI_X, DIA_Y, ODI_Z], [ODI_Y, ODI_Z, DIA_Z]]479// @User: Advanced480// @Calibration: 1481AP_GROUPINFO("DIA3", 28, Compass, _state._priv_instance[2].diagonals, 1.0),482483// @Param: ODI3_X484// @DisplayName: Compass3 soft-iron off-diagonal X component485// @Description: ODI_X in the compass3 soft-iron calibration matrix: [[DIA_X, ODI_X, ODI_Y], [ODI_X, DIA_Y, ODI_Z], [ODI_Y, ODI_Z, DIA_Z]]486// @User: Advanced487// @Calibration: 1488489// @Param: ODI3_Y490// @DisplayName: Compass3 soft-iron off-diagonal Y component491// @Description: ODI_Y in the compass3 soft-iron calibration matrix: [[DIA_X, ODI_X, ODI_Y], [ODI_X, DIA_Y, ODI_Z], [ODI_Y, ODI_Z, DIA_Z]]492// @User: Advanced493// @Calibration: 1494495// @Param: ODI3_Z496// @DisplayName: Compass3 soft-iron off-diagonal Z component497// @Description: ODI_Z in the compass3 soft-iron calibration matrix: [[DIA_X, ODI_X, ODI_Y], [ODI_X, DIA_Y, ODI_Z], [ODI_Y, ODI_Z, DIA_Z]]498// @User: Advanced499// @Calibration: 1500AP_GROUPINFO("ODI3", 29, Compass, _state._priv_instance[2].offdiagonals, 0),501#endif // COMPASS_MAX_INSTANCES502#endif // AP_COMPASS_DIAGONALS_ENABLED503504#if COMPASS_CAL_ENABLED505// @Param: CAL_FIT506// @DisplayName: Compass calibration fitness507// @Description: This controls the fitness level required for a successful compass calibration. A lower value makes for a stricter fit (less likely to pass). This is the value used for the primary magnetometer. Other magnetometers get double the value.508// @Range: 4 32509// @Values: 4:Very Strict,8:Strict,16:Default,32:Relaxed510// @Increment: 0.1511// @User: Advanced512AP_GROUPINFO("CAL_FIT", 30, Compass, _calibration_threshold, AP_COMPASS_CALIBRATION_FITNESS_DEFAULT),513#endif514515#ifndef HAL_BUILD_AP_PERIPH516// @Param: OFFS_MAX517// @DisplayName: Compass maximum offset518// @Description: This sets the maximum allowed compass offset in calibration and arming checks519// @Range: 500 3000520// @Increment: 1521// @User: Advanced522AP_GROUPINFO("OFFS_MAX", 31, Compass, _offset_max, AP_COMPASS_OFFSETS_MAX_DEFAULT),523#endif524525#if COMPASS_MOT_ENABLED526// @Group: PMOT527// @Path: Compass_PerMotor.cpp528AP_SUBGROUPINFO(_per_motor, "PMOT", 32, Compass, Compass_PerMotor),529#endif530531// @Param: DISBLMSK532// @DisplayName: Compass disable driver type mask533// @Description: This is a bitmask of driver types to disable. If a driver type is set in this mask then that driver will not try to find a sensor at startup534// @Bitmask: 0:HMC5883,1:LSM303D,2:AK8963,3:BMM150,4:LSM9DS1,5:LIS3MDL,6:AK0991x,7:IST8310,8:ICM20948,9:MMC3416,11:DroneCAN,12:QMC5883,14:MAG3110,15:IST8308,16:RM3100,17:MSP,18:ExternalAHRS,19:MMC5XX3,20:QMC5883P,21:BMM350,22:IIS2MDC,23:LIS2MDL535// @User: Advanced536AP_GROUPINFO("DISBLMSK", 33, Compass, _driver_type_mask, 0),537538// @Param: FLTR_RNG539// @DisplayName: Range in which sample is accepted540// @Description: This sets the range around the average value that new samples must be within to be accepted. This can help reduce the impact of noise on sensors that are on long I2C cables. The value is a percentage from the average value. A value of zero disables this filter.541// @Units: %542// @Range: 0 100543// @Increment: 1544AP_GROUPINFO("FLTR_RNG", 34, Compass, _filter_range, HAL_COMPASS_FILTER_DEFAULT),545546#if COMPASS_CAL_ENABLED547// @Param: AUTO_ROT548// @DisplayName: Automatically check orientation549// @Description: When enabled this will automatically check the orientation of compasses on successful completion of compass calibration. If set to 2 then external compasses will have their orientation automatically corrected.550// @Values: 0:Disabled,1:CheckOnly,2:CheckAndFix,3:use same tolerance to auto rotate 45 deg rotations551AP_GROUPINFO("AUTO_ROT", 35, Compass, _rotate_auto, HAL_COMPASS_AUTO_ROT_DEFAULT),552#endif553554#if COMPASS_MAX_INSTANCES > 1555// @Param: PRIO1_ID556// @DisplayName: Compass device id with 1st order priority557// @Description: Compass device id with 1st order priority, set automatically if 0. Reboot required after change.558// @RebootRequired: True559// @User: Advanced560AP_GROUPINFO("PRIO1_ID", 36, Compass, _priority_did_stored_list._priv_instance[0], 0),561562// @Param: PRIO2_ID563// @DisplayName: Compass device id with 2nd order priority564// @Description: Compass device id with 2nd order priority, set automatically if 0. Reboot required after change.565// @RebootRequired: True566// @User: Advanced567AP_GROUPINFO("PRIO2_ID", 37, Compass, _priority_did_stored_list._priv_instance[1], 0),568#endif // COMPASS_MAX_INSTANCES569570#if COMPASS_MAX_INSTANCES > 2571// @Param: PRIO3_ID572// @DisplayName: Compass device id with 3rd order priority573// @Description: Compass device id with 3rd order priority, set automatically if 0. Reboot required after change.574// @RebootRequired: True575// @User: Advanced576AP_GROUPINFO("PRIO3_ID", 38, Compass, _priority_did_stored_list._priv_instance[2], 0),577#endif // COMPASS_MAX_INSTANCES578579// @Param: ENABLE580// @DisplayName: Enable Compass581// @Description: Setting this to Enabled(1) will enable the compass. Setting this to Disabled(0) will disable the compass. Note that this is separate from COMPASS_USE. This will enable the low level senor, and will enable logging of magnetometer data. To use the compass for navigation you must also set COMPASS_USE to 1.582// @User: Standard583// @RebootRequired: True584// @Values: 0:Disabled,1:Enabled585AP_GROUPINFO("ENABLE", 39, Compass, _enabled, AP_COMPASS_ENABLE_DEFAULT),586587#ifndef HAL_BUILD_AP_PERIPH588// @Param: SCALE589// @DisplayName: Compass1 scale factor590// @Description: Scaling factor for first compass to compensate for sensor scaling errors. If this is 0 then no scaling is done591// @User: Standard592// @Range: 0 1.3593AP_GROUPINFO("SCALE", 40, Compass, _state._priv_instance[0].scale_factor, 0),594595#if COMPASS_MAX_INSTANCES > 1596// @Param: SCALE2597// @DisplayName: Compass2 scale factor598// @Description: Scaling factor for 2nd compass to compensate for sensor scaling errors. If this is 0 then no scaling is done599// @User: Standard600// @Range: 0 1.3601AP_GROUPINFO("SCALE2", 41, Compass, _state._priv_instance[1].scale_factor, 0),602#endif603604#if COMPASS_MAX_INSTANCES > 2605// @Param: SCALE3606// @DisplayName: Compass3 scale factor607// @Description: Scaling factor for 3rd compass to compensate for sensor scaling errors. If this is 0 then no scaling is done608// @User: Standard609// @Range: 0 1.3610AP_GROUPINFO("SCALE3", 42, Compass, _state._priv_instance[2].scale_factor, 0),611#endif612#endif // HAL_BUILD_AP_PERIPH613614#ifndef HAL_BUILD_AP_PERIPH615// @Param: OPTIONS616// @DisplayName: Compass options617// @Description: This sets options to change the behaviour of the compass618// @Bitmask: 0:CalRequireGPS619// @Bitmask: 1: Allow missing DroneCAN compasses to be automaticaly replaced (calibration still required)620// @User: Advanced621AP_GROUPINFO("OPTIONS", 43, Compass, _options, 0),622#endif623624#if COMPASS_MAX_UNREG_DEV > 0625// @Param: DEV_ID4626// @DisplayName: Compass4 device id627// @Description: Extra 4th compass's device id. Automatically detected, do not set manually628// @ReadOnly: True629// @User: Advanced630AP_GROUPINFO("DEV_ID4", 44, Compass, extra_dev_id[0], 0),631#endif // COMPASS_MAX_UNREG_DEV632633#if COMPASS_MAX_UNREG_DEV > 1634// @Param: DEV_ID5635// @DisplayName: Compass5 device id636// @Description: Extra 5th compass's device id. Automatically detected, do not set manually637// @ReadOnly: True638// @User: Advanced639AP_GROUPINFO("DEV_ID5", 45, Compass, extra_dev_id[1], 0),640#endif // COMPASS_MAX_UNREG_DEV641642#if COMPASS_MAX_UNREG_DEV > 2643// @Param: DEV_ID6644// @DisplayName: Compass6 device id645// @Description: Extra 6th compass's device id. Automatically detected, do not set manually646// @ReadOnly: True647// @User: Advanced648AP_GROUPINFO("DEV_ID6", 46, Compass, extra_dev_id[2], 0),649#endif // COMPASS_MAX_UNREG_DEV650651#if COMPASS_MAX_UNREG_DEV > 3652// @Param: DEV_ID7653// @DisplayName: Compass7 device id654// @Description: Extra 7th compass's device id. Automatically detected, do not set manually655// @ReadOnly: True656// @User: Advanced657AP_GROUPINFO("DEV_ID7", 47, Compass, extra_dev_id[3], 0),658#endif // COMPASS_MAX_UNREG_DEV659660#if COMPASS_MAX_UNREG_DEV > 4661// @Param: DEV_ID8662// @DisplayName: Compass8 device id663// @Description: Extra 8th compass's device id. Automatically detected, do not set manually664// @ReadOnly: True665// @User: Advanced666AP_GROUPINFO("DEV_ID8", 48, Compass, extra_dev_id[4], 0),667#endif // COMPASS_MAX_UNREG_DEV668669// @Param: CUS_ROLL670// @DisplayName: Custom orientation roll offset671// @Description: Compass mounting position roll offset. Positive values = roll right, negative values = roll left. This parameter is only used when COMPASS_ORIENT/2/3 is set to CUSTOM.672// @Range: -180 180673// @Units: deg674// @Increment: 1675// @RebootRequired: True676// @User: Advanced677678// index 49679680// @Param: CUS_PIT681// @DisplayName: Custom orientation pitch offset682// @Description: Compass mounting position pitch offset. Positive values = pitch up, negative values = pitch down. This parameter is only used when COMPASS_ORIENT/2/3 is set to CUSTOM.683// @Range: -180 180684// @Units: deg685// @Increment: 1686// @RebootRequired: True687// @User: Advanced688689// index 50690691// @Param: CUS_YAW692// @DisplayName: Custom orientation yaw offset693// @Description: Compass mounting position yaw offset. Positive values = yaw right, negative values = yaw left. This parameter is only used when COMPASS_ORIENT/2/3 is set to CUSTOM.694// @Range: -180 180695// @Units: deg696// @Increment: 1697// @RebootRequired: True698// @User: Advanced699700// index 51701702AP_GROUPEND703};704705// Default constructor.706// Note that the Vector/Matrix constructors already implicitly zero707// their values.708//709Compass::Compass(void)710{711if (_singleton != nullptr) {712#if CONFIG_HAL_BOARD == HAL_BOARD_SITL713AP_HAL::panic("Compass must be singleton");714#endif715return;716}717_singleton = this;718AP_Param::setup_object_defaults(this, var_info);719}720721// Default init method722//723void Compass::init()724{725if (!_enabled) {726return;727}728729/*730on init() if any devid is set then we set suppress_devid_save to731false. This is used to determine if we save device ids during732the init process.733*/734suppress_devid_save = true;735for (uint8_t i=0; i<COMPASS_MAX_INSTANCES; i++) {736if (_state._priv_instance[i].dev_id != 0) {737suppress_devid_save = false;738break;739}740#if COMPASS_MAX_INSTANCES > 1741if (_priority_did_stored_list._priv_instance[i] != 0) {742suppress_devid_save = false;743break;744}745#endif746}747748// convert to new custom rotation method749// PARAMETER_CONVERSION - Added: Nov-2021750#if AP_CUSTOMROTATIONS_ENABLED751for (StateIndex i(0); i<COMPASS_MAX_INSTANCES; i++) {752if (_state[i].orientation != ROTATION_CUSTOM_OLD) {753continue;754}755_state[i].orientation.set_and_save(ROTATION_CUSTOM_2);756AP_Param::ConversionInfo info;757if (AP_Param::find_top_level_key_by_pointer(this, info.old_key)) {758info.type = AP_PARAM_FLOAT;759float rpy[3] = {};760AP_Float rpy_param;761for (info.old_group_element=49; info.old_group_element<=51; info.old_group_element++) {762if (AP_Param::find_old_parameter(&info, &rpy_param)) {763rpy[info.old_group_element-49] = rpy_param.get();764}765}766AP::custom_rotations().convert(ROTATION_CUSTOM_2, rpy[0], rpy[1], rpy[2]);767}768break;769}770#endif // AP_CUSTOMROTATIONS_ENABLED771772#if COMPASS_MAX_INSTANCES > 1773// Look if there was a primary compass setup in previous version774// if so and the primary compass is not set in current setup775// make the devid as primary.776if (_priority_did_stored_list[Priority(0)] == 0) {777uint16_t k_param_compass;778if (AP_Param::find_top_level_key_by_pointer(this, k_param_compass)) {779const AP_Param::ConversionInfo primary_compass_old_param = {k_param_compass, 12, AP_PARAM_INT8, ""};780AP_Int8 value;781value.set(0);782bool primary_param_exists = AP_Param::find_old_parameter(&primary_compass_old_param, &value);783int8_t oldvalue = value.get();784if ((oldvalue!=0) && (oldvalue<COMPASS_MAX_INSTANCES) && primary_param_exists) {785_priority_did_stored_list[Priority(0)].set_and_save_ifchanged(_state[StateIndex(oldvalue)].dev_id);786}787}788}789790// Load priority list from storage, the changes to priority list791// by user only take effect post reboot, after this792if (!suppress_devid_save) {793for (Priority i(0); i<COMPASS_MAX_INSTANCES; i++) {794if (_priority_did_stored_list[i] != 0) {795_priority_did_list[i] = _priority_did_stored_list[i];796} else {797// Maintain a list without gaps and duplicates798for (Priority j(i+1); j<COMPASS_MAX_INSTANCES; j++) {799int32_t temp;800if (_priority_did_stored_list[j] == _priority_did_stored_list[i]) {801_priority_did_stored_list[j].set_and_save_ifchanged(0);802}803if (_priority_did_stored_list[j] == 0) {804continue;805}806temp = _priority_did_stored_list[j];807_priority_did_stored_list[j].set_and_save_ifchanged(0);808_priority_did_list[i] = temp;809_priority_did_stored_list[i].set_and_save_ifchanged(temp);810break;811}812}813}814}815#endif // COMPASS_MAX_INSTANCES816817// cache expected dev ids for use during runtime detection818for (StateIndex i(0); i<COMPASS_MAX_INSTANCES; i++) {819_state[i].expected_dev_id = _state[i].dev_id;820}821822#if COMPASS_MAX_UNREG_DEV823// set the dev_id to 0 for undetected compasses. extra_dev_id is just an824// interface for users to see unreg compasses, we actually never store it825// in storage.826for (uint8_t i=_unreg_compass_count; i<COMPASS_MAX_UNREG_DEV; i++) {827// cache the extra devices detected in last boot828// for detecting replacement mag829_previously_unreg_mag[i] = extra_dev_id[i];830extra_dev_id[i].set(0);831}832#endif833834#if COMPASS_MAX_INSTANCES > 1835// This method calls set_and_save_ifchanged on parameters836// which are set() but not saved() during normal runtime,837// do not move this call without ensuring that is not happening838// read comments under set_and_save_ifchanged for details839if (!suppress_devid_save) {840_reorder_compass_params();841}842#endif843844if (_compass_count == 0) {845// detect available backends. Only called once846_detect_backends();847}848849if (_compass_count != 0) {850// get initial health status851hal.scheduler->delay(100);852read();853}854// set the dev_id to 0 for undetected compasses, to make it easier855// for users to see how many compasses are detected. We don't do a856// set_and_save() as the user may have temporarily removed the857// compass, and we don't want to force a re-cal if they plug it858// back in again859for (StateIndex i(0); i<COMPASS_MAX_INSTANCES; i++) {860if (!_state[i].registered) {861_state[i].dev_id.set(0);862}863}864865#ifndef HAL_BUILD_AP_PERIPH866// updating the AHRS orientation updates our own orientation:867AP::ahrs().update_orientation();868#endif869870init_done = true;871suppress_devid_save = false;872}873874#if COMPASS_MAX_INSTANCES > 1 || COMPASS_MAX_UNREG_DEV875// Update Priority List for Mags, by default, we just876// load them as they come up the first time877Compass::Priority Compass::_update_priority_list(int32_t dev_id)878{879// Check if already in priority list880for (Priority i(0); i<COMPASS_MAX_INSTANCES; i++) {881if (_priority_did_list[i] == dev_id) {882if (i >= _compass_count) {883_compass_count = uint8_t(i)+1;884}885return i;886}887}888889// We are not in priority list, let's add at first empty890for (Priority i(0); i<COMPASS_MAX_INSTANCES; i++) {891if (_priority_did_stored_list[i] == 0) {892if (suppress_devid_save) {893_priority_did_stored_list[i].set(dev_id);894} else {895_priority_did_stored_list[i].set_and_save(dev_id);896}897_priority_did_list[i] = dev_id;898if (i >= _compass_count) {899_compass_count = uint8_t(i)+1;900}901return i;902}903}904return Priority(COMPASS_MAX_INSTANCES);905}906#endif907908909#if COMPASS_MAX_INSTANCES > 1910// This method reorganises devid list to match911// priority list, only call before detection at boot912void Compass::_reorder_compass_params()913{914mag_state swap_state;915StateIndex curr_state_id;916for (Priority i(0); i<COMPASS_MAX_INSTANCES; i++) {917if (_priority_did_list[i] == 0) {918continue;919}920curr_state_id = COMPASS_MAX_INSTANCES;921for (StateIndex j(0); j<COMPASS_MAX_INSTANCES; j++) {922if (_priority_did_list[i] == _state[j].dev_id) {923curr_state_id = j;924break;925}926}927if (curr_state_id != COMPASS_MAX_INSTANCES && uint8_t(curr_state_id) != uint8_t(i)) {928//let's swap929swap_state.copy_from(_state[curr_state_id]);930_state[curr_state_id].copy_from(_state[StateIndex(uint8_t(i))]);931_state[StateIndex(uint8_t(i))].copy_from(swap_state);932}933}934}935#endif936937void Compass::mag_state::copy_from(const Compass::mag_state& state)938{939external.set_and_save_ifchanged(state.external);940orientation.set_and_save_ifchanged(state.orientation);941offset.set_and_save_ifchanged(state.offset);942#if AP_COMPASS_DIAGONALS_ENABLED943diagonals.set_and_save_ifchanged(state.diagonals);944offdiagonals.set_and_save_ifchanged(state.offdiagonals);945#endif946scale_factor.set_and_save_ifchanged(state.scale_factor);947dev_id.set_and_save_ifchanged(state.dev_id);948motor_compensation.set_and_save_ifchanged(state.motor_compensation);949expected_dev_id = state.expected_dev_id;950detected_dev_id = state.detected_dev_id;951}952// Register a new compass instance953//954bool Compass::register_compass(int32_t dev_id, uint8_t& instance)955{956957#if COMPASS_MAX_INSTANCES == 1 && !COMPASS_MAX_UNREG_DEV958// simple single compass setup for AP_Periph959Priority priority(0);960StateIndex i(0);961if (_state[i].registered) {962return false;963}964_state[i].registered = true;965_state[i].priority = priority;966instance = uint8_t(i);967_compass_count = 1;968return true;969#else970Priority priority;971// Check if we already have this dev_id registered972for (StateIndex i(0); i<COMPASS_MAX_INSTANCES; i++) {973priority = _update_priority_list(dev_id);974if (_state[i].expected_dev_id == dev_id && priority < COMPASS_MAX_INSTANCES) {975_state[i].registered = true;976_state[i].priority = priority;977instance = uint8_t(i);978return true;979}980}981982// This is an unregistered compass, check if any free slot is available983for (StateIndex i(0); i<COMPASS_MAX_INSTANCES; i++) {984priority = _update_priority_list(dev_id);985if (_state[i].dev_id == 0 && priority < COMPASS_MAX_INSTANCES) {986_state[i].registered = true;987_state[i].priority = priority;988instance = uint8_t(i);989return true;990}991}992993// This might be a replacement compass module, find any unregistered compass994// instance and replace that995priority = _update_priority_list(dev_id);996// try to match priority and state index if possible, this ensure that compass order997// to state order while detection is preserved, this ensures that if compasses in priority998// list show up out of order during detection, it does not replace the state.999StateIndex priority_index = StateIndex(uint8_t(priority));1000if (!_state[priority_index].registered && priority < COMPASS_MAX_INSTANCES) {1001_state[priority_index].registered = true;1002_state[priority_index].priority = priority;1003instance = uint8_t(priority_index);1004return true;1005}1006for (StateIndex i(0); i<COMPASS_MAX_INSTANCES; i++) {1007priority = _update_priority_list(dev_id);1008if (!_state[i].registered && priority < COMPASS_MAX_INSTANCES) {1009_state[i].registered = true;1010_state[i].priority = priority;1011instance = uint8_t(i);1012return true;1013}1014}1015#endif10161017#if COMPASS_MAX_UNREG_DEV1018// Set extra dev id1019if (_unreg_compass_count >= COMPASS_MAX_UNREG_DEV) {1020AP_HAL::panic("Too many compass instances");1021}10221023for (uint8_t i=0; i<COMPASS_MAX_UNREG_DEV; i++) {1024if (extra_dev_id[i] == dev_id) {1025if (i >= _unreg_compass_count) {1026_unreg_compass_count = i+1;1027}1028instance = i+COMPASS_MAX_INSTANCES;1029return false;1030} else if (extra_dev_id[i] == 0) {1031extra_dev_id[_unreg_compass_count++].set(dev_id);1032instance = i+COMPASS_MAX_INSTANCES;1033return false;1034}1035}1036#else1037AP_HAL::panic("Too many compass instances");1038#endif10391040return false;1041}10421043Compass::StateIndex Compass::_get_state_id(Compass::Priority priority) const1044{1045#if COMPASS_MAX_INSTANCES > 11046if (_priority_did_list[priority] == 0) {1047return StateIndex(COMPASS_MAX_INSTANCES);1048}1049for (StateIndex i(0); i<COMPASS_MAX_INSTANCES; i++) {1050if (_priority_did_list[priority] == _state[i].detected_dev_id) {1051return i;1052}1053}1054return StateIndex(COMPASS_MAX_INSTANCES);1055#else1056return StateIndex(0);1057#endif1058}10591060/*1061return true if a driver type is enabled1062*/1063bool Compass::_driver_enabled(enum DriverType driver_type)1064{1065uint32_t mask = (1U<<uint8_t(driver_type));1066return (mask & uint32_t(_driver_type_mask.get())) == 0;1067}10681069/*1070wrapper around hal.i2c_mgr->get_device() that prevents duplicate devices being opened1071*/1072bool Compass::_i2c_sensor_is_registered(uint8_t bus, uint8_t address) const1073{1074for (StateIndex i(0); i<COMPASS_MAX_INSTANCES; i++) {1075if (!_state[i].registered) {1076continue;1077}1078if (AP_HAL::Device::make_bus_id(AP_HAL::Device::BUS_TYPE_I2C, bus, address, 0) ==1079AP_HAL::Device::change_bus_id(uint32_t(_state[i].dev_id.get()), 0)) {1080// we are already using this device1081return true;1082}1083}1084return false;1085}10861087#if COMPASS_MAX_UNREG_DEV > 01088#define RETURN_IF_NO_SPACE if (_unreg_compass_count == COMPASS_MAX_UNREG_DEV) return1089#else1090#define RETURN_IF_NO_SPACE1091#endif10921093/*1094macro to add a backend with check for too many backends or compass1095instances. We don't try to start more than the maximum allowed1096*/1097void Compass::add_backend(Compass::DriverType driver_type, AP_Compass_Backend *backend)1098{1099if (!_driver_enabled(driver_type)) {1100return;1101}11021103if (!backend) {1104return;1105}11061107if (_backend_count == COMPASS_MAX_BACKEND) {1108return;1109}11101111_backends[_backend_count++] = backend;1112}11131114#define GET_I2C_DEVICE(bus, address) _i2c_sensor_is_registered(bus, address)?nullptr:hal.i2c_mgr->get_device(bus, address)11151116/*1117look for compasses on external i2c buses1118*/1119void Compass::_probe_external_i2c_compasses(void)1120{1121#if AP_COMPASS_INTERNAL_BUS_PROBING_ENABLED1122bool all_external = (AP_BoardConfig::get_board_type() == AP_BoardConfig::PX4_BOARD_PIXHAWK2);1123(void)all_external; // in case all backends using this are compiled out1124#endif // AP_COMPASS_INTERNAL_BUS_PROBING_ENABLED11251126#if AP_COMPASS_HMC5843_ENABLED1127// external i2c bus1128FOREACH_I2C_EXTERNAL(i) {1129probe_i2c_dev(DRIVER_HMC5843, AP_Compass_HMC5843::probe, i, HAL_COMPASS_HMC5843_I2C_ADDR, true, ROTATION_ROLL_180);1130RETURN_IF_NO_SPACE;1131}11321133#if AP_COMPASS_HMC5843_INTERNAL_BUS_PROBING_ENABLED1134if (AP_BoardConfig::get_board_type() != AP_BoardConfig::PX4_BOARD_AEROFC) {1135// internal i2c bus1136FOREACH_I2C_INTERNAL(i) {1137probe_i2c_dev(DRIVER_HMC5843, AP_Compass_HMC5843::probe, i, HAL_COMPASS_HMC5843_I2C_ADDR, all_external, all_external?ROTATION_ROLL_180:ROTATION_YAW_270);1138RETURN_IF_NO_SPACE;1139}1140}1141#endif // AP_COMPASS_KMC5843_INTERNAL_BUS_PROBING_ENABLED1142#endif // AP_COMPASS_HMC5843_ENABLED11431144#if AP_COMPASS_QMC5883L_ENABLED1145//external i2c bus1146FOREACH_I2C_EXTERNAL(i) {1147probe_i2c_dev(DRIVER_QMC5883L, AP_Compass_QMC5883L::probe, i, HAL_COMPASS_QMC5883L_I2C_ADDR, true, HAL_COMPASS_QMC5883L_ORIENTATION_EXTERNAL);1148RETURN_IF_NO_SPACE;1149}11501151// internal i2c bus1152#if AP_COMPASS_INTERNAL_BUS_PROBING_ENABLED1153if (all_external) {1154// only probe QMC5883L on internal if we are treating internals as externals1155FOREACH_I2C_INTERNAL(i) {1156probe_i2c_dev(DRIVER_QMC5883L, AP_Compass_QMC5883L::probe, i, HAL_COMPASS_QMC5883L_I2C_ADDR, all_external, all_external?HAL_COMPASS_QMC5883L_ORIENTATION_EXTERNAL:HAL_COMPASS_QMC5883L_ORIENTATION_INTERNAL);1157RETURN_IF_NO_SPACE;1158}1159}1160#endif // AP_COMPASS_INTERNAL_BUS_PROBING_ENABLED1161#endif // AP_COMPASS_QMC5883L_ENABLED11621163#if AP_COMPASS_QMC5883P_ENABLED1164//external i2c bus1165FOREACH_I2C_EXTERNAL(i) {1166probe_i2c_dev(DRIVER_QMC5883P, AP_Compass_QMC5883P::probe, i, HAL_COMPASS_QMC5883P_I2C_ADDR, true, HAL_COMPASS_QMC5883P_ORIENTATION_EXTERNAL);1167RETURN_IF_NO_SPACE;1168}11691170// internal i2c bus1171#if AP_COMPASS_INTERNAL_BUS_PROBING_ENABLED1172if (all_external) {1173// only probe QMC5883P on internal if we are treating internals as externals1174FOREACH_I2C_INTERNAL(i) {1175probe_i2c_dev(DRIVER_QMC5883P, AP_Compass_QMC5883P::probe, i, HAL_COMPASS_QMC5883P_I2C_ADDR, all_external, all_external?HAL_COMPASS_QMC5883P_ORIENTATION_EXTERNAL:HAL_COMPASS_QMC5883P_ORIENTATION_INTERNAL);1176RETURN_IF_NO_SPACE;1177}1178}1179#endif // AP_COMPASS_INTERNAL_BUS_PROBING_ENABLED1180#endif // AP_COMPASS_QMC5883P_ENABLED11811182#if AP_COMPASS_IIS2MDC_ENABLED1183//external i2c bus1184FOREACH_I2C_EXTERNAL(i) {1185probe_i2c_dev(DRIVER_IIS2MDC, AP_Compass_IIS2MDC::probe, i, HAL_COMPASS_IIS2MDC_I2C_ADDR, true, HAL_COMPASS_IIS2MDC_ORIENTATION_EXTERNAL);1186RETURN_IF_NO_SPACE;1187}11881189// internal i2c bus1190#if AP_COMPASS_INTERNAL_BUS_PROBING_ENABLED1191if (all_external) {1192// only probe IIS2MDC on internal if we are treating internals as externals1193FOREACH_I2C_INTERNAL(i) {1194probe_i2c_dev(DRIVER_IIS2MDC, AP_Compass_IIS2MDC::probe, i, HAL_COMPASS_IIS2MDC_I2C_ADDR, all_external, all_external?HAL_COMPASS_IIS2MDC_ORIENTATION_EXTERNAL:HAL_COMPASS_IIS2MDC_ORIENTATION_INTERNAL);1195RETURN_IF_NO_SPACE;1196}1197}1198#endif // AP_COMPASS_INTERNAL_BUS_PROBING_ENABLED1199#endif // AP_COMPASS_QMC5883P_ENABLED12001201// AK09916 on ICM209481202#if AP_COMPASS_AK09916_ENABLED && AP_COMPASS_ICM20948_ENABLED1203FOREACH_I2C_EXTERNAL(i) {1204probe_ak09916_via_icm20948(i, HAL_COMPASS_AK09916_I2C_ADDR, HAL_COMPASS_ICM20948_I2C_ADDR, true, ROTATION_PITCH_180_YAW_90);1205RETURN_IF_NO_SPACE;1206probe_ak09916_via_icm20948(i, HAL_COMPASS_AK09916_I2C_ADDR, HAL_COMPASS_ICM20948_I2C_ADDR2, true, ROTATION_PITCH_180_YAW_90);1207RETURN_IF_NO_SPACE;1208}12091210#if AP_COMPASS_INTERNAL_BUS_PROBING_ENABLED1211FOREACH_I2C_INTERNAL(i) {1212probe_ak09916_via_icm20948(i, HAL_COMPASS_AK09916_I2C_ADDR, HAL_COMPASS_ICM20948_I2C_ADDR, all_external, ROTATION_PITCH_180_YAW_90);1213RETURN_IF_NO_SPACE;1214probe_ak09916_via_icm20948(i, HAL_COMPASS_AK09916_I2C_ADDR, HAL_COMPASS_ICM20948_I2C_ADDR2, all_external, ROTATION_PITCH_180_YAW_90);1215RETURN_IF_NO_SPACE;1216}1217#endif // AP_COMPASS_INTERNAL_BUS_PROBING_ENABLED1218#endif // AP_COMPASS_AK09916_ENABLED && AP_COMPASS_ICM20948_ENABLED12191220#if AP_COMPASS_LIS3MDL_ENABLED1221// lis3mdl on bus 0 with default address1222#if AP_COMPASS_INTERNAL_BUS_PROBING_ENABLED1223FOREACH_I2C_INTERNAL(i) {1224probe_i2c_dev(DRIVER_LIS3MDL, AP_Compass_LIS3MDL::probe, i, HAL_COMPASS_LIS3MDL_I2C_ADDR, all_external, all_external?ROTATION_YAW_90:ROTATION_NONE);1225RETURN_IF_NO_SPACE;1226}12271228// lis3mdl on bus 0 with alternate address1229FOREACH_I2C_INTERNAL(i) {1230probe_i2c_dev(DRIVER_LIS3MDL, AP_Compass_LIS3MDL::probe, i, HAL_COMPASS_LIS3MDL_I2C_ADDR2, all_external, all_external?ROTATION_YAW_90:ROTATION_NONE);1231RETURN_IF_NO_SPACE;1232}1233#endif1234// external lis3mdl on bus 1 with default address1235FOREACH_I2C_EXTERNAL(i) {1236probe_i2c_dev(DRIVER_LIS3MDL, AP_Compass_LIS3MDL::probe, i, HAL_COMPASS_LIS3MDL_I2C_ADDR, true, ROTATION_YAW_90);1237RETURN_IF_NO_SPACE;1238}12391240// external lis3mdl on bus 1 with alternate address1241FOREACH_I2C_EXTERNAL(i) {1242probe_i2c_dev(DRIVER_LIS3MDL, AP_Compass_LIS3MDL::probe, i, HAL_COMPASS_LIS3MDL_I2C_ADDR2, true, ROTATION_YAW_90);1243RETURN_IF_NO_SPACE;1244}1245#endif // AP_COMPASS_LIS3MDL_ENABLED12461247#if AP_COMPASS_LIS2MDL_ENABLED1248// external lis2mdl1249#if AP_COMPASS_LIS2MDL_EXTERNAL_BUS_PROBING_ENABLED1250FOREACH_I2C_EXTERNAL(i) {1251probe_i2c_dev(DRIVER_LIS2MDL, AP_Compass_LIS2MDL::probe, i, HAL_COMPASS_LIS2MDL_I2C_ADDR, true, ROTATION_NONE);1252RETURN_IF_NO_SPACE;1253}1254#endif1255// internal lis2mdl1256#if AP_COMPASS_INTERNAL_BUS_PROBING_ENABLED1257FOREACH_I2C_INTERNAL(i) {1258probe_i2c_dev(DRIVER_LIS2MDL, AP_Compass_LIS2MDL::probe, i, HAL_COMPASS_LIS2MDL_I2C_ADDR, all_external, ROTATION_NONE);1259RETURN_IF_NO_SPACE;1260}1261#endif1262#endif // AP_COMPASS_LIS2MDL_ENABLED12631264#if AP_COMPASS_AK09916_ENABLED1265// AK09916. This can be found twice, due to the ICM20948 i2c bus pass-thru, so we need to be careful to avoid that1266FOREACH_I2C_EXTERNAL(i) {1267probe_i2c_dev(DRIVER_AK09916, AP_Compass_AK09916::probe, i, HAL_COMPASS_AK09916_I2C_ADDR, true, ROTATION_YAW_270);1268RETURN_IF_NO_SPACE;1269}1270#if AP_COMPASS_INTERNAL_BUS_PROBING_ENABLED1271FOREACH_I2C_INTERNAL(i) {1272probe_i2c_dev(DRIVER_AK09916, AP_Compass_AK09916::probe, i, HAL_COMPASS_AK09916_I2C_ADDR, all_external, all_external?ROTATION_YAW_270:ROTATION_NONE);1273RETURN_IF_NO_SPACE;1274}1275#endif // AP_COMPASS_INTERNAL_BUS_PROBING_ENABLED1276#endif // AP_COMPASS_AK09916_ENABLED12771278#if AP_COMPASS_IST8310_EXTERNAL_BUS_PROBING_ENABLED || AP_COMPASS_IST8310_INTERNAL_BUS_PROBING_ENABLED1279// IST8310 on external and internal bus1280if (AP_BoardConfig::get_board_type() != AP_BoardConfig::PX4_BOARD_FMUV6) {1281enum Rotation default_rotation = AP_COMPASS_IST8310_DEFAULT_ROTATION;12821283if (AP_BoardConfig::get_board_type() == AP_BoardConfig::PX4_BOARD_AEROFC) {1284default_rotation = ROTATION_PITCH_180_YAW_90;1285}1286// probe all 4 possible addresses1287const uint8_t ist8310_addr[] = { 0x0C, 0x0D, 0x0E, 0x0F };12881289for (uint8_t a=0; a<ARRAY_SIZE(ist8310_addr); a++) {1290#if AP_COMPASS_IST8310_EXTERNAL_BUS_PROBING_ENABLED1291FOREACH_I2C_EXTERNAL(i) {1292probe_i2c_dev(DRIVER_IST8310, AP_Compass_IST8310::probe, i, ist8310_addr[a], true, default_rotation);1293RETURN_IF_NO_SPACE;1294}1295#endif // AP_COMPASS_IST8310_EXTERNAL_BUS_PROBING_ENABLED1296#if AP_COMPASS_IST8310_INTERNAL_BUS_PROBING_ENABLED1297FOREACH_I2C_INTERNAL(i) {1298probe_i2c_dev(DRIVER_IST8310, AP_Compass_IST8310::probe, i, ist8310_addr[a], all_external, default_rotation);1299RETURN_IF_NO_SPACE;1300}1301#endif // AP_COMPASS_IST8310_INTERNAL_BUS_PROBING_ENABLED1302}1303}1304#endif // AP_COMPASS_IST8310_EXTERNAL_BUS_PROBING_ENABLED || AP_COMPASS_IST8310_INTERNAL_BUS_PROBING_ENABLED13051306#if AP_COMPASS_IST8308_ENABLED1307// external i2c bus1308FOREACH_I2C_EXTERNAL(i) {1309probe_i2c_dev(DRIVER_IST8308, AP_Compass_IST8308::probe, i, HAL_COMPASS_IST8308_I2C_ADDR, true, ROTATION_NONE);1310RETURN_IF_NO_SPACE;1311}1312#if AP_COMPASS_INTERNAL_BUS_PROBING_ENABLED1313FOREACH_I2C_INTERNAL(i) {1314probe_i2c_dev(DRIVER_IST8308, AP_Compass_IST8308::probe, i, HAL_COMPASS_IST8308_I2C_ADDR, all_external, ROTATION_NONE);1315RETURN_IF_NO_SPACE;1316}1317#endif // AP_COMPASS_INTERNAL_BUS_PROBING_ENABLED1318#endif // AP_COMPASS_IST8308_ENABLED13191320#if AP_COMPASS_MMC3416_ENABLED1321// external i2c bus1322FOREACH_I2C_EXTERNAL(i) {1323probe_i2c_dev(DRIVER_MMC3416, AP_Compass_MMC3416::probe, i, HAL_COMPASS_MMC3416_I2C_ADDR, true, ROTATION_NONE);1324RETURN_IF_NO_SPACE;1325}1326#if AP_COMPASS_INTERNAL_BUS_PROBING_ENABLED1327FOREACH_I2C_INTERNAL(i) {1328probe_i2c_dev(DRIVER_MMC3416, AP_Compass_MMC3416::probe, i, HAL_COMPASS_MMC3416_I2C_ADDR, all_external, ROTATION_NONE);1329RETURN_IF_NO_SPACE;1330}1331#endif // AP_COMPASS_INTERNAL_BUS_PROBING_ENABLED1332#endif // AP_COMPASS_MMC3416_ENABLED13331334#if AP_COMPASS_MMC5XX3_ENABLED1335// external i2c bus1336FOREACH_I2C_EXTERNAL(i) {1337probe_i2c_dev(DRIVER_MMC5XX3, AP_Compass_MMC5XX3::probe, i, HAL_COMPASS_MMC5xx3_I2C_ADDR, true, ROTATION_NONE);1338RETURN_IF_NO_SPACE;1339}1340#endif // AP_COMPASS_MMC5XX3_ENABLED (MMC5983MA)13411342#if AP_COMPASS_RM3100_ENABLED1343#ifdef HAL_COMPASS_RM3100_I2C_ADDR1344const uint8_t rm3100_addresses[] = { HAL_COMPASS_RM3100_I2C_ADDR };1345#else1346// RM3100 can be on 4 different addresses1347const uint8_t rm3100_addresses[] = { HAL_COMPASS_RM3100_I2C_ADDR1,1348HAL_COMPASS_RM3100_I2C_ADDR2,1349HAL_COMPASS_RM3100_I2C_ADDR3,1350HAL_COMPASS_RM3100_I2C_ADDR4 };1351#endif1352// external i2c bus1353FOREACH_I2C_EXTERNAL(i) {1354for (uint8_t j=0; j<ARRAY_SIZE(rm3100_addresses); j++) {1355probe_i2c_dev(DRIVER_RM3100, AP_Compass_RM3100::probe, i, rm3100_addresses[j], true, ROTATION_NONE);1356RETURN_IF_NO_SPACE;1357}1358}13591360#if AP_COMPASS_INTERNAL_BUS_PROBING_ENABLED1361FOREACH_I2C_INTERNAL(i) {1362for (uint8_t j=0; j<ARRAY_SIZE(rm3100_addresses); j++) {1363probe_i2c_dev(DRIVER_RM3100, AP_Compass_RM3100::probe, i, rm3100_addresses[j], all_external, ROTATION_NONE);1364RETURN_IF_NO_SPACE;1365}1366}1367#endif // AP_COMPASS_INTERNAL_BUS_PROBING_ENABLED1368#endif // AP_COMPASS_RM3100_ENABLED13691370#if AP_COMPASS_BMM150_ENABLED1371// BMM150 on I2C1372FOREACH_I2C_EXTERNAL(i) {1373for (uint8_t addr=BMM150_I2C_ADDR_MIN; addr <= BMM150_I2C_ADDR_MAX; addr++) {1374probe_i2c_dev(DRIVER_BMM150, AP_Compass_BMM150::probe, i, addr, true, ROTATION_NONE);1375RETURN_IF_NO_SPACE;1376}1377}1378#endif // AP_COMPASS_BMM150_ENABLED13791380#if AP_COMPASS_BMM350_ENABLED1381// BMM350 on I2C1382FOREACH_I2C_EXTERNAL(i) {1383for (uint8_t addr=BMM350_I2C_ADDR_MIN; addr <= BMM350_I2C_ADDR_MAX; addr++) {1384probe_i2c_dev(DRIVER_BMM350, AP_Compass_BMM350::probe, i, addr, true, ROTATION_NONE);1385RETURN_IF_NO_SPACE;1386}1387}1388#if AP_COMPASS_INTERNAL_BUS_PROBING_ENABLED1389FOREACH_I2C_INTERNAL(i) {1390for (uint8_t addr=BMM350_I2C_ADDR_MIN; addr <= BMM350_I2C_ADDR_MAX; addr++) {1391probe_i2c_dev(DRIVER_BMM350, AP_Compass_BMM350::probe, i, addr, all_external, ROTATION_NONE);1392RETURN_IF_NO_SPACE;1393}1394}1395#endif // AP_COMPASS_INTERNAL_BUS_PROBING_ENABLED1396#endif // AP_COMPASS_BMM350_ENABLED1397}13981399/*1400detect available backends for this board1401*/1402void Compass::_detect_backends(void)1403{1404#if AP_COMPASS_EXTERNALAHRS_ENABLED1405const int8_t serial_port = AP::externalAHRS().get_port(AP_ExternalAHRS::AvailableSensor::COMPASS);1406if (serial_port >= 0) {1407add_backend(DRIVER_EXTERNALAHRS, AP_Compass_ExternalAHRS::probe(serial_port));1408RETURN_IF_NO_SPACE;1409}1410#endif14111412#if AP_FEATURE_BOARD_DETECT1413if (AP_BoardConfig::get_board_type() == AP_BoardConfig::PX4_BOARD_PIXHAWK2) {1414// default to disabling LIS3MDL on pixhawk2 due to hardware issue1415#if AP_COMPASS_LIS3MDL_ENABLED1416_driver_type_mask.set_default(1U<<DRIVER_LIS3MDL);1417#endif1418}1419#endif14201421#if AP_COMPASS_SITL_ENABLED && !AP_TEST_DRONECAN_DRIVERS1422// create several SITL compass backends:1423for (uint8_t i=0; i<MAX_CONNECTED_MAGS; i++) {1424add_backend(DRIVER_SITL, NEW_NOTHROW AP_Compass_SITL(i));1425RETURN_IF_NO_SPACE;1426}1427#endif14281429#if AP_COMPASS_DRONECAN_ENABLED1430// probe DroneCAN before I2C and SPI so that DroneCAN compasses1431// default to first in the list for a new board1432probe_dronecan_compasses();1433RETURN_IF_NO_SPACE;1434#endif14351436#if AP_COMPASS_PROBING_ENABLED1437// allow boards to ask for external probing of all i2c compass types in hwdef.dat1438_probe_external_i2c_compasses();1439RETURN_IF_NO_SPACE;1440#endif // AP_COMPASS_PROBING_ENABLED14411442#if AP_COMPASS_MSP_ENABLED1443for (uint8_t i=0; i<8; i++) {1444if (msp_instance_mask & (1U<<i)) {1445auto *backend = AP_Compass_MSP::probe(i);1446if (backend == nullptr) {1447continue;1448}1449add_backend(DRIVER_MSP, backend);1450RETURN_IF_NO_SPACE;1451}1452}1453#endif14541455// finally look for i2c and spi compasses not found yet1456RETURN_IF_NO_SPACE;1457probe_i2c_spi_compasses();14581459if (_backend_count == 0 ||1460_compass_count == 0) {1461DEV_PRINTF("No Compass backends available\n");1462}1463}14641465/*1466probe i2c and SPI compasses1467*/1468void Compass::probe_i2c_spi_compasses(void)1469{1470#if defined(HAL_MAG_PROBE_LIST)1471// driver probes defined by COMPASS lines in hwdef.dat1472HAL_MAG_PROBE_LIST;1473#elif AP_FEATURE_BOARD_DETECT1474switch (AP_BoardConfig::get_board_type()) {1475case AP_BoardConfig::PX4_BOARD_PX4V1:1476case AP_BoardConfig::PX4_BOARD_PIXHAWK:1477case AP_BoardConfig::PX4_BOARD_PHMINI:1478case AP_BoardConfig::PX4_BOARD_AUAV21:1479case AP_BoardConfig::PX4_BOARD_PH2SLIM:1480case AP_BoardConfig::PX4_BOARD_PIXHAWK2:1481case AP_BoardConfig::PX4_BOARD_FMUV6:1482case AP_BoardConfig::PX4_BOARD_AEROFC:1483_probe_external_i2c_compasses();1484RETURN_IF_NO_SPACE;1485break;14861487default:1488break;1489}1490switch (AP_BoardConfig::get_board_type()) {1491case AP_BoardConfig::PX4_BOARD_PIXHAWK:1492#if AP_COMPASS_HMC5843_ENABLED1493probe_spi_dev(DRIVER_HMC5843, AP_Compass_HMC5843::probe, HAL_COMPASS_HMC5843_NAME, false, ROTATION_PITCH_180);1494RETURN_IF_NO_SPACE;1495#endif1496#if AP_COMPASS_LSM303D_ENABLED1497probe_spi_dev(DRIVER_LSM303D, AP_Compass_LSM303D::probe, HAL_INS_LSM9DS0_A_NAME, ROTATION_NONE);1498RETURN_IF_NO_SPACE;1499#endif1500break;15011502case AP_BoardConfig::PX4_BOARD_PIXHAWK2:1503#if AP_COMPASS_LSM303D_ENABLED1504probe_spi_dev(DRIVER_LSM303D, AP_Compass_LSM303D::probe, HAL_INS_LSM9DS0_EXT_A_NAME, ROTATION_YAW_270);1505RETURN_IF_NO_SPACE;1506#endif1507#if AP_COMPASS_AK8963_ENABLED1508// we run the AK8963 only on the 2nd MPU9250, which leaves the1509// first MPU9250 to run without disturbance at high rate1510probe_ak8963_via_mpu9250(1, ROTATION_YAW_270);1511RETURN_IF_NO_SPACE;1512#endif1513#if AP_COMPASS_AK09916_ENABLED && AP_COMPASS_ICM20948_ENABLED1514probe_ak09916_via_icm20948(0, ROTATION_ROLL_180_YAW_90);1515RETURN_IF_NO_SPACE;1516#endif1517break;15181519case AP_BoardConfig::PX4_BOARD_FMUV6:1520#if AP_COMPASS_IST8310_ENABLED1521FOREACH_I2C_EXTERNAL(i) {1522probe_i2c_dev(DRIVER_IST8310, AP_Compass_IST8310::probe, i, HAL_COMPASS_IST8310_I2C_ADDR, true, ROTATION_ROLL_180_YAW_90);1523RETURN_IF_NO_SPACE;1524}1525FOREACH_I2C_INTERNAL(i) {1526probe_i2c_dev(DRIVER_IST8310, AP_Compass_IST8310::probe, i, HAL_COMPASS_IST8310_I2C_ADDR, false, ROTATION_ROLL_180_YAW_90);1527RETURN_IF_NO_SPACE;1528}1529#endif // AP_COMPASS_IST8310_ENABLED1530break;15311532case AP_BoardConfig::PX4_BOARD_PHMINI:1533#if AP_COMPASS_AK8963_ENABLED1534probe_ak8963_via_mpu9250(0, ROTATION_ROLL_180);1535RETURN_IF_NO_SPACE;1536#endif1537break;15381539case AP_BoardConfig::PX4_BOARD_AUAV21:1540#if AP_COMPASS_AK8963_ENABLED1541probe_ak8963_via_mpu9250(0, ROTATION_ROLL_180_YAW_90);1542RETURN_IF_NO_SPACE;1543#endif1544break;15451546case AP_BoardConfig::PX4_BOARD_PH2SLIM:1547#if AP_COMPASS_AK8963_ENABLED1548probe_ak8963_via_mpu9250(0, ROTATION_YAW_270);1549RETURN_IF_NO_SPACE;1550#endif1551break;15521553default:1554break;1555}1556#endif1557}15581559#if AP_COMPASS_AK8963_ENABLED1560void Compass::probe_ak8963_via_mpu9250(uint8_t imu_instance, Rotation rotation)1561{1562if (!_driver_enabled(DRIVER_AK8963)) {1563return;1564}1565auto *backend = AP_Compass_AK8963::probe_mpu9250(imu_instance, rotation);1566add_backend(DRIVER_AK8963, backend); // add_backend does nullptr check1567}1568#endif // AP_COMPASS_AK8963_ENABLED15691570#if AP_COMPASS_AK09916_ENABLED && AP_COMPASS_ICM20948_ENABLED1571void Compass::probe_ak09916_via_icm20948(uint8_t i2c_bus, uint8_t ak09916_addr, uint8_t icm20948_addr, bool external, Rotation rotation)1572{1573if (!_driver_enabled(DRIVER_ICM20948)) {1574return;1575}1576auto *backend = AP_Compass_AK09916::probe_ICM20948(1577GET_I2C_DEVICE(i2c_bus, ak09916_addr),1578GET_I2C_DEVICE(i2c_bus, icm20948_addr),1579external,1580rotation1581);15821583add_backend(DRIVER_ICM20948, backend); // add_backend does nullptr check1584}15851586void Compass::probe_ak09916_via_icm20948(uint8_t ins_instance, Rotation rotation)1587{1588if (!_driver_enabled(DRIVER_AK09916)) {1589return;1590}15911592auto *backend = AP_Compass_AK09916::probe_ICM20948(ins_instance, rotation);15931594add_backend(DRIVER_AK09916, backend);1595}15961597#endif // #if AP_COMPASS_AK09916_ENABLED && AP_COMPASS_ICM20948_ENABLED15981599void Compass::probe_i2c_dev(DriverType driver_type, probe_i2c_dev_probefn_t probefn, uint8_t i2c_bus, uint8_t i2c_addr, bool external, Rotation rotation)1600{1601if (!_driver_enabled(driver_type)) {1602return;1603}1604auto *backend = probefn(1605GET_I2C_DEVICE(i2c_bus, i2c_addr),1606external,1607rotation1608);16091610add_backend(driver_type, backend); // add_backend does nullptr check1611}16121613void Compass::probe_spi_dev(DriverType driver_type, probe_spi_dev_probefn_t probefn, const char *name, bool external, Rotation rotation)1614{1615if (!_driver_enabled(driver_type)) {1616return;1617}1618auto *backend = probefn(hal.spi->get_device(name), external, rotation);16191620add_backend(driver_type, backend); // add_backend does nullptr check1621}16221623// short-lived method which expectes a probe function that doesn't1624// offer the ability to specify an external rotation:1625void Compass::probe_spi_dev(DriverType driver_type, probe_spi_dev_noexternal_probefn_t probefn, const char *name, Rotation rotation)1626{1627if (!_driver_enabled(driver_type)) {1628return;1629}1630auto *backend = probefn(hal.spi->get_device(name), rotation);16311632add_backend(driver_type, backend); // add_backend does nullptr check1633}16341635#if AP_COMPASS_DRONECAN_ENABLED1636/*1637look for DroneCAN compasses1638*/1639void Compass::probe_dronecan_compasses(void)1640{1641if (!_driver_enabled(DRIVER_UAVCAN)) {1642return;1643}1644for (uint8_t i=0; i<MAX_CONNECTED_MAGS; i++) {1645AP_Compass_Backend* _uavcan_backend = AP_Compass_DroneCAN::probe(i);1646if (_uavcan_backend) {1647add_backend(DRIVER_UAVCAN, _uavcan_backend);1648}1649#if COMPASS_MAX_UNREG_DEV > 01650if (_unreg_compass_count == COMPASS_MAX_UNREG_DEV) {1651break;1652}1653#endif1654}16551656#if COMPASS_MAX_UNREG_DEV > 01657if (option_set(Option::ALLOW_DRONECAN_AUTO_REPLACEMENT) && !suppress_devid_save) {1658// check if there's any uavcan compass in prio slot that's not found1659// and replace it if there's a replacement compass1660for (Priority i(0); i<COMPASS_MAX_INSTANCES; i++) {1661if (AP_HAL::Device::devid_get_bus_type(_priority_did_list[i]) != AP_HAL::Device::BUS_TYPE_UAVCAN1662|| _get_state(i).registered) {1663continue;1664}1665// There's a UAVCAN compass missing1666// Let's check if there's a replacement1667for (uint8_t j=0; j<COMPASS_MAX_INSTANCES; j++) {1668uint32_t detected_devid = AP_Compass_DroneCAN::get_detected_devid(j);1669// Check if this is a potential replacement mag1670if (!is_replacement_mag(detected_devid)) {1671continue;1672}1673// We have found a replacement mag, let's replace the existing one1674// with this by setting the priority to zero and calling uavcan probe1675GCS_SEND_TEXT(MAV_SEVERITY_ALERT, "Mag: Compass #%d with DEVID %lu replaced", uint8_t(i), (unsigned long)_priority_did_list[i]);1676_priority_did_stored_list[i].set_and_save(0);1677_priority_did_list[i] = 0;16781679AP_Compass_Backend* _uavcan_backend = AP_Compass_DroneCAN::probe(j);1680if (_uavcan_backend) {1681add_backend(DRIVER_UAVCAN, _uavcan_backend);1682// we also need to remove the id from unreg list1683remove_unreg_dev_id(detected_devid);1684} else {1685// the mag has already been allocated,1686// let's begin the replacement1687bool found_replacement = false;1688for (StateIndex k(0); k<COMPASS_MAX_INSTANCES; k++) {1689if ((uint32_t)_state[k].dev_id == detected_devid) {1690if (_state[k].priority <= uint8_t(i)) {1691// we are already on higher priority1692// nothing to do1693break;1694}1695found_replacement = true;1696// reset old priority of replacement mag1697_priority_did_stored_list[_state[k].priority].set_and_save(0);1698_priority_did_list[_state[k].priority] = 0;1699// update new priority1700_state[k].priority = i;1701}1702}1703if (!found_replacement) {1704continue;1705}1706_priority_did_stored_list[i].set_and_save(detected_devid);1707_priority_did_list[i] = detected_devid;1708}1709}1710}1711}1712#endif // #if COMPASS_MAX_UNREG_DEV > 01713}1714#endif // AP_COMPASS_DRONECAN_ENABLED171517161717// Check if the devid is a potential replacement compass1718// Following are the checks done to ensure the compass is a replacement1719// * The compass is an UAVCAN compass1720// * The compass wasn't seen before this boot as additional unreg mag1721// * The compass might have been seen before but never setup1722bool Compass::is_replacement_mag(uint32_t devid) {1723#if COMPASS_MAX_INSTANCES > 11724// We only do this for UAVCAN mag1725if (devid == 0 || (AP_HAL::Device::devid_get_bus_type(devid) != AP_HAL::Device::BUS_TYPE_UAVCAN)) {1726return false;1727}17281729#if COMPASS_MAX_UNREG_DEV > 01730// Check that its not an unused additional mag1731for (uint8_t i = 0; i<COMPASS_MAX_UNREG_DEV; i++) {1732if (_previously_unreg_mag[i] == devid) {1733return false;1734}1735}1736#endif17371738// Check that its not previously setup mag1739for (StateIndex i(0); i<COMPASS_MAX_INSTANCES; i++) {1740if ((uint32_t)_state[i].expected_dev_id == devid) {1741return false;1742}1743}1744#endif1745return true;1746}17471748void Compass::remove_unreg_dev_id(uint32_t devid)1749{1750#if COMPASS_MAX_INSTANCES > 11751// We only do this for UAVCAN mag1752if (devid == 0 || (AP_HAL::Device::devid_get_bus_type(devid) != AP_HAL::Device::BUS_TYPE_UAVCAN)) {1753return;1754}17551756#if COMPASS_MAX_UNREG_DEV > 01757for (uint8_t i = 0; i<COMPASS_MAX_UNREG_DEV; i++) {1758if ((uint32_t)extra_dev_id[i] == devid) {1759extra_dev_id[i].set(0);1760return;1761}1762}1763#endif1764#endif1765}17661767void Compass::_reset_compass_id()1768{1769#if COMPASS_MAX_INSTANCES > 11770// Check if any of the registered devs are not registered1771for (Priority i(0); i<COMPASS_MAX_INSTANCES; i++) {1772if (_priority_did_stored_list[i] != _priority_did_list[i] ||1773_priority_did_stored_list[i] == 0) {1774//We don't touch priorities that might have been touched by the user1775continue;1776}1777if (!_get_state(i).registered) {1778_priority_did_stored_list[i].set_and_save(0);1779GCS_SEND_TEXT(MAV_SEVERITY_ALERT, "Mag: Compass #%d with DEVID %lu removed", uint8_t(i), (unsigned long)_priority_did_list[i]);1780}1781}17821783// Check if any of the old registered devs are not registered1784// and hence can be removed1785for (StateIndex i(0); i<COMPASS_MAX_INSTANCES; i++) {1786if (_state[i].dev_id == 0 && _state[i].expected_dev_id != 0) {1787// also hard reset dev_ids that are not detected1788_state[i].dev_id.save();1789}1790}1791#endif1792}17931794// Look for devices beyond initialisation1795void1796Compass::_detect_runtime(void)1797{1798#if AP_COMPASS_DRONECAN_ENABLED1799if (!available()) {1800return;1801}18021803//Don't try to add device while armed1804if (hal.util->get_soft_armed()) {1805return;1806}1807static uint32_t last_try;1808//Try once every second1809if ((AP_HAL::millis() - last_try) < 1000) {1810return;1811}1812last_try = AP_HAL::millis();1813if (_driver_enabled(DRIVER_UAVCAN)) {1814for (uint8_t i=0; i<COMPASS_MAX_BACKEND; i++) {1815AP_Compass_Backend* _uavcan_backend = AP_Compass_DroneCAN::probe(i);1816if (_uavcan_backend) {1817add_backend(DRIVER_UAVCAN, _uavcan_backend);1818}1819RETURN_IF_NO_SPACE;1820}1821}1822#endif // AP_COMPASS_DRONECAN_ENABLED1823}18241825bool1826Compass::read(void)1827{1828if (!available()) {1829return false;1830}18311832#if HAL_LOGGING_ENABLED1833const bool old_healthy = healthy();1834#endif18351836#ifndef HAL_BUILD_AP_PERIPH1837if (!_initial_location_set) {1838try_set_initial_location();1839}1840#endif18411842_detect_runtime();18431844for (uint8_t i=0; i< _backend_count; i++) {1845// call read on each of the backend. This call updates field[i]1846_backends[i]->read();1847}1848uint32_t time = AP_HAL::millis();1849bool any_healthy = false;1850for (StateIndex i(0); i < COMPASS_MAX_INSTANCES; i++) {1851_state[i].healthy = (time - _state[i].last_update_ms < 500);1852any_healthy |= _state[i].healthy;1853}1854#if COMPASS_LEARN_ENABLED1855if (_learn == LearnType::INFLIGHT && !learn_allocated) {1856learn_allocated = true;1857learn = NEW_NOTHROW CompassLearn(*this);1858}1859if (_learn == LearnType::INFLIGHT && learn != nullptr) {1860learn->update();1861}1862#endif1863#if HAL_LOGGING_ENABLED1864if (any_healthy && _log_bit != (uint32_t)-1 && AP::logger().should_log(_log_bit)) {1865AP::logger().Write_Compass();1866}1867#endif18681869// Set _first_usable parameter1870for (Priority i(0); i<COMPASS_MAX_INSTANCES; i++) {1871if (_use_for_yaw[i]) {1872_first_usable = uint8_t(i);1873break;1874}1875}1876const bool new_healthy = healthy();18771878#if HAL_LOGGING_ENABLED18791880#define MASK_LOG_ANY 0xFFFF18811882if (new_healthy != old_healthy) {1883if (AP::logger().should_log(MASK_LOG_ANY)) {1884const LogErrorCode code = new_healthy ? LogErrorCode::ERROR_RESOLVED : LogErrorCode::UNHEALTHY;1885AP::logger().Write_Error(LogErrorSubsystem::COMPASS, code);1886}1887}1888#endif18891890return new_healthy;1891}18921893uint8_t1894Compass::get_healthy_mask() const1895{1896uint8_t healthy_mask = 0;1897for (Priority i(0); i<COMPASS_MAX_INSTANCES; i++) {1898if (healthy(uint8_t(i))) {1899healthy_mask |= 1 << uint8_t(i);1900}1901}1902return healthy_mask;1903}19041905void1906Compass::set_offsets(uint8_t i, const Vector3f &offsets)1907{1908// sanity check compass instance provided1909StateIndex id = _get_state_id(Priority(i));1910if (id < COMPASS_MAX_INSTANCES) {1911_state[id].offset.set(offsets);1912}1913}19141915void1916Compass::set_and_save_offsets(uint8_t i, const Vector3f &offsets)1917{1918// sanity check compass instance provided1919StateIndex id = _get_state_id(Priority(i));1920if (id < COMPASS_MAX_INSTANCES) {1921_state[id].offset.set(offsets);1922save_offsets(i);1923}1924}19251926#if AP_COMPASS_DIAGONALS_ENABLED1927void1928Compass::set_and_save_diagonals(uint8_t i, const Vector3f &diagonals)1929{1930// sanity check compass instance provided1931StateIndex id = _get_state_id(Priority(i));1932if (id < COMPASS_MAX_INSTANCES) {1933_state[id].diagonals.set_and_save(diagonals);1934}1935}19361937void1938Compass::set_and_save_offdiagonals(uint8_t i, const Vector3f &offdiagonals)1939{1940// sanity check compass instance provided1941StateIndex id = _get_state_id(Priority(i));1942if (id < COMPASS_MAX_INSTANCES) {1943_state[id].offdiagonals.set_and_save(offdiagonals);1944}1945}1946#endif // AP_COMPASS_DIAGONALS_ENABLED19471948void1949Compass::set_and_save_scale_factor(uint8_t i, float scale_factor)1950{1951StateIndex id = _get_state_id(Priority(i));1952if (i < COMPASS_MAX_INSTANCES) {1953_state[id].scale_factor.set_and_save(scale_factor);1954}1955}19561957void1958Compass::save_offsets(uint8_t i)1959{1960StateIndex id = _get_state_id(Priority(i));1961if (id < COMPASS_MAX_INSTANCES) {1962_state[id].offset.save(); // save offsets1963_state[id].dev_id.set_and_save(_state[id].detected_dev_id);1964}1965}19661967void1968Compass::set_and_save_orientation(uint8_t i, Rotation orientation)1969{1970StateIndex id = _get_state_id(Priority(i));1971if (id < COMPASS_MAX_INSTANCES) {1972_state[id].orientation.set_and_save_ifchanged(orientation);1973}1974}19751976void1977Compass::save_offsets(void)1978{1979for (Priority i(0); i<COMPASS_MAX_INSTANCES; i++) {1980save_offsets(uint8_t(i));1981}1982}19831984void1985Compass::set_motor_compensation(uint8_t i, const Vector3f &motor_comp_factor)1986{1987StateIndex id = _get_state_id(Priority(i));1988if (id < COMPASS_MAX_INSTANCES) {1989_state[id].motor_compensation.set(motor_comp_factor);1990}1991}19921993void1994Compass::save_motor_compensation()1995{1996StateIndex id;1997_motor_comp_type.save();1998for (Priority k(0); k<COMPASS_MAX_INSTANCES; k++) {1999id = _get_state_id(k);2000if (id<COMPASS_MAX_INSTANCES) {2001_state[id].motor_compensation.save();2002}2003}2004}20052006void Compass::try_set_initial_location()2007{2008if (!_auto_declination) {2009return;2010}2011if (!_enabled) {2012return;2013}20142015Location loc;2016if (!AP::ahrs().get_location(loc)) {2017return;2018}2019_initial_location_set = true;20202021// if automatic declination is configured, then compute2022// the declination based on the initial GPS fix2023// Set the declination based on the lat/lng from GPS2024_declination.set(radians(2025AP_Declination::get_declination(2026(float)loc.lat / 10000000,2027(float)loc.lng / 10000000)));2028}20292030/// return true if the compass should be used for yaw calculations2031bool2032Compass::use_for_yaw(void) const2033{2034return healthy(_first_usable) && use_for_yaw(_first_usable);2035}20362037/// return true if the specified compass can be used for yaw calculations2038bool2039Compass::use_for_yaw(uint8_t i) const2040{2041if (!available()) {2042return false;2043}2044// when we are doing in-flight compass learning the state2045// estimator must not use the compass. The learning code turns off2046// inflight learning when it has converged2047return _use_for_yaw[Priority(i)] && _learn != LearnType::INFLIGHT;2048}20492050/*2051return the number of enabled sensors. Used to determine if2052non-compass operation is desired2053*/2054uint8_t Compass::get_num_enabled(void) const2055{2056if (get_count() == 0) {2057return 0;2058}2059uint8_t count = 0;2060for (uint8_t i=0; i<COMPASS_MAX_INSTANCES; i++) {2061if (use_for_yaw(i)) {2062count++;2063}2064}2065return count;2066}20672068void2069Compass::set_declination(float radians, bool save_to_eeprom)2070{2071if (save_to_eeprom) {2072_declination.set_and_save(radians);2073} else {2074_declination.set(radians);2075}2076}20772078float2079Compass::get_declination() const2080{2081return _declination.get();2082}20832084/*2085calculate a compass heading given the attitude from DCM and the mag vector2086*/2087float2088Compass::calculate_heading(const Matrix3f &dcm_matrix, uint8_t i) const2089{2090/*2091This extracts a roll/pitch-only rotation which is then used to rotate the body frame field into earth frame so the heading can be calculated.2092One could do:2093float roll, pitch, yaw;2094dcm_matrix.to_euler(roll, pitch, yaw)2095Matrix3f rp_rot;2096rp_rot.from_euler(roll, pitch, 0)2097Vector3f ef = rp_rot * field20982099Because only the X and Y components are needed it's more efficient to manually calculate:21002101rp_rot = [ cos(pitch), sin(roll) * sin(pitch), cos(roll) * sin(pitch)21020, cos(roll), -sin(roll)]21032104If the whole matrix is multiplied by cos(pitch) the required trigonometric values can be extracted directly from the existing dcm matrix.2105This multiplication has no effect on the calculated heading as it changes the length of the North/East vector but not its angle.21062107rp_rot = [ cos(pitch)^2, sin(roll) * sin(pitch) * cos(pitch), cos(roll) * sin(pitch) * cos(pitch)21080, cos(roll) * cos(pitch), -sin(roll) * cos(pitch)]21092110Preexisting values can be substituted in:21112112dcm_matrix.c.x = -sin(pitch)2113dcm_matrix.c.y = sin(roll) * cos(pitch)2114dcm_matrix.c.z = cos(roll) * cos(pitch)21152116rp_rot = [ cos(pitch)^2, dcm_matrix.c.y * -dcm_matrix.c.x, dcm_matrix.c.z * -dcm_matrix.c.x21170, dcm_matrix.c.z, -dcm_matrix.c.y]21182119cos(pitch)^2 is stil needed. This is the same as 1 - sin(pitch)^2.2120sin(pitch) is avalable as dcm_matrix.c.x2121*/21222123const float cos_pitch_sq = 1.0f-(dcm_matrix.c.x*dcm_matrix.c.x);21242125// Tilt compensated magnetic field Y component:2126const Vector3f &field = get_field(i);21272128const float headY = field.y * dcm_matrix.c.z - field.z * dcm_matrix.c.y;21292130// Tilt compensated magnetic field X component:2131const float headX = field.x * cos_pitch_sq - dcm_matrix.c.x * (field.y * dcm_matrix.c.y + field.z * dcm_matrix.c.z);21322133// magnetic heading2134// 6/4/11 - added constrain to keep bad values from ruining DCM Yaw - Jason S.2135const float heading = constrain_float(atan2f(-headY,headX), -M_PI, M_PI);21362137// Declination correction2138return wrap_PI(heading + _declination);2139}21402141/// Returns True if the compasses have been configured (i.e. offsets saved)2142///2143/// @returns True if compass has been configured2144///2145bool Compass::configured(uint8_t i)2146{2147// exit immediately if instance is beyond the number of compasses we have available2148if (i > get_count()) {2149return false;2150}21512152// exit immediately if all offsets are zero2153if (is_zero(get_offsets(i).length())) {2154return false;2155}21562157StateIndex id = _get_state_id(Priority(i));2158// exit immediately if dev_id hasn't been detected2159if (_state[id].detected_dev_id == 0 ||2160id == COMPASS_MAX_INSTANCES) {2161return false;2162}21632164#ifdef HAL_USE_EMPTY_STORAGE2165// the load() call below returns zeroes on empty storage, so the2166// check-stored-value check here will always fail. Since nobody2167// really cares about the empty-storage case, shortcut out here2168// for simplicity.2169return true;2170#endif21712172// back up cached value of dev_id2173int32_t dev_id_cache_value = _state[id].dev_id;21742175// load dev_id from eeprom2176_state[id].dev_id.load();21772178// if dev_id loaded from eeprom is different from detected dev id or dev_id loaded from eeprom is different from cached dev_id, compass is unconfigured2179if (_state[id].dev_id != _state[id].detected_dev_id || _state[id].dev_id != dev_id_cache_value) {2180// restore cached value2181_state[id].dev_id.set(dev_id_cache_value);2182// return failure2183return false;2184}21852186// if we got here then it must be configured2187return true;2188}21892190bool Compass::configured(char *failure_msg, uint8_t failure_msg_len)2191{2192#if COMPASS_MAX_INSTANCES > 12193// Check if any of the registered devs are not registered2194for (Priority i(0); i<COMPASS_MAX_INSTANCES; i++) {2195if (_priority_did_list[i] != 0 && use_for_yaw(uint8_t(i))) {2196if (!_get_state(i).registered) {2197snprintf(failure_msg, failure_msg_len, "Compass %d not found", uint8_t(i + 1));2198return false;2199}2200if (_priority_did_list[i] != _priority_did_stored_list[i]) {2201snprintf(failure_msg, failure_msg_len, "Compass order change requires reboot");2202return false;2203}2204}2205}2206#endif22072208bool all_configured = true;2209for (uint8_t i=0; i<get_count(); i++) {2210if (configured(i)) {2211continue;2212}2213if (!use_for_yaw(i)) {2214// we're not planning on using this anyway so sure,2215// whatever, it's configured....2216continue;2217}2218all_configured = false;2219break;2220}2221if (!all_configured) {2222snprintf(failure_msg, failure_msg_len, "Compass not calibrated");2223}2224return all_configured;2225}22262227/*2228set the type of motor compensation to use2229*/2230void Compass::motor_compensation_type(const uint8_t comp_type)2231{2232if (_motor_comp_type <= AP_COMPASS_MOT_COMP_CURRENT && _motor_comp_type != (int8_t)comp_type) {2233_motor_comp_type.set((int8_t)comp_type);2234_thr = 0; // set current throttle to zero2235for (uint8_t i=0; i<COMPASS_MAX_INSTANCES; i++) {2236set_motor_compensation(i, Vector3f(0,0,0)); // clear out invalid compensation vectors2237}2238}2239}22402241bool Compass::consistent() const2242{2243const Vector3f &primary_mag_field { get_field() };2244const Vector2f &primary_mag_field_xy { primary_mag_field.xy() };22452246for (uint8_t i=0; i<get_count(); i++) {2247if (!use_for_yaw(i)) {2248// configured not-to-be-used2249continue;2250}22512252const Vector3f &mag_field = get_field(i);2253const Vector2f &mag_field_xy = mag_field.xy();22542255if (mag_field_xy.is_zero()) {2256return false;2257}22582259// check for gross misalignment on all axes2260const float xyz_ang_diff = mag_field.angle(primary_mag_field);2261if (xyz_ang_diff > AP_COMPASS_MAX_XYZ_ANG_DIFF) {2262return false;2263}22642265// check for an unacceptable angle difference on the xy plane2266const float xy_ang_diff = mag_field_xy.angle(primary_mag_field_xy);2267if (xy_ang_diff > AP_COMPASS_MAX_XY_ANG_DIFF) {2268return false;2269}22702271// check for an unacceptable length difference on the xy plane2272const float xy_len_diff = (primary_mag_field_xy-mag_field_xy).length();2273if (xy_len_diff > AP_COMPASS_MAX_XY_LENGTH_DIFF) {2274return false;2275}2276}2277return true;2278}22792280bool Compass::healthy(uint8_t i) const2281{2282return (i < COMPASS_MAX_INSTANCES) ? _get_state(Priority(i)).healthy : false;2283}22842285/*2286return true if we have a valid scale factor2287*/2288bool Compass::have_scale_factor(uint8_t i) const2289{2290if (!available()) {2291return false;2292}2293StateIndex id = _get_state_id(Priority(i));2294if (id >= COMPASS_MAX_INSTANCES ||2295_state[id].scale_factor < COMPASS_MIN_SCALE_FACTOR ||2296_state[id].scale_factor > COMPASS_MAX_SCALE_FACTOR) {2297return false;2298}2299return true;2300}23012302#if AP_COMPASS_MSP_ENABLED2303void Compass::handle_msp(const MSP::msp_compass_data_message_t &pkt)2304{2305if (!_driver_enabled(DRIVER_MSP)) {2306return;2307}2308if (!init_done) {2309if (pkt.instance < 8) {2310msp_instance_mask |= 1U<<pkt.instance;2311}2312} else {2313for (uint8_t i=0; i<_backend_count; i++) {2314_backends[i]->handle_msp(pkt);2315}2316}2317}2318#endif // AP_COMPASS_MSP_ENABLED23192320#if AP_COMPASS_EXTERNALAHRS_ENABLED2321void Compass::handle_external(const AP_ExternalAHRS::mag_data_message_t &pkt)2322{2323if (!_driver_enabled(DRIVER_EXTERNALAHRS)) {2324return;2325}2326for (uint8_t i=0; i<_backend_count; i++) {2327_backends[i]->handle_external(pkt);2328}2329}2330#endif // AP_COMPASS_EXTERNALAHRS_ENABLED23312332// force save of current calibration as valid2333void Compass::force_save_calibration(void)2334{2335for (StateIndex i(0); i<COMPASS_MAX_INSTANCES; i++) {2336if (_state[i].dev_id != 0) {2337_state[i].dev_id.save();2338}2339}2340}23412342// singleton instance2343Compass *Compass::_singleton;23442345namespace AP2346{23472348Compass &compass()2349{2350return *Compass::get_singleton();2351}23522353}23542355#endif // AP_COMPASS_ENABLED235623572358