#include #include #include "Compass_learn.h" #include #include #include #include #if COMPASS_LEARN_ENABLED extern const AP_HAL::HAL &hal; // constructor CompassLearn::CompassLearn(Compass &_compass) : compass(_compass) { GCS_SEND_TEXT(MAV_SEVERITY_INFO, "CompassLearn: Initialised"); } // accuracy threshold applied for GSF yaw estimate #define YAW_ACCURACY_THRESHOLD_DEG 5.0 /* update when new compass sample available */ void CompassLearn::update(void) { const AP_Vehicle *vehicle = AP::vehicle(); if (compass.get_learn_type() != Compass::LearnType::INFLIGHT || !hal.util->get_soft_armed() || vehicle->get_time_flying_ms() < 3000) { // only learn when flying and with enough time to be clear of // the ground return; } const auto &ahrs = AP::ahrs(); const auto *gsf = ahrs.get_yaw_estimator(); if (gsf == nullptr) { // no GSF available return; } if (fabsf(ahrs.get_pitch_deg()) > 50) { // we don't want to be too close to nose up, or yaw gets // problematic. Tailsitters need to wait till they are in // forward flight return; } AP_Notify::flags.compass_cal_running = true; ftype yaw_rad, yaw_variance; uint8_t n_clips; if (!gsf->getYawData(yaw_rad, yaw_variance, &n_clips) || !is_positive(yaw_variance) || n_clips > 1 || yaw_variance >= sq(radians(YAW_ACCURACY_THRESHOLD_DEG))) { // not converged return; } const bool result = compass.mag_cal_fixed_yaw(degrees(yaw_rad), (1U<= COMPASS_MAX_INSTANCES) { return false; } if (MAG_BOARD_ORIENTATION != ROTATION_NONE) { return false; } const mag_state &state = _get_state(Priority(i)); if (state.rotation != ROTATION_NONE) { return false; } if (!state.external) { return _board_orientation == ROTATION_NONE; } return (enum Rotation)state.orientation.get() == ROTATION_NONE; } uint32_t Compass::get_dev_id(uint8_t i) const { if (i >= COMPASS_MAX_INSTANCES) { return 0; } return uint32_t(_get_state(Priority(i)).dev_id.get()); } /* save any compass offsets the EKF has learned. Called on disarm. */ void Compass::save_ekf_learned_offsets() { if (get_learn_type() != LearnType::COPY_FROM_EKF) { return; } auto &ahrs = AP::ahrs(); if (!ahrs.healthy()) { // Note that this is a deliberate tightening rather than part of // moving the vehicles' code here: Copter, Sub and Blimp each // asked getMagOffsets() directly, with no health precondition. // getMagOffsets() judges whether an individual estimate is // usable, but it cannot tell that the estimator as a whole is // unhealthy or that the active backend is no longer the // configured one - and offsets are set_and_save()d, so a bad // set persists across a reboot and has to be recalibrated out // by hand. Declining to copy costs a learning opportunity; the // vehicle keeps the offsets it already had. return; } if (!ahrs.use_compass()) { return; } // note that this loop is not redundant even though a single EKF // core will only ever return offsets for its own selected // compass; with EK3_AFFINITY compass affinity enabled each core // takes a different compass (AP_NavEKF3_Measurements.cpp // update_mag_selection) and the frontend asks every core for each // instance in turn (AP_NavEKF3.cpp getMagOffsets). uint8_t saved_count = 0; for (uint8_t i=0; i