LCOV - code coverage report
Current view: top level - AP_AHRS - AP_AHRS_DCM.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 487 522 93.3 %
Date: 2026-09-09 11:02:08 Functions: 30 30 100.0 %

          Line data    Source code
       1             : /*
       2             :  *       APM_AHRS_DCM.cpp
       3             :  *
       4             :  *       AHRS system using DCM matrices
       5             :  *
       6             :  *       Based on DCM code by Doug Weibel, Jordi Munoz and Jose Julio. DIYDrones.com
       7             :  *
       8             :  *       Adapted for the general ArduPilot AHRS interface by Andrew Tridgell
       9             : 
      10             :   This program is free software: you can redistribute it and/or modify
      11             :   it under the terms of the GNU General Public License as published by
      12             :   the Free Software Foundation, either version 3 of the License, or
      13             :   (at your option) any later version.
      14             : 
      15             :   This program is distributed in the hope that it will be useful,
      16             :   but WITHOUT ANY WARRANTY; without even the implied warranty of
      17             :   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      18             :   GNU General Public License for more details.
      19             : 
      20             :   You should have received a copy of the GNU General Public License
      21             :   along with this program.  If not, see <http://www.gnu.org/licenses/>.
      22             :  */
      23             : 
      24             : #include "AP_AHRS_config.h"
      25             : 
      26             : #if AP_AHRS_DCM_ENABLED
      27             : 
      28             : #include "AP_AHRS.h"
      29             : #include <AP_HAL/AP_HAL.h>
      30             : #include <GCS_MAVLink/GCS.h>
      31             : #include <AP_GPS/AP_GPS.h>
      32             : #include <AP_Baro/AP_Baro.h>
      33             : #include <AP_Compass/AP_Compass.h>
      34             : #include <AP_Logger/AP_Logger.h>
      35             : #include <AP_Vehicle/AP_Vehicle_Type.h>
      36             : 
      37             : extern const AP_HAL::HAL& hal;
      38             : 
      39             : // this is the speed in m/s above which we first get a yaw lock with
      40             : // the GPS
      41             : #define GPS_SPEED_MIN 3
      42             : 
      43             : // the limit (in degrees/second) beyond which we stop integrating
      44             : // omega_I. At larger spin rates the DCM PI controller can get 'dizzy'
      45             : // which results in false gyro drift. See
      46             : // http://gentlenav.googlecode.com/files/fastRotations.pdf
      47             : #define SPIN_RATE_LIMIT 20
      48             : 
      49             : // reset the current gyro drift estimate
      50             : //  should be called if gyro offsets are recalculated
      51             : void
      52           7 : AP_AHRS_DCM::reset_gyro_drift(void)
      53             : {
      54           7 :     _omega_I.zero();
      55           7 :     _omega_I_sum.zero();
      56           7 :     _omega_I_sum_time = 0;
      57           7 : }
      58             : 
      59             : // run a full DCM update round
      60             : void
      61    41686716 : AP_AHRS_DCM::update()
      62             : {
      63    41686716 :     AP_InertialSensor &_ins = AP::ins();
      64             : 
      65             :     // ask the IMU how much time this sensor reading represents
      66    41686716 :     const float delta_t = _ins.get_delta_time();
      67             : 
      68             :     // if the update call took more than 0.2 seconds then discard it,
      69             :     // otherwise we may move too far. This happens when arming motors
      70             :     // in ArduCopter
      71    41686716 :     if (delta_t > 0.2f) {
      72           0 :         memset((void *)&_ra_sum[0], 0, sizeof(_ra_sum));
      73           0 :         _ra_deltat = 0;
      74           0 :         return;
      75             :     }
      76             : 
      77             :     // Integrate the DCM matrix using gyro inputs
      78    41686716 :     matrix_update();
      79             : 
      80             :     // Normalize the DCM matrix
      81    41686716 :     normalize();
      82             : 
      83             :     // Perform drift correction
      84    41686716 :     drift_correction(delta_t);
      85             : 
      86             :     // paranoid check for bad values in the DCM matrix
      87    41686716 :     check_matrix();
      88             : 
      89             :     // calculate the euler angles and DCM matrix which will be used
      90             :     // for high level navigation control. Apply trim such that a
      91             :     // positive trim value results in a positive vehicle rotation
      92             :     // about that axis (ie a negative offset)
      93    41686716 :     _body_dcm_matrix = _dcm_matrix * AP::ahrs().get_rotation_vehicle_body_to_autopilot_body();
      94    41686716 :     _body_dcm_matrix.to_euler(&roll, &pitch, &yaw);
      95             : 
      96             :     // pre-calculate some trig for CPU purposes:
      97    41686716 :     _cos_yaw = cosf(yaw);
      98    41686716 :     _sin_yaw = sinf(yaw);
      99             : 
     100    41686716 :     backup_attitude();
     101             : 
     102             :     // remember the last origin for fallback support
     103    41686716 :     IGNORE_RETURN(AP::ahrs().get_origin(last_origin));
     104             : 
     105             :     // update our wind estimate when a new GPS sample arrives and we
     106             :     // have a 3D fix; the GPS velocity is what feeds the wind triangle.
     107             :     // estimate_wind itself is rate-limited.
     108    41686716 :     const AP_GPS &gps = AP::gps();
     109    41686716 :     if (gps.status() >= AP_GPS_FixType::FIX_3D) {
     110    38125678 :         const uint32_t last_gps_ms = gps.last_message_time_ms();
     111    38125678 :         if (last_gps_ms != _last_wind_gps_ms) {
     112      696272 :             _last_wind_gps_ms = last_gps_ms;
     113      696272 :             estimate_wind();
     114             :         }
     115             :     }
     116             : 
     117             : #if HAL_LOGGING_ENABLED
     118    41686716 :     const uint32_t now_ms = AP_HAL::millis();
     119    41686716 :     if (now_ms - last_log_ms >= 100) {
     120             :         // log DCM at 10Hz
     121     1501471 :         last_log_ms = now_ms;
     122             : 
     123             : // @LoggerMessage: DCM
     124             : // @Description: DCM Estimator Data
     125             : // @Field: TimeUS: Time since system startup
     126             : // @Field: Roll: estimated roll
     127             : // @Field: Pitch: estimated pitch
     128             : // @Field: Yaw: estimated yaw
     129             : // @Field: ErrRP: lowest estimated gyro drift error
     130             : // @Field: ErrYaw: difference between measured yaw and DCM yaw estimate
     131             : // @Field: VWN: wind velocity, to-the-North component
     132             : // @Field: VWE: wind velocity, to-the-East component
     133             : // @Field: VWD: wind velocity, Up-to-Down component
     134             : // @Field: SAs: synthetic (equivalent) airspeed
     135     3002942 :         AP::logger().WriteStreaming(
     136             :             "DCM",
     137             :             "TimeUS," "Roll," "Pitch," "Yaw," "ErrRP," "ErrYaw," "VWN," "VWE," "VWD," "SAs",
     138             :             "s"       "d"     "d"      "d"    "d"      "h"       "n"    "n"    "n"    "n",
     139             :             "F"       "0"     "0"      "0"    "0"      "0"       "0"    "0"    "0"    "0",
     140             :             "Q"       "f"     "f"      "f"    "f"      "f"       "f"    "f"    "f"    "f",
     141             :             AP_HAL::micros64(),
     142     1501471 :             degrees(roll),
     143     1501471 :             degrees(pitch),
     144     1501471 :             wrap_360(degrees(yaw)),
     145     1501471 :             get_error_rp(),
     146     1501471 :             get_error_yaw(),
     147     1501471 :             _wind.x,
     148     1501471 :             _wind.y,
     149     1501471 :             _wind.z,
     150     1501471 :             _last_airspeed_TAS / get_EAS2TAS()
     151             :        );
     152             :     }
     153             : #endif // HAL_LOGGING_ENABLED
     154             : }
     155             : 
     156             : // note that contrary to most code we leave commented code in here as
     157             : // an exhaustive list of all of the data that *can* be returned in the
     158             : // Estimates:
     159    41686716 : void AP_AHRS_DCM::get_results(AP_AHRS_Backend::Estimates &results)
     160             : {
     161    41686716 :     const auto now_ms = AP_HAL::millis();
     162             : 
     163             :     // always initialised
     164    41686716 :     results.initialised = true;
     165             : 
     166             :     // consider ourselves healthy if there have been no failures for 5 seconds
     167    41686716 :     results.healthy = (_last_failure_ms == 0 || now_ms - _last_failure_ms > 5000);
     168             : 
     169             :     // not using a specific sensor:
     170    41686716 :     results.primary_gyro = AP::ins().get_first_usable_gyro();
     171    41686716 :     results.primary_accel = AP::ins().get_first_usable_accel();
     172             : 
     173    41686716 :     results.roll_rad = roll;
     174    41686716 :     results.pitch_rad = pitch;
     175    41686716 :     results.yaw_rad = yaw;
     176             : 
     177    41686716 :     results.dcm_matrix = _body_dcm_matrix;
     178             : 
     179             :     // quaternion is derived from transformation matrix:
     180    41686716 :     results.quaternion.from_rotation_matrix(_dcm_matrix);
     181    41686716 :     results.quaternion.rotate(-AP::ahrs().get_trim());
     182             : 
     183    41686716 :     results.attitude_valid = true;
     184             : 
     185    41686716 :     results.gyro_estimate = _omega;
     186    41686716 :     results.gyro_drift = _omega_I;
     187             : 
     188             :     // true when the state estimates are significantly degraded by vibration:
     189             :     // results.is_vibration_affected = false;
     190             : 
     191             :     /*
     192             :      * acceleration estimates
     193             :      */
     194             :     // results.accel_bias = {} - DCM does not estimate accel bias
     195    41686716 :     results.accel_ef = _accel_ef;
     196             : 
     197    41686716 :     results.velocity_NED_valid = get_velocity_NED(results.velocity_NED);
     198             : 
     199             :     // ground velocity estimate in meters/second, in North/East order
     200             :     // note: velocity_NE is significantly different to results.velocity_NED.xy()!
     201    41686716 :     results.velocity_NE = groundspeed_vector();
     202             : 
     203    41686716 :     results.vert_pos_rate_D_valid = get_vert_pos_rate_D(results.vert_pos_rate_D);
     204             : 
     205             :     /*
     206             :      * position estimates
     207             :      */
     208    41686716 :     results.location_valid = get_location(results.location);
     209             : 
     210             :     // origin-relative functions
     211             :     // results.provides_common_origin = false;
     212             : 
     213             :     // origin-relative position:
     214             :     {
     215             :         // DCM calculates in global co-ordinates and here converts
     216             :         // back to relative-to-origin by subtracting the current
     217             :         // location from the origin:
     218    41686716 :         Location origin;
     219    41686716 :         if (get_origin(origin) && results.location_valid) {
     220    36648032 :             const Vector3p posNED = origin.get_distance_NED_postype(results.location);
     221    36648032 :             results.position_NE = posNED.xy();
     222    36648032 :             results.position_NE_valid = true;
     223    36648032 :             results.position_D = posNED.z;
     224    36648032 :             results.position_D_valid = true;
     225             :         }
     226             :     }
     227             :     // hagl is not supplied:
     228             :     // results.hagl_valid = false;
     229             :     // results.hagl = 0;
     230             : 
     231             :     /*
     232             :      * air data estimates
     233             :      */
     234    41686716 :     results.wind = _wind;
     235    41686716 :     results.wind_valid = true;
     236             : 
     237             :     /*
     238             :      * Sensor-related information
     239             :      */
     240             :     // true if the estimator will use GPS data in creating its
     241             :     // estimate when the data is good:
     242    41686716 :     results.configured_to_use_gps = _gps_use != GPSUse::Disable;
     243             :     // true if GPS is configured as the horizontal position source
     244             :     // for this estimator.  Used to decide whether GPS will set
     245             :     // the navigation origin.
     246    41686716 :     results.configured_to_use_gps_for_pos_XY = _gps_use != GPSUse::Disable;
     247             : 
     248             :     // are we consuming yaw from an external (e.g. vision-based) source?
     249             :     // results.using_extnav_for_yaw = false;
     250             : 
     251             :     // are we consuming yaw from a source which is *not* a compass
     252             :     // results.using_noncompass_for_yaw = false;
     253             : 
     254             : #if AP_AHRS_GET_MAG_DATA_ENABLED
     255             :     // estimators can provide their predicted magnetic fields:
     256             :     // ... but DCM does not:
     257             :     // results.mag_field_NED = {};
     258             :     // results.mag_field_NED_valid = false;
     259             :     // results.mag_field_corrections = {};
     260             :     // results.mag_field_corrections_valid = false;
     261             : #endif  // AP_AHRS_GET_MAG_DATA_ENABLED
     262             : 
     263             :     /*
     264             :      * filter status and estimates quality values:
     265             :      */
     266             :     // getFilterStatus(results.filter_status);
     267             :     // results.filter_status_valid = false;
     268             : 
     269             :     // provides the innovations normalised between 0 and 1:
     270             :     // results.variances_valid = false;
     271             : 
     272             :     // terrain_alt_variance_valid = false;
     273             : 
     274             :     // lower gains in VTOL controllers when flying on DCM
     275    41686716 :     results.control_ground_speed_limit_ms = 50.0;
     276    41686716 :     results.control_gain_scaler_XY = 0.5;
     277    41686716 :     results.control_gain_scaler_Z = 0.25;
     278             :     // control height is never limited:
     279             :     // results.control_height_limit_valid = false;
     280             :     // results.control_height_limit_m = 0;
     281    41686716 : }
     282             : 
     283             : /*
     284             :   backup attitude to persistent_data for use in watchdog reset
     285             :  */
     286    41686716 : void AP_AHRS_DCM::backup_attitude(void)
     287             : {
     288    41686716 :     AP_HAL::Util::PersistentData &pd = hal.util->persistent_data;
     289    41686716 :     pd.roll_rad = roll;
     290    41686716 :     pd.pitch_rad = pitch;
     291    41686716 :     pd.yaw_rad = yaw;
     292    41686716 : }
     293             : 
     294             : // update the DCM matrix using only the gyros
     295    41686716 : void AP_AHRS_DCM::matrix_update(void)
     296             : {
     297             :     // use only the primary gyro so our bias estimate is valid, allowing us to return the right filtered gyro
     298             :     // for rate controllers
     299    41686716 :     const auto &_ins = AP::ins();
     300    41686716 :     Vector3f delta_angle;
     301             :     float dangle_dt;
     302    41686716 :     if (_ins.get_delta_angle(delta_angle, dangle_dt) && dangle_dt > 0) {
     303    41686716 :         _omega = delta_angle / dangle_dt;
     304    41686716 :         _omega += _omega_I;
     305    41686716 :         _dcm_matrix.rotate((_omega + _omega_P + _omega_yaw_P) * dangle_dt);
     306             :     }
     307             : 
     308             :     // now update _omega from the filtered value from the primary IMU. We need to use
     309             :     // the primary IMU as the notch filters will only be running on one IMU
     310             : 
     311             :     // note that we do not include the P terms in _omega. This is
     312             :     // because the spin_rate is calculated from _omega.length(),
     313             :     // and including the P terms would give positive feedback into
     314             :     // the _P_gain() calculation, which can lead to a very large P
     315             :     // value
     316    41686716 :     _omega = _ins.get_gyro() + _omega_I;
     317    41686716 : }
     318             : 
     319             : 
     320             : /*
     321             :  *  reset the DCM matrix and omega. Used on ground start, and on
     322             :  *  extreme errors in the matrix
     323             :  */
     324             : void
     325        2115 : AP_AHRS_DCM::reset(bool recover_eulers)
     326             : {
     327             :     // reset the integration terms
     328        2115 :     _omega_I.zero();
     329        2115 :     _omega_P.zero();
     330        2115 :     _omega_yaw_P.zero();
     331        2115 :     _omega.zero();
     332             : 
     333             :     // if the caller wants us to try to recover to the current
     334             :     // attitude then calculate the dcm matrix from the current
     335             :     // roll/pitch/yaw values
     336        2115 :     if (hal.util->was_watchdog_reset() && AP_HAL::millis() < 10000) {
     337           1 :         const AP_HAL::Util::PersistentData &pd = hal.util->persistent_data;
     338           1 :         roll = pd.roll_rad;
     339           1 :         pitch = pd.pitch_rad;
     340           1 :         yaw = pd.yaw_rad;
     341           1 :         _dcm_matrix.from_euler(roll, pitch, yaw);
     342           1 :         GCS_SEND_TEXT(MAV_SEVERITY_INFO, "Restored watchdog attitude %.0f %.0f %.0f",
     343             :                         degrees(roll), degrees(pitch), degrees(yaw));
     344        2114 :     } else if (recover_eulers && !isnan(roll) && !isnan(pitch) && !isnan(yaw)) {
     345           0 :         _dcm_matrix.from_euler(roll, pitch, yaw);
     346             :     } else {
     347             : 
     348             :         // Use the measured accel due to gravity to calculate an initial
     349             :         // roll and pitch estimate
     350             : 
     351        2114 :         AP_InertialSensor &_ins = AP::ins();
     352             : 
     353             :         // Get body frame accel vector
     354        2114 :         Vector3f initAccVec = _ins.get_accel();
     355        2114 :         uint8_t counter = 0;
     356             : 
     357             :         // the first vector may be invalid as the filter starts up
     358        2846 :         while ((initAccVec.length() < 9.0f || initAccVec.length() > 11) && counter++ < 20) {
     359         732 :             _ins.wait_for_sample();
     360         732 :             _ins.update();
     361         732 :             initAccVec = _ins.get_accel();
     362             :         }
     363             : 
     364             :         // normalise the acceleration vector
     365        2114 :         if (initAccVec.length() > 5.0f) {
     366             :             // calculate initial pitch angle
     367        2114 :             pitch = atan2f(initAccVec.x, norm(initAccVec.y, initAccVec.z));
     368             :             // calculate initial roll angle
     369        2114 :             roll = atan2f(-initAccVec.y, -initAccVec.z);
     370             :         } else {
     371             :             // If we can't use the accel vector, then align flat
     372           0 :             roll = 0.0f;
     373           0 :             pitch = 0.0f;
     374             :         }
     375        2114 :         _dcm_matrix.from_euler(roll, pitch, 0);
     376             : 
     377             :     }
     378             : 
     379             :     // pre-calculate some trig for CPU purposes:
     380        2115 :     _cos_yaw = cosf(yaw);
     381        2115 :     _sin_yaw = sinf(yaw);
     382             : 
     383        2115 :     _last_startup_ms = AP_HAL::millis();
     384        2115 : }
     385             : 
     386             : /*
     387             :  *  check the DCM matrix for pathological values
     388             :  */
     389             : void
     390    41686716 : AP_AHRS_DCM::check_matrix(void)
     391             : {
     392    41686716 :     if (_dcm_matrix.is_nan()) {
     393             :         //Serial.printf("ERROR: DCM matrix NAN\n");
     394           0 :         AP_AHRS_DCM::reset(true);
     395           0 :         return;
     396             :     }
     397             :     // some DCM matrix values can lead to an out of range error in
     398             :     // the pitch calculation via asin().  These NaN values can
     399             :     // feed back into the rest of the DCM matrix via the
     400             :     // error_course value.
     401    41686716 :     if (!(_dcm_matrix.c.x < 1.0f &&
     402    41682169 :             _dcm_matrix.c.x > -1.0f)) {
     403             :         // We have an invalid matrix. Force a normalisation.
     404       12220 :         normalize();
     405             : 
     406       24440 :         if (_dcm_matrix.is_nan() ||
     407       12220 :                 fabsf(_dcm_matrix.c.x) > 10.0) {
     408             :             // See Issue #20284: regarding the selection of 10.0 for DCM reset
     409             :             // This won't be lowered without evidence of an issue or mathematical proof & testing of a lower bound
     410             : 
     411             :             // normalisation didn't fix the problem! We're
     412             :             // in real trouble. All we can do is reset
     413             :             //Serial.printf("ERROR: DCM matrix error. _dcm_matrix.c.x=%f\n",
     414             :             //     _dcm_matrix.c.x);
     415           0 :             AP_AHRS_DCM::reset(true);
     416             :         }
     417             :     }
     418             : }
     419             : 
     420             : // renormalise one vector component of the DCM matrix
     421             : // this will return false if renormalization fails
     422             : bool
     423   125096808 : AP_AHRS_DCM::renorm(Vector3f const &a, Vector3f &result)
     424             : {
     425             :     // numerical errors will slowly build up over time in DCM,
     426             :     // causing inaccuracies. We can keep ahead of those errors
     427             :     // using the renormalization technique from the DCM IMU paper
     428             :     // (see equations 18 to 21).
     429             : 
     430             :     // For APM we don't bother with the taylor expansion
     431             :     // optimisation from the paper as on our 2560 CPU the cost of
     432             :     // the sqrt() is 44 microseconds, and the small time saving of
     433             :     // the taylor expansion is not worth the potential of
     434             :     // additional error buildup.
     435             : 
     436             :     // Note that we can get significant renormalisation values
     437             :     // when we have a larger delta_t due to a glitch elsewhere in
     438             :     // APM, such as a I2c timeout or a set of EEPROM writes. While
     439             :     // we would like to avoid these if possible, if it does happen
     440             :     // we don't want to compound the error by making DCM less
     441             :     // accurate.
     442             : 
     443   125096808 :     const float renorm_val = 1.0f / a.length();
     444             : 
     445             :     // keep the average for reporting
     446   125096808 :     _renorm_val_sum += renorm_val;
     447   125096808 :     _renorm_val_count++;
     448             : 
     449   125096808 :     if (!(renorm_val < 2.0f && renorm_val > 0.5f)) {
     450             :         // this is larger than it should get - log it as a warning
     451           0 :         if (!(renorm_val < 1.0e6f && renorm_val > 1.0e-6f)) {
     452             :             // we are getting values which are way out of
     453             :             // range, we will reset the matrix and hope we
     454             :             // can recover our attitude using drift
     455             :             // correction before we hit the ground!
     456             :             //Serial.printf("ERROR: DCM renormalisation error. renorm_val=%f\n",
     457             :             //     renorm_val);
     458           0 :             return false;
     459             :         }
     460             :     }
     461             : 
     462   125096808 :     result = a * renorm_val;
     463   125096808 :     return true;
     464             : }
     465             : 
     466             : /*************************************************
     467             :  *  Direction Cosine Matrix IMU: Theory
     468             :  *  William Premerlani and Paul Bizard
     469             :  *
     470             :  *  Numerical errors will gradually reduce the orthogonality conditions expressed by equation 5
     471             :  *  to approximations rather than identities. In effect, the axes in the two frames of reference no
     472             :  *  longer describe a rigid body. Fortunately, numerical error accumulates very slowly, so it is a
     473             :  *  simple matter to stay ahead of it.
     474             :  *  We call the process of enforcing the orthogonality conditions: renormalization.
     475             :  */
     476             : void
     477    41698936 : AP_AHRS_DCM::normalize(void)
     478             : {
     479    41698936 :     const float error = _dcm_matrix.a * _dcm_matrix.b;                                 // eq.18
     480             : 
     481    41698936 :     const Vector3f t0 = _dcm_matrix.a - (_dcm_matrix.b * (0.5f * error));              // eq.19
     482    41698936 :     const Vector3f t1 = _dcm_matrix.b - (_dcm_matrix.a * (0.5f * error));              // eq.19
     483    41698936 :     const Vector3f t2 = t0 % t1;                                                       // c= a x b // eq.20
     484             : 
     485    41698936 :     if (!renorm(t0, _dcm_matrix.a) ||
     486    83397872 :             !renorm(t1, _dcm_matrix.b) ||
     487    41698936 :             !renorm(t2, _dcm_matrix.c)) {
     488             :         // Our solution is blowing up and we will force back
     489             :         // to last euler angles
     490           0 :         _last_failure_ms = AP_HAL::millis();
     491           0 :         AP_AHRS_DCM::reset(true);
     492             :     }
     493    41698936 : }
     494             : 
     495             : 
     496             : // produce a yaw error value. The returned value is proportional
     497             : // to sin() of the current heading error in earth frame
     498             : float
     499     1488533 : AP_AHRS_DCM::yaw_error_compass(Compass &compass)
     500             : {
     501     1488533 :     const Vector3f &mag = compass.get_field();
     502             :     // get the mag vector in the earth frame
     503     1488533 :     Vector2f rb = _dcm_matrix.mulXY(mag);
     504             : 
     505     1488533 :     if (rb.length() < FLT_EPSILON) {
     506           0 :         return 0.0f;
     507             :     }
     508             : 
     509     1488533 :     rb.normalize();
     510     1488533 :     if (rb.is_inf()) {
     511             :         // not a valid vector
     512           0 :         return 0.0f;
     513             :     }
     514             : 
     515             :     // update vector holding earths magnetic field (if required)
     516     1488533 :     if( !is_equal(_last_declination, compass.get_declination()) ) {
     517        1100 :         _last_declination = compass.get_declination();
     518        1100 :         _mag_earth.x = cosf(_last_declination);
     519        1100 :         _mag_earth.y = sinf(_last_declination);
     520             :     }
     521             : 
     522             :     // calculate the error term in earth frame
     523             :     // calculate the Z component of the cross product of rb and _mag_earth
     524     1488533 :     return rb % _mag_earth;
     525             : }
     526             : 
     527             : // the _P_gain raises the gain of the PI controller
     528             : // when we are spinning fast. See the fastRotations
     529             : // paper from Bill.
     530             : float
     531     2247777 : AP_AHRS_DCM::_P_gain(float spin_rate)
     532             : {
     533     2247777 :     if (spin_rate < radians(50)) {
     534     2197184 :         return 1.0f;
     535             :     }
     536       50593 :     if (spin_rate > radians(500)) {
     537        2869 :         return 10.0f;
     538             :     }
     539       47724 :     return spin_rate/radians(50);
     540             : }
     541             : 
     542             : // _yaw_gain reduces the gain of the PI controller applied to heading errors
     543             : // when observability from change of velocity is good (eg changing speed or turning)
     544             : // This reduces unwanted roll and pitch coupling due to compass errors for planes.
     545             : // High levels of noise on _accel_ef will cause the gain to drop and could lead to
     546             : // increased heading drift during straight and level flight, however some gain is
     547             : // always available. TODO check the necessity of adding adjustable acc threshold
     548             : // and/or filtering accelerations before getting magnitude
     549             : float
     550     1488962 : AP_AHRS_DCM::_yaw_gain(void) const
     551             : {
     552     1488962 :     const float VdotEFmag = _accel_ef.xy().length();
     553             : 
     554     1488962 :     if (VdotEFmag <= 4.0f) {
     555     1369568 :         return 0.2f*(4.5f - VdotEFmag);
     556             :     }
     557      119394 :     return 0.1f;
     558             : }
     559             : 
     560             : 
     561             : // return true if we have and should use GPS
     562    48876285 : bool AP_AHRS_DCM::have_gps(void) const
     563             : {
     564    48876285 :     if (_gps_use == GPSUse::Disable || AP::gps().status() <= AP_GPS_FixType::NONE) {
     565     4263268 :         return false;
     566             :     }
     567    44613017 :     return true;
     568             : }
     569             : 
     570             : /*
     571             :   when we are getting the initial attitude we want faster gains so
     572             :   that if the board starts upside down we quickly approach the right
     573             :   attitude.
     574             :   We don't want to keep those high gains for too long though as high P
     575             :   gains cause slow gyro offset learning. So we keep the high gains for
     576             :   a maximum of 20 seconds
     577             :  */
     578     2247777 : bool AP_AHRS_DCM::use_fast_gains(void) const
     579             : {
     580     2247777 :     return !hal.util->get_soft_armed() && (AP_HAL::millis() - _last_startup_ms) < 20000U;
     581             : }
     582             : 
     583             : 
     584             : // return true if we should use the compass for yaw correction
     585    41963735 : bool AP_AHRS_DCM::use_compass(void)
     586             : {
     587    41963735 :     const Compass &compass = AP::compass();
     588             : 
     589    41963735 :     if (!compass.use_for_yaw()) {
     590             :         // no compass available
     591      109007 :         return false;
     592             :     }
     593    41854728 :     if (!AP::ahrs().get_fly_forward() || !have_gps()) {
     594             :         // we don't have any alternative to the compass
     595    36872081 :         return true;
     596             :     }
     597     4982647 :     if (AP::gps().ground_speed() < GPS_SPEED_MIN) {
     598             :         // we are not going fast enough to use the GPS
     599     1255781 :         return true;
     600             :     }
     601             : 
     602             :     // if the current yaw differs from the GPS yaw by more than 45
     603             :     // degrees and the estimated wind speed is less than 80% of the
     604             :     // ground speed, then switch to GPS navigation. This will help
     605             :     // prevent flyaways with very bad compass offsets
     606     3726866 :     const float error = fabsf(wrap_180(degrees(yaw) - AP::gps().ground_course()));
     607     3726866 :     if (error > 45 && _wind.xy().length() < AP::gps().ground_speed()*0.8f) {
     608       10396 :         if (AP_HAL::millis() - _last_consistent_heading > 2000) {
     609             :             // start using the GPS for heading if the compass has been
     610             :             // inconsistent with the GPS for 2 seconds
     611        1023 :             return false;
     612             :         }
     613             :     } else {
     614     3716470 :         _last_consistent_heading = AP_HAL::millis();
     615             :     }
     616             : 
     617             :     // use the compass
     618     3725843 :     return true;
     619             : }
     620             : 
     621             : // yaw drift correction using the compass or GPS
     622             : // this function produces the _omega_yaw_P vector, and also
     623             : // contributes to the _omega_I.z long term yaw drift estimate
     624             : void
     625    41686716 : AP_AHRS_DCM::drift_correction_yaw(void)
     626             : {
     627    41686716 :     bool new_value = false;
     628             :     float yaw_error;
     629    41686716 :     float yaw_deltat = 0.0f;
     630             : 
     631    41686716 :     const AP_GPS &_gps = AP::gps();
     632             : 
     633    41686716 :     Compass &compass = AP::compass();
     634             : 
     635             : #if COMPASS_CAL_ENABLED
     636    41686716 :     if (compass.is_calibrating()) {
     637             :         // don't do any yaw correction while calibrating
     638      565279 :         return;
     639             :     }
     640             : #endif
     641             :     
     642    41121437 :     if (AP_AHRS_DCM::use_compass()) {
     643             :         /*
     644             :           we are using compass for yaw
     645             :          */
     646    41013052 :         if (compass.last_update_usec() != _compass_last_update) {
     647     1488533 :             yaw_deltat = (compass.last_update_usec() - _compass_last_update) * 1.0e-6f;
     648     1488533 :             _compass_last_update = compass.last_update_usec();
     649             :             // we force an additional compass read()
     650             :             // here. This has the effect of throwing away
     651             :             // the first compass value, which can be bad
     652     1488533 :             if (!have_initial_yaw && compass.read()) {
     653        2082 :                 const float heading = compass.calculate_heading(_dcm_matrix);
     654        2082 :                 _dcm_matrix.from_euler(roll, pitch, heading);
     655        2082 :                 _omega_yaw_P.zero();
     656        2082 :                 have_initial_yaw = true;
     657             :             }
     658     1488533 :             new_value = true;
     659     1488533 :             yaw_error = yaw_error_compass(compass);
     660             : 
     661             :             // also update the _gps_last_update, so if we later
     662             :             // disable the compass due to significant yaw error we
     663             :             // don't suddenly change yaw with a reset
     664     1488533 :             _gps_last_update = _gps.last_fix_time_ms();
     665             :         }
     666      108385 :     } else if (AP::ahrs().get_fly_forward() && have_gps()) {
     667             :         /*
     668             :           we are using GPS for yaw
     669             :          */
     670       15537 :         if (_gps.last_fix_time_ms() != _gps_last_update &&
     671        5309 :                 _gps.ground_speed() >= GPS_SPEED_MIN) {
     672         429 :             yaw_deltat = (_gps.last_fix_time_ms() - _gps_last_update) * 1.0e-3f;
     673         429 :             _gps_last_update = _gps.last_fix_time_ms();
     674         429 :             new_value = true;
     675         429 :             const float gps_course_rad = radians(_gps.ground_course());
     676         429 :             const float yaw_error_rad = wrap_PI(gps_course_rad - yaw);
     677         429 :             yaw_error = sinf(yaw_error_rad);
     678             : 
     679             :             /* reset yaw to match GPS heading under any of the
     680             :                following 3 conditions:
     681             : 
     682             :                1) if we have reached GPS_SPEED_MIN and have never had
     683             :                yaw information before
     684             : 
     685             :                2) if the last time we got yaw information from the GPS
     686             :                is more than 20 seconds ago, which means we may have
     687             :                suffered from considerable gyro drift
     688             : 
     689             :                3) if we are over 3*GPS_SPEED_MIN (which means 9m/s)
     690             :                and our yaw error is over 60 degrees, which means very
     691             :                poor yaw. This can happen on bungee launch when the
     692             :                operator pulls back the plane rapidly enough then on
     693             :                release the GPS heading changes very rapidly
     694             :             */
     695         427 :             if (!have_initial_yaw ||
     696        1246 :                     yaw_deltat > 20 ||
     697         817 :                     (_gps.ground_speed() >= 3*GPS_SPEED_MIN && fabsf(yaw_error_rad) >= 1.047f)) {
     698             :                 // reset DCM matrix based on current yaw
     699          37 :                 _dcm_matrix.from_euler(roll, pitch, gps_course_rad);
     700          37 :                 _omega_yaw_P.zero();
     701          37 :                 have_initial_yaw = true;
     702          37 :                 yaw_error = 0;
     703             :             }
     704             :         }
     705             :     }
     706             : 
     707    41121437 :     if (!new_value) {
     708             :         // we don't have any new yaw information
     709             :         // slowly decay _omega_yaw_P to cope with loss
     710             :         // of our yaw source
     711    39632475 :         _omega_yaw_P *= 0.97f;
     712    39632475 :         return;
     713             :     }
     714             : 
     715             :     // convert the error vector to body frame
     716     1488962 :     const float error_z = _dcm_matrix.c.z * yaw_error;
     717             : 
     718             :     // the spin rate changes the P gain, and disables the
     719             :     // integration at higher rates
     720     1488962 :     const float spin_rate = _omega.length();
     721             : 
     722             :     // sanity check _kp_yaw
     723     1488962 :     if (_kp_yaw < AP_AHRS_YAW_P_MIN) {
     724           0 :         _kp_yaw.set(AP_AHRS_YAW_P_MIN);
     725             :     }
     726             : 
     727             :     // update the proportional control to drag the
     728             :     // yaw back to the right value. We use a gain
     729             :     // that depends on the spin rate. See the fastRotations.pdf
     730             :     // paper from Bill Premerlani
     731             :     // We also adjust the gain depending on the rate of change of horizontal velocity which
     732             :     // is proportional to how observable the heading is from the accelerations and GPS velocity
     733             :     // The acceleration derived heading will be more reliable in turns than compass or GPS
     734             : 
     735     1488962 :     _omega_yaw_P.z = error_z * _P_gain(spin_rate) * _kp_yaw * _yaw_gain();
     736     1488962 :     if (use_fast_gains()) {
     737      214454 :         _omega_yaw_P.z *= 8;
     738             :     }
     739             : 
     740             :     // don't update the drift term if we lost the yaw reference
     741             :     // for more than 2 seconds
     742     1488962 :     if (yaw_deltat < 2.0f && spin_rate < radians(SPIN_RATE_LIMIT)) {
     743             :         // also add to the I term
     744     1368043 :         _omega_I_sum.z += error_z * _ki_yaw * yaw_deltat;
     745             :     }
     746             : 
     747     1488962 :     _error_yaw = 0.8f * _error_yaw + 0.2f * fabsf(yaw_error);
     748             : }
     749             : 
     750             : 
     751             : /**
     752             :    return an accel vector delayed by AHRS_ACCEL_DELAY samples for a
     753             :    specific accelerometer instance
     754             :  */
     755     1204792 : Vector3f AP_AHRS_DCM::ra_delayed(uint8_t instance, const Vector3f &ra)
     756             : {
     757             :     // get the old element, and then fill it with the new element
     758     1204792 :     const Vector3f ret = _ra_delay_buffer[instance];
     759     1204792 :     _ra_delay_buffer[instance] = ra;
     760     1204792 :     if (ret.is_zero()) {
     761             :         // use the current vector if the previous vector is exactly
     762             :         // zero. This prevents an error on initialisation
     763        2426 :         return ra;
     764             :     }
     765     1202366 :     return ret;
     766             : }
     767             : 
     768             : /* returns true if attitude should be corrected from GPS-derived
     769             :  * velocity-deltas.  We turn this off for Copter and other similar
     770             :  * vehicles while the vehicle is disarmed to avoid the HUD bobbing
     771             :  * around while the vehicle is disarmed.
     772             :  */
     773      758815 : bool AP_AHRS_DCM::should_correct_centrifugal() const
     774             : {
     775             : #if APM_BUILD_COPTER_OR_HELI || APM_BUILD_TYPE(APM_BUILD_ArduSub) || APM_BUILD_TYPE(APM_BUILD_Blimp)
     776      406873 :     return hal.util->get_soft_armed();
     777             : #endif
     778             : 
     779      351942 :     return true;
     780             : }
     781             : 
     782             : // perform drift correction. This function aims to update _omega_P and
     783             : // _omega_I with our best estimate of the short term and long term
     784             : // gyro error. The _omega_P value is what pulls our attitude solution
     785             : // back towards the reference vector quickly. The _omega_I term is an
     786             : // attempt to learn the long term drift rate of the gyros.
     787             : //
     788             : // This drift correction implementation is based on a paper
     789             : // by Bill Premerlani from here:
     790             : //   http://gentlenav.googlecode.com/files/RollPitchDriftCompensation.pdf
     791             : void
     792    41686716 : AP_AHRS_DCM::drift_correction(float deltat)
     793             : {
     794    41686716 :     Vector3f velocity;
     795             :     uint32_t last_correction_time;
     796             : 
     797             :     // perform yaw drift correction if we have a new yaw reference
     798             :     // vector
     799    41686716 :     drift_correction_yaw();
     800             : 
     801    41686716 :     const AP_InertialSensor &_ins = AP::ins();
     802             : 
     803             :     // rotate accelerometer values into the earth frame
     804   125136564 :     for (uint8_t i=0; i<_ins.get_accel_count(); i++) {
     805    83449848 :         if (_ins.use_accel(i)) {
     806             :             /*
     807             :               by using get_delta_velocity() instead of get_accel() the
     808             :               accel value is sampled over the right time delta for
     809             :               each sensor, which prevents an aliasing effect
     810             :              */
     811    83449848 :             Vector3f delta_velocity;
     812             :             float delta_velocity_dt;
     813    83449848 :             _ins.get_delta_velocity(i, delta_velocity, delta_velocity_dt);
     814    83449848 :             if (delta_velocity_dt > 0) {
     815    83449846 :                 Vector3f accel_ef = _dcm_matrix * (delta_velocity / delta_velocity_dt);
     816             :                 // integrate the accel vector in the earth frame between GPS readings
     817    83449846 :                 _ra_sum[i] += accel_ef * deltat;
     818             :             }
     819             :         }
     820             :     }
     821             : 
     822             :     // set _accel_ef_blended based on filtered accel
     823    41686716 :     _accel_ef = _dcm_matrix * _ins.get_accel();
     824             : 
     825             :     // keep a sum of the deltat values, so we know how much time
     826             :     // we have integrated over
     827    41686716 :     _ra_deltat += deltat;
     828             : 
     829    41686716 :     const AP_GPS &_gps = AP::gps();
     830    41686716 :     const bool fly_forward = AP::ahrs().get_fly_forward();
     831             : 
     832    79812329 :     if (!have_gps() ||
     833    79812329 :             _gps.status() < AP_GPS_FixType::FIX_3D ||
     834    38125373 :             _gps.num_sats() < _gps_minsats) {
     835             :         // no GPS, or not a good lock. From experience we need at
     836             :         // least 6 satellites to get a really reliable velocity number
     837             :         // from the GPS.
     838             :         //
     839             :         // As a fallback we use the fixed wing acceleration correction
     840             :         // if we have an airspeed estimate (which we only have if
     841             :         // _fly_forward is set), otherwise no correction
     842     3579164 :         if (_ra_deltat < 0.2f) {
     843             :             // not enough time has accumulated
     844    40927901 :             return;
     845             :         }
     846             : 
     847       65188 :         float airspeed_TAS = _last_airspeed_TAS;
     848             : #if AP_AIRSPEED_ENABLED
     849       65188 :         if (airspeed_sensor_enabled()) {
     850       14638 :             airspeed_TAS = AP::airspeed()->get_airspeed() * get_EAS2TAS();
     851             :         }
     852             : #endif
     853             : 
     854             :         // use airspeed to estimate our ground velocity in
     855             :         // earth frame by subtracting the wind
     856       65188 :         velocity = _dcm_matrix.colx() * airspeed_TAS;
     857             : 
     858             :         // add in wind estimate
     859       65188 :         velocity += _wind;
     860             : 
     861       65188 :         last_correction_time = AP_HAL::millis();
     862       65188 :         _have_gps_lock = false;
     863             :     } else {
     864    38107552 :         if (_gps.last_fix_time_ms() == _ra_sum_start) {
     865             :             // we don't have a new GPS fix - nothing more to do
     866    37411837 :             return;
     867             :         }
     868      695715 :         velocity = _gps.velocity();
     869      695715 :         last_correction_time = _gps.last_fix_time_ms();
     870      695715 :         if (_have_gps_lock == false) {
     871             :             // if we didn't have GPS lock in the last drift
     872             :             // correction interval then set the velocities equal
     873        1134 :             _last_velocity = velocity;
     874             :         }
     875      695715 :         _have_gps_lock = true;
     876             : 
     877             :         // keep last airspeed estimate for dead-reckoning purposes
     878      695715 :         Vector3f airspeed = velocity - _wind;
     879             : 
     880             :         // rotate vector to body frame
     881      695715 :         airspeed = _body_dcm_matrix.mul_transpose(airspeed);
     882             : 
     883             :         // take positive component in X direction. This mimics a pitot
     884             :         // tube
     885      695715 :         _last_airspeed_TAS = MAX(airspeed.x, 0);
     886             :     }
     887             : 
     888      760903 :     if (have_gps()) {
     889             :         // use GPS for positioning with any fix, even a 2D fix
     890      696160 :         _last_lat = _gps.location().lat;
     891      696160 :         _last_lng = _gps.location().lng;
     892      696160 :         _last_pos_ms = AP_HAL::millis();
     893      696160 :         _position_offset_north = 0;
     894      696160 :         _position_offset_east = 0;
     895             : 
     896             :         // once we have a single GPS lock, we can update using
     897             :         // dead-reckoning from then on
     898      696160 :         _have_position = true;
     899             :     } else {
     900             :         // update dead-reckoning position estimate
     901       64743 :         _position_offset_north += velocity.x * _ra_deltat;
     902       64743 :         _position_offset_east  += velocity.y * _ra_deltat;
     903             :     }
     904             : 
     905             :     // see if this is our first time through - in which case we
     906             :     // just setup the start times and return
     907      760903 :     if (_ra_sum_start == 0) {
     908        2088 :         _ra_sum_start = last_correction_time;
     909        2088 :         _last_velocity = velocity;
     910        2088 :         return;
     911             :     }
     912             : 
     913             :     // equation 9: get the corrected acceleration vector in earth frame. Units
     914             :     // are m/s/s
     915      758815 :     Vector3f GA_e(0.0f, 0.0f, -1.0f);
     916             : 
     917      758815 :     if (_ra_deltat <= 0) {
     918             :         // waiting for more data
     919           0 :         return;
     920             :     }
     921             :     
     922      758815 :     bool using_gps_corrections = false;
     923      758815 :     float ra_scale = 1.0f/(_ra_deltat*GRAVITY_MSS);
     924             : 
     925      758815 :     if (should_correct_centrifugal() && (_have_gps_lock || fly_forward)) {
     926      599345 :         const float v_scale = gps_gain.get() * ra_scale;
     927      599345 :         const Vector3f vdelta = (velocity - _last_velocity) * v_scale;
     928      599345 :         GA_e += vdelta;
     929      599345 :         GA_e.normalize();
     930      599345 :         if (GA_e.is_inf()) {
     931             :             // wait for some non-zero acceleration information
     932           0 :             _last_failure_ms = AP_HAL::millis();
     933           0 :             return;
     934             :         }
     935      599345 :         using_gps_corrections = true;
     936             :     }
     937             : 
     938             :     // calculate the error term in earth frame.
     939             :     // we do this for each available accelerometer then pick the
     940             :     // accelerometer that leads to the smallest error term. This takes
     941             :     // advantage of the different sample rates on different
     942             :     // accelerometers to dramatically reduce the impact of aliasing
     943             :     // due to harmonics of vibrations that match closely the sampling
     944             :     // rate of our accelerometers. On the Pixhawk we have the LSM303D
     945             :     // running at 800Hz and the MPU6000 running at 1kHz, by combining
     946             :     // the two the effects of aliasing are greatly reduced.
     947      758815 :     Vector3f error[INS_MAX_INSTANCES];
     948             :     float error_dirn[INS_MAX_INSTANCES];
     949      758815 :     Vector3f GA_b[INS_MAX_INSTANCES];
     950      758815 :     int8_t besti = -1;
     951      758815 :     float best_error = 0;
     952     2282990 :     for (uint8_t i=0; i<_ins.get_accel_count(); i++) {
     953     1524175 :         if (!_ins.get_accel_health(i)) {
     954             :             // only use healthy sensors
     955           0 :             continue;
     956             :         }
     957     1524175 :         _ra_sum[i] *= ra_scale;
     958             : 
     959             :         // get the delayed ra_sum to match the GPS lag
     960     1524175 :         if (using_gps_corrections) {
     961     1204792 :             GA_b[i] = ra_delayed(i, _ra_sum[i]);
     962             :         } else {
     963      319383 :             GA_b[i] = _ra_sum[i];
     964             :         }
     965     1524175 :         if (GA_b[i].is_zero()) {
     966             :             // wait for some non-zero acceleration information
     967           0 :             continue;
     968             :         }
     969     1524175 :         GA_b[i].normalize();
     970     1524175 :         if (GA_b[i].is_inf()) {
     971             :             // wait for some non-zero acceleration information
     972           0 :             continue;
     973             :         }
     974     1524175 :         error[i] = GA_b[i] % GA_e;
     975             :         // Take dot product to catch case vectors are opposite sign and parallel
     976     1524175 :         error_dirn[i] = GA_b[i] * GA_e;
     977     1524175 :         const float error_length = error[i].length();
     978     1524175 :         if (besti == -1 || error_length < best_error) {
     979     1128359 :             besti = i;
     980     1128359 :             best_error = error_length;
     981             :         }
     982             :         // Catch case where orientation is 180 degrees out
     983     1524175 :         if (error_dirn[besti] < 0.0f) {
     984        4470 :             best_error = 1.0f;
     985             :         }
     986             : 
     987             :     }
     988             : 
     989      758815 :     if (besti == -1) {
     990             :         // no healthy accelerometers!
     991           0 :         _last_failure_ms = AP_HAL::millis();
     992           0 :         return;
     993             :     }
     994             : 
     995      758815 :     _active_accel_instance = besti;
     996             : 
     997             : #define YAW_INDEPENDENT_DRIFT_CORRECTION 0
     998             : #if YAW_INDEPENDENT_DRIFT_CORRECTION
     999             :     // step 2 calculate earth_error_Z
    1000             :     const float earth_error_Z = error.z;
    1001             : 
    1002             :     // equation 10
    1003             :     const float tilt = GA_e.xy().length();
    1004             : 
    1005             :     // equation 11
    1006             :     const float theta = atan2f(GA_b[besti].y, GA_b[besti].x);
    1007             : 
    1008             :     // equation 12
    1009             :     const Vector3f GA_e2 = Vector3f(cosf(theta)*tilt, sinf(theta)*tilt, GA_e.z);
    1010             : 
    1011             :     // step 6
    1012             :     error = GA_b[besti] % GA_e2;
    1013             :     error.z = earth_error_Z;
    1014             : #endif // YAW_INDEPENDENT_DRIFT_CORRECTION
    1015             : 
    1016             :     // to reduce the impact of two competing yaw controllers, we
    1017             :     // reduce the impact of the gps/accelerometers on yaw when we are
    1018             :     // flat, but still allow for yaw correction using the
    1019             :     // accelerometers at high roll angles as long as we have a GPS
    1020      758815 :     if (AP_AHRS_DCM::use_compass()) {
    1021      757170 :         if (have_gps() && is_equal(gps_gain.get(), 1.0f)) {
    1022      694957 :             error[besti].z *= sinf(fabsf(roll));
    1023             :         } else {
    1024       62213 :             error[besti].z = 0;
    1025             :         }
    1026             :     }
    1027             : 
    1028             :     // if ins is unhealthy then stop attitude drift correction and
    1029             :     // hope the gyros are OK for a while. Just slowly reduce _omega_P
    1030             :     // to prevent previous bad accels from throwing us off
    1031      758815 :     if (!_ins.healthy()) {
    1032           0 :         error[besti].zero();
    1033             :     } else {
    1034             :         // convert the error term to body frame
    1035      758815 :         error[besti] = _dcm_matrix.mul_transpose(error[besti]);
    1036             :     }
    1037             : 
    1038      758815 :     if (error[besti].is_nan() || error[besti].is_inf()) {
    1039             :         // don't allow bad values
    1040           0 :         check_matrix();
    1041           0 :         _last_failure_ms = AP_HAL::millis();
    1042           0 :         return;
    1043             :     }
    1044             : 
    1045      758815 :     _error_rp = 0.8f * _error_rp + 0.2f * best_error;
    1046             : 
    1047             :     // base the P gain on the spin rate
    1048      758815 :     const float spin_rate = _omega.length();
    1049             : 
    1050             :     // sanity check _kp value
    1051      758815 :     if (_kp < AP_AHRS_RP_P_MIN) {
    1052           0 :         _kp.set(AP_AHRS_RP_P_MIN);
    1053             :     }
    1054             : 
    1055             :     // we now want to calculate _omega_P and _omega_I. The
    1056             :     // _omega_P value is what drags us quickly to the
    1057             :     // accelerometer reading.
    1058      758815 :     _omega_P = error[besti] * _P_gain(spin_rate) * _kp;
    1059      758815 :     if (use_fast_gains()) {
    1060      109693 :         _omega_P *= 8;
    1061             :     }
    1062             : 
    1063      456328 :     if (fly_forward && _gps.status() >= AP_GPS_FixType::FIX_2D &&
    1064      220180 :             _gps.ground_speed() < GPS_SPEED_MIN &&
    1065       59989 :             _ins.get_accel().x >= 7 &&
    1066      995234 :         pitch > radians(-30) && pitch < radians(30)) {
    1067             :         // assume we are in a launch acceleration, and reduce the
    1068             :         // rp gain by 50% to reduce the impact of GPS lag on
    1069             :         // takeoff attitude when using a catapult
    1070         122 :         _omega_P *= 0.5f;
    1071             :     }
    1072             : 
    1073             :     // accumulate some integrator error
    1074      758815 :     if (spin_rate < radians(SPIN_RATE_LIMIT)) {
    1075      694098 :         _omega_I_sum += error[besti] * _ki * _ra_deltat;
    1076      694098 :         _omega_I_sum_time += _ra_deltat;
    1077             :     }
    1078             : 
    1079      758815 :     if (_omega_I_sum_time >= 5) {
    1080             :         // limit the rate of change of omega_I to the hardware
    1081             :         // reported maximum gyro drift rate. This ensures that
    1082             :         // short term errors don't cause a buildup of omega_I
    1083             :         // beyond the physical limits of the device
    1084       25788 :         const float change_limit = AP::ins().get_gyro_drift_rate() * _omega_I_sum_time;
    1085       25788 :         _omega_I_sum.x = constrain_float(_omega_I_sum.x, -change_limit, change_limit);
    1086       25788 :         _omega_I_sum.y = constrain_float(_omega_I_sum.y, -change_limit, change_limit);
    1087       25788 :         _omega_I_sum.z = constrain_float(_omega_I_sum.z, -change_limit, change_limit);
    1088       25788 :         _omega_I += _omega_I_sum;
    1089       25788 :         _omega_I_sum.zero();
    1090       25788 :         _omega_I_sum_time = 0;
    1091             :     }
    1092             : 
    1093             :     // zero our accumulator ready for the next GPS step
    1094      758815 :     memset((void *)&_ra_sum[0], 0, sizeof(_ra_sum));
    1095      758815 :     _ra_deltat = 0;
    1096      758815 :     _ra_sum_start = last_correction_time;
    1097             : 
    1098             :     // remember the velocity for next time
    1099      758815 :     _last_velocity = velocity;
    1100             : }
    1101             : 
    1102             : 
    1103             : // update our wind speed estimate
    1104      696272 : void AP_AHRS_DCM::estimate_wind(void)
    1105             : {
    1106      696272 :     if (!AP::ahrs().get_wind_estimation_enabled()) {
    1107      458447 :         return;
    1108             :     }
    1109             : 
    1110             :     // the wind-triangle filters below blend a fixed fraction of the new
    1111             :     // estimate on each call, and the straight-flight branch does not
    1112             :     // update _last_wind_time, so the effective filter time constant is
    1113             :     // set by the call rate.  Now that this is driven on each new GPS
    1114             :     // sample rather than by a fixed-rate vehicle task, limit to 10Hz
    1115             :     // here so the time constant stays sane:
    1116      264533 :     const uint32_t now = AP_HAL::millis();
    1117      264533 :     if (now - _last_wind_estimate_ms < 100) {
    1118        5890 :         return;
    1119             :     }
    1120      258643 :     _last_wind_estimate_ms = now;
    1121             : 
    1122      258643 :     const Vector3f &velocity = _last_velocity;
    1123             : 
    1124             :     // this is based on the wind speed estimation code from MatrixPilot by
    1125             :     // Bill Premerlani. Adaption for ArduPilot by Jon Challinger
    1126             :     // See http://gentlenav.googlecode.com/files/WindEstimation.pdf
    1127             :     // use the trim-corrected (vehicle body) forward axis, not the raw board
    1128             :     // axis _dcm_matrix.colx(): a non-zero AHRS_TRIM otherwise feeds the wind
    1129             :     // triangle the sensor-board direction rather than the fuselage direction.
    1130      258643 :     const Vector3f fuselageDirection = _body_dcm_matrix.colx();
    1131      258643 :     const Vector3f fuselageDirectionDiff = fuselageDirection - _last_fuse;
    1132             : 
    1133             :     // scrap our data and start over if we're taking too long to get a direction change
    1134      258643 :     if (now - _last_wind_time > 10000) {
    1135        2803 :         _last_wind_time = now;
    1136        2803 :         _last_fuse = fuselageDirection;
    1137        2803 :         _last_vel = velocity;
    1138        2803 :         return;
    1139             :     }
    1140             : 
    1141      255840 :     float diff_length = fuselageDirectionDiff.length();
    1142      255840 :     if (diff_length > 0.2f) {
    1143             :         // when turning, use the attitude response to estimate
    1144             :         // wind speed
    1145       18015 :         const Vector3f velocityDiff = velocity - _last_vel;
    1146             : 
    1147             :         // estimate airspeed it using equation 6
    1148       18015 :         const float V = velocityDiff.length() / diff_length;
    1149             : 
    1150       18015 :         const Vector3f fuselageDirectionSum = fuselageDirection + _last_fuse;
    1151       18015 :         const Vector3f velocitySum = velocity + _last_vel;
    1152             : 
    1153       18015 :         _last_fuse = fuselageDirection;
    1154       18015 :         _last_vel = velocity;
    1155             : 
    1156       18015 :         const float theta = atan2f(velocityDiff.y, velocityDiff.x) - atan2f(fuselageDirectionDiff.y, fuselageDirectionDiff.x);
    1157       18015 :         const float sintheta = sinf(theta);
    1158       18015 :         const float costheta = cosf(theta);
    1159             : 
    1160             :         Vector3f wind{
    1161       18015 :             velocitySum.x - V * (costheta * fuselageDirectionSum.x - sintheta * fuselageDirectionSum.y),
    1162       18015 :             velocitySum.y - V * (sintheta * fuselageDirectionSum.x + costheta * fuselageDirectionSum.y),
    1163       18015 :             velocitySum.z - V * fuselageDirectionSum.z
    1164       18015 :         };
    1165       18015 :         wind *= 0.5f;
    1166             : 
    1167       18015 :         if (wind.length() < _wind.length() + 20) {
    1168       16356 :             _wind = _wind * 0.95f + wind * 0.05f;
    1169             :         }
    1170             : 
    1171       18015 :         _last_wind_time = now;
    1172             : 
    1173       18015 :         return;
    1174             :     }
    1175             : 
    1176             : #if AP_AIRSPEED_ENABLED
    1177      237825 :     if (now - _last_wind_time > 2000 && airspeed_sensor_enabled()) {
    1178             :         // when flying straight use airspeed to get wind estimate if available
    1179      137503 :         const Vector3f airspeed = fuselageDirection * AP::airspeed()->get_airspeed();
    1180      137503 :         const Vector3f wind = velocity - (airspeed * get_EAS2TAS());
    1181      137503 :         _wind = _wind * 0.92f + wind * 0.08f;
    1182             :     }
    1183             : #endif
    1184             : }
    1185             : 
    1186             : #if AP_AHRS_EXTERNAL_WIND_ESTIMATE_ENABLED
    1187          14 : void AP_AHRS_DCM::set_external_wind_estimate(float speed, float direction) {
    1188          14 :     _wind.x = -cosf(radians(direction)) * speed;
    1189          14 :     _wind.y = -sinf(radians(direction)) * speed;
    1190          14 :     _wind.z = 0;
    1191          14 : }
    1192             : #endif
    1193             : 
    1194             : // return our current position estimate using
    1195             : // dead-reckoning or GPS
    1196    41686716 : bool AP_AHRS_DCM::get_location(Location &loc) const
    1197             : {
    1198    41686716 :     if (_last_lat == 0 && _last_lng == 0) {
    1199             :         // we have never had a position
    1200     3211625 :         return false;
    1201             :     }
    1202    38475091 :     loc.lat = _last_lat;
    1203    38475091 :     loc.lng = _last_lng;
    1204    38475091 :     const auto &baro = AP::baro();
    1205    38475091 :     const auto &gps = AP::gps();
    1206             :     int32_t alt_cm;
    1207    38475091 :     if (_gps_use == GPSUse::EnableWithHeight &&
    1208           0 :         gps.status() >= AP_GPS_FixType::FIX_3D) {
    1209           0 :         alt_cm = gps.location().alt;
    1210             :     } else {
    1211    38475091 :         alt_cm = baro.get_altitude() * 100 + AP::ahrs().get_home().alt;
    1212             :     }
    1213    38475091 :     loc.set_alt_cm(alt_cm, Location::AltFrame::ABSOLUTE);
    1214    38475091 :     loc.offset(_position_offset_north, _position_offset_east);
    1215    38475091 :     if (_have_position) {
    1216    38475091 :         const uint32_t now = AP_HAL::millis();
    1217    38475091 :         float dt = 0;
    1218    38475091 :         gps.get_lag(dt);
    1219    38475091 :         dt += constrain_float((now - _last_pos_ms) * 0.001, 0, 0.5);
    1220    38475091 :         Vector2f dpos = _last_velocity.xy() * dt;
    1221    38475091 :         loc.offset(dpos.x, dpos.y);
    1222             :     }
    1223    38475091 :     return _have_position;
    1224             : }
    1225             : 
    1226    43673294 : bool AP_AHRS_DCM::airspeed_EAS(float &airspeed_ret) const
    1227             : {
    1228             : #if AP_AIRSPEED_ENABLED
    1229    43673294 :     const auto *airspeed = AP::airspeed();
    1230    43673294 :     if (airspeed != nullptr) {
    1231    43581483 :         return airspeed_EAS(airspeed->get_primary(), airspeed_ret);
    1232             :     }
    1233             : #endif
    1234             :     // airspeed_estimate will also make this nullptr check and act
    1235             :     // appropriately when we call it with a dummy sensor ID.
    1236       91811 :     return airspeed_EAS(0, airspeed_ret);
    1237             : }
    1238             : 
    1239             : // return an (equivalent) airspeed estimate:
    1240             : //  - from a real sensor if available
    1241             : //  - otherwise from a GPS-derived wind-triangle estimate (if GPS available)
    1242             : //  - otherwise from a cached wind-triangle estimate value (but returning false)
    1243    43744280 : bool AP_AHRS_DCM::airspeed_EAS(uint8_t airspeed_index, float &airspeed_ret) const
    1244             : {
    1245             :     // airspeed_ret: will always be filled-in by get_unconstrained_airspeed_EAS which fills in airspeed_ret in this order:
    1246             :     //               airspeed as filled-in by an enabled airspeed sensor
    1247             :     //               if no airspeed sensor: airspeed estimated using the GPS speed & wind_speed_estimation
    1248             :     //               Or if none of the above, fills-in using the previous airspeed estimate
    1249             :     // Return false: if we are using the previous airspeed estimate
    1250    43744280 :     if (!get_unconstrained_airspeed_EAS(airspeed_index, airspeed_ret)) {
    1251    34344030 :         return false;
    1252             :     }
    1253             : 
    1254     9400250 :     const float _wind_max = AP::ahrs().get_max_wind();
    1255     9400250 :     if (_wind_max > 0 && AP::gps().status() >= AP_GPS_FixType::FIX_2D) {
    1256             :         // constrain the airspeed by the ground speed
    1257             :         // and AHRS_WIND_MAX
    1258           0 :         const float gnd_speed = AP::gps().ground_speed();
    1259           0 :         float true_airspeed = airspeed_ret * get_EAS2TAS();
    1260           0 :         true_airspeed = constrain_float(true_airspeed,
    1261             :                                         gnd_speed - _wind_max,
    1262             :                                         gnd_speed + _wind_max);
    1263           0 :         airspeed_ret = true_airspeed / get_EAS2TAS();
    1264             :     }
    1265             : 
    1266     9400250 :     return true;
    1267             : }
    1268             : 
    1269             : // airspeed_ret: will always be filled-in by get_unconstrained_airspeed_EAS which fills in airspeed_ret in this order:
    1270             : //               airspeed as filled-in by an enabled airspeed sensor
    1271             : //               if no airspeed sensor: airspeed estimated using the GPS speed & wind_speed_estimation
    1272             : //               Or if none of the above, fills-in using the previous airspeed estimate
    1273             : // Return false: if we are using the previous airspeed estimate
    1274    43744280 : bool AP_AHRS_DCM::get_unconstrained_airspeed_EAS(uint8_t airspeed_index, float &airspeed_ret) const
    1275             : {
    1276             : #if AP_AIRSPEED_ENABLED
    1277    43744280 :     if (airspeed_sensor_enabled(airspeed_index)) {
    1278     9296838 :         airspeed_ret = AP::airspeed()->get_airspeed(airspeed_index);
    1279     9296838 :         return true;
    1280             :     }
    1281             : #endif
    1282             : 
    1283    34447442 :     if (AP::ahrs().get_wind_estimation_enabled() && have_gps()) {
    1284             :         // estimated via GPS speed and wind
    1285      103412 :         airspeed_ret = _last_airspeed_TAS * get_TAS2EAS();
    1286      103412 :         return true;
    1287             :     }
    1288             : 
    1289             :     // Else give the last estimate, but return false.
    1290             :     // This is used by the dead-reckoning code
    1291    34344030 :     airspeed_ret = _last_airspeed_TAS * get_TAS2EAS();
    1292    34344030 :     return false;
    1293             : }
    1294             : 
    1295             : /*
    1296             :   return NED velocity if we have GPS lock
    1297             :  */
    1298    83373432 : bool AP_AHRS_DCM::get_velocity_NED(Vector3f &vec) const
    1299             : {
    1300    83373432 :     const AP_GPS &_gps = AP::gps();
    1301    83373432 :     if (_gps.status() < AP_GPS_FixType::FIX_3D) {
    1302     7122076 :         return false;
    1303             :     }
    1304    76251356 :     vec = _gps.velocity();
    1305    76251356 :     return true;
    1306             : }
    1307             : 
    1308             : // return a ground speed estimate in m/s
    1309    41686716 : Vector2f AP_AHRS_DCM::groundspeed_vector(void)
    1310             : {
    1311             :     // Generate estimate of ground speed vector using air data system
    1312    41686716 :     Vector2f gndVelADS;
    1313    41686716 :     Vector2f gndVelGPS;
    1314    41686716 :     float airspeed = 0;
    1315    41686716 :     const bool gotAirspeed = airspeed_TAS(airspeed);
    1316    41686716 :     const bool gotGPS = (AP::gps().status() >= AP_GPS_FixType::FIX_2D);
    1317    41686716 :     if (gotAirspeed) {
    1318     8316926 :         const Vector2f airspeed_vector{_cos_yaw * airspeed, _sin_yaw * airspeed};
    1319     8316926 :         gndVelADS = airspeed_vector + _wind.xy();
    1320             :     }
    1321             : 
    1322             :     // Generate estimate of ground speed vector using GPS
    1323    41686716 :     if (gotGPS) {
    1324    38125918 :         const float cog = radians(AP::gps().ground_course());
    1325    38125918 :         gndVelGPS = Vector2f(cosf(cog), sinf(cog)) * AP::gps().ground_speed();
    1326             :     }
    1327             :     // If both ADS and GPS data is available, apply a complementary filter
    1328    41686716 :     if (gotAirspeed && gotGPS) {
    1329             :         // The LPF is applied to the GPS and the HPF is applied to the air data estimate
    1330             :         // before the two are summed
    1331             :         //Define filter coefficients
    1332             :         // alpha and beta must sum to one
    1333             :         // beta = dt/Tau, where
    1334             :         // dt = filter time step (0.1 sec if called by nav loop)
    1335             :         // Tau = cross-over time constant (nominal 2 seconds)
    1336             :         // More lag on GPS requires Tau to be bigger, less lag allows it to be smaller
    1337             :         // To-Do - set Tau as a function of GPS lag.
    1338     7944122 :         const float alpha = 1.0f - beta;
    1339             :         // Run LP filters
    1340     7944122 :         _lp = gndVelGPS * beta  + _lp * alpha;
    1341             :         // Run HP filters
    1342     7944122 :         _hp = (gndVelADS - _lastGndVelADS) + _hp * alpha;
    1343             :         // Save the current ADS ground vector for the next time step
    1344     7944122 :         _lastGndVelADS = gndVelADS;
    1345             :         // Sum the HP and LP filter outputs
    1346     7944122 :         return _hp + _lp;
    1347             :     }
    1348             :     // Only ADS data is available return ADS estimate
    1349    33742594 :     if (gotAirspeed && !gotGPS) {
    1350      372804 :         return gndVelADS;
    1351             :     }
    1352             :     // Only GPS data is available so return GPS estimate
    1353    33369790 :     if (!gotAirspeed && gotGPS) {
    1354    30181796 :         return gndVelGPS;
    1355             :     }
    1356             : 
    1357     3187994 :     if (airspeed > 0) {
    1358             :         // we have a rough airspeed, and we have a yaw. For
    1359             :         // dead-reckoning purposes we can create a estimated
    1360             :         // groundspeed vector
    1361      102808 :         Vector2f ret{_cos_yaw, _sin_yaw};
    1362      102808 :         ret *= airspeed;
    1363             :         // adjust for estimated wind
    1364      102808 :         ret += _wind.xy();
    1365      102808 :         return ret;
    1366             :     }
    1367             : 
    1368     3085186 :     return Vector2f(0.0f, 0.0f);
    1369             : }
    1370             : 
    1371             : // Get a derivative of the vertical position in m/s which is kinematically consistent with the vertical position is required by some control loops.
    1372             : // This is different to the vertical velocity from the EKF which is not always consistent with the vertical position due to the various errors that are being corrected for.
    1373    41686716 : bool AP_AHRS_DCM::get_vert_pos_rate_D(float &velocity) const
    1374             : {
    1375    41686716 :     Vector3f velned;
    1376    41686716 :     if (get_velocity_NED(velned)) {
    1377    38125678 :         velocity = velned.z;
    1378    38125678 :         return true;
    1379     3561038 :     } else if (AP::baro().healthy()) {
    1380     3546448 :         velocity = -AP::baro().get_climb_rate();
    1381     3546448 :         return true;
    1382             :     }
    1383       14590 :     return false;
    1384             : }
    1385             : 
    1386             : /*
    1387             :   relative-origin functions for fallback in AP_InertialNav
    1388             : */
    1389    41913062 : bool AP_AHRS_DCM::get_origin(Location &ret) const
    1390             : {
    1391    41913062 :     ret = last_origin;
    1392    41913062 :     if (ret.is_zero()) {
    1393             :         // use home if we never have had an origin
    1394     5110769 :         ret = AP::ahrs().get_home();
    1395             :     }
    1396    41913062 :     return !ret.is_zero();
    1397             : }
    1398             : 
    1399             : // return true if DCM has a yaw source available
    1400     8462025 : bool AP_AHRS_DCM::yaw_source_available(void) const
    1401             : {
    1402     8462025 :     return AP::compass().use_for_yaw();
    1403             : }
    1404             : 
    1405             : #endif  // AP_AHRS_DCM_ENABLED

Generated by: LCOV version 1.14