LCOV - code coverage report
Current view: top level - AP_AHRS - AP_AHRS_NavEKF2.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 91 93 97.8 %
Date: 2026-09-09 11:02:08 Functions: 4 4 100.0 %

          Line data    Source code
       1             : #include "AP_AHRS_config.h"
       2             : 
       3             : #if AP_AHRS_NAVEKF2_ENABLED
       4             : 
       5             : #include "AP_AHRS_NavEKF2.h"
       6             : #include <AP_AHRS/AP_AHRS.h>
       7             : #include <AP_HAL/AP_HAL.h>
       8             : #include <AP_Logger/AP_Logger.h>
       9             : #include <GCS_MAVLink/GCS.h>
      10             : 
      11             : extern const AP_HAL::HAL& hal;
      12             : 
      13             : NavEKF2 AP_AHRS_NavEKF2::EKF2;
      14             : 
      15    39580103 : bool AP_AHRS_NavEKF2::start()
      16             : {
      17    39580103 :     const auto now_ms = AP_HAL::millis();
      18             : 
      19    39580103 :     if (start_time_ms == 0) {
      20        2086 :         start_time_ms = now_ms;
      21             :     }
      22             : 
      23             : #if HAL_LOGGING_ENABLED
      24             :     // if we're doing Replay logging then don't allow any data
      25             :     // into the EKF yet.  Don't allow it to block us for long.
      26    39580103 :     if (!hal.util->was_watchdog_reset()) {
      27    39579661 :         if (now_ms - start_time_ms < 5000) {
      28     2171934 :             if (!AP::logger().allow_start_ekf()) {
      29          43 :                 return false;
      30             :             }
      31             :         }
      32             :     }
      33             : #endif
      34             : 
      35             :     // wait 1 second for DCM to output a valid tilt error estimate
      36             :     // FIXME: work out whether this is still required!
      37    39580060 :     if (now_ms - start_time_ms <= 1000) {
      38      530414 :         return false;
      39             :     }
      40             : 
      41             :     // try to start the filter:
      42    39049646 :     return EKF2.InitialiseFilter();
      43             : }
      44             : 
      45    41686716 : void AP_AHRS_NavEKF2::update()
      46             : {
      47    41686716 :     if (!started) {
      48    39580103 :         started = start();
      49             :     }
      50    41686716 :     if (!started) {
      51    39579824 :         return;
      52             :     }
      53     2106892 :     EKF2.UpdateFilter();
      54             : 
      55             :     // check the current primary core; if it has changed then assume
      56             :     // our attitude is reset:
      57     2106892 :     const int8_t primary_core = EKF2.getPrimaryCoreIndex();
      58     2106892 :     if (attitude_reset_tracker.update(primary_core)) {
      59           6 :         LOGGER_WRITE_ERROR(LogErrorSubsystem::EKF_PRIMARY, LogErrorCode(primary_core));
      60           6 :         GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "EKF2 primary changed:%d", (unsigned)primary_core);
      61             :     }
      62             : 
      63     2106892 :     yaw_reset_tracker.update(EKF2.getYawResetCount());
      64             : 
      65     2106892 :     position_NE_reset_tracker.update(EKF2.getPosNorthEastResetCount());
      66             : 
      67     2106892 :     position_D_reset_tracker.update(EKF2.getPosDownResetCount());
      68             : }
      69             : 
      70    41686716 : void AP_AHRS_NavEKF2::get_results(AP_AHRS_Backend::Estimates &results)
      71             : {
      72    41686716 :     const auto now_ms = AP_HAL::millis();
      73             : 
      74             :     // initialisation complete some time after ekf has started
      75    41686716 :     results.initialised = (started && (now_ms - start_time_ms > 20000));
      76             : 
      77    41686716 :     results.healthy = started && EKF2.healthy();
      78             : 
      79    41686716 :     const AP_InertialSensor &_ins = AP::ins();
      80             : 
      81             :     /*
      82             :      * rotational rate estimates:
      83             :      */
      84             : 
      85             :     // The backend should tell us what gyro it is using right now.
      86             :     // There's no API for that at the moment, so we assume the
      87             :     // gyro corresponds to the IMU index.
      88    41686716 :     const int8_t ek2_primary_gyro = EKF2.getPrimaryCoreIMUIndex();
      89    41686716 :     if (ek2_primary_gyro == -1) {
      90    39476267 :         results.primary_gyro = _ins.get_first_usable_gyro();
      91             :     } else {
      92     2210449 :         results.primary_gyro = ek2_primary_gyro;
      93             :     }
      94             : 
      95             :     // get gyro bias for primary EKF and change sign to give gyro drift
      96             :     // Note sign convention used by EKF is bias = measurement - truth
      97    41686716 :     Vector3f drift;
      98    41686716 :     EKF2.getGyroBias(drift);
      99    41686716 :     results.gyro_drift = -drift;
     100             : 
     101             :     // use the same IMU as the primary EKF and correct for gyro drift
     102    41686716 :     results.gyro_estimate = _ins.get_gyro(results.primary_gyro) + results.gyro_drift;
     103             : 
     104             :     /*
     105             :      * attitude estimates:
     106             :      */
     107    41686716 :     EKF2.getRotationBodyToNED(results.dcm_matrix);
     108    41686716 :     Vector3f eulers;
     109    41686716 :     EKF2.getEulerAngles(eulers);
     110    41686716 :     results.roll_rad  = eulers.x;
     111    41686716 :     results.pitch_rad = eulers.y;
     112    41686716 :     results.yaw_rad   = eulers.z;
     113             : 
     114    41686716 :     EKF2.getQuaternion(results.quaternion);
     115    41686716 :     results.quaternion.rotate(-AP::ahrs().get_trim());
     116             : 
     117    41686716 :     results.attitude_valid = started;
     118             : 
     119    41686716 :     results.attitude_reset_count = attitude_reset_tracker.count();
     120             : 
     121    41686716 :     results.yaw_reset_count = yaw_reset_tracker.count();
     122             : 
     123             :     /*
     124             :      * acceleration estimates
     125             :      */
     126             : 
     127             :     // The backend should tell us what accelerometer it is using
     128             :     // right now.  There's no API for that at the moment, so we assume
     129             :     // the accelerometer corresponds to the IMU index.
     130    41686716 :     const int8_t ek2_primary_accel = EKF2.getPrimaryCoreIMUIndex();
     131    41686716 :     if (ek2_primary_accel == -1) {
     132    39476267 :         results.primary_accel = _ins.get_first_usable_accel();
     133             :     } else {
     134     2210449 :         results.primary_accel = ek2_primary_accel;
     135             :     }
     136             : 
     137             :     // get z accel bias estimate from active EKF (this is usually for the primary IMU)
     138    41686716 :     EKF2.getAccelZBias(results.accel_bias.z);
     139             : 
     140             :     // This EKF is currently using primary_imu, and a bias applies to only that IMU
     141    41686716 :     Vector3f accel = _ins.get_accel(results.primary_accel);
     142    41686716 :     accel.z -= results.accel_bias.z;
     143    41686716 :     results.accel_ef = results.dcm_matrix * AP::ahrs().get_rotation_autopilot_body_to_vehicle_body() * accel;
     144             : 
     145             :     /*
     146             :      * velocity estimates
     147             :      */
     148    41686716 :     EKF2.getVelNED(results.velocity_NED);
     149    41686716 :     results.velocity_NED_valid = true;
     150    41686716 :     results.vert_pos_rate_D = EKF2.getPosDownDerivative();
     151    41686716 :     results.vert_pos_rate_D_valid = true;
     152             : 
     153             :     // ground velocity estimate in meters/second, in North/East order
     154    41686716 :     results.velocity_NE = results.velocity_NED.xy();
     155             : 
     156             :     /*
     157             :      * position estimates
     158             :      */
     159    41686716 :     results.location_valid = EKF2.getLLH(results.location);
     160             : 
     161             :     // origin-relative functions
     162    41686716 :     results.provides_common_origin = true;
     163             : 
     164             :     // origin-relative position:
     165    41686716 :     results.position_NE_valid = EKF2.getPosNE(results.position_NE);
     166    41686716 :     results.position_NE_reset_count = position_NE_reset_tracker.count();
     167             : 
     168    41686716 :     results.position_D_valid = EKF2.getPosD(results.position_D);
     169    41686716 :     results.position_D_reset_count = position_D_reset_tracker.count();
     170             : 
     171    41686716 :     results.hagl_valid = EKF2.getHAGL(results.hagl);
     172             : 
     173             :     /*
     174             :      * air data estimates
     175             :      */
     176    41686716 :     EKF2.getWind(results.wind);
     177    41686716 :     results.wind_valid = true;
     178             : 
     179             :     /*
     180             :      * Sensor-related information
     181             :      */
     182             :     // true if the estimator will use GPS data in creating its
     183             :     // estimate when the data is good:
     184    41686716 :     results.configured_to_use_gps = EKF2.using_gps();
     185             :     // true if GPS is configured as the horizontal position source for
     186             :     // this estimator.  Used to decide whether GPS will set the
     187             :     // navigation origin:
     188    41686716 :     results.configured_to_use_gps_for_pos_XY = EKF2.configuredToUseGPSForPosXY();
     189             : 
     190             :     // are we consuming yaw from an external (e.g. vision-based) source?
     191    41686716 :     results.using_extnav_for_yaw = EKF2.isExtNavUsedForYaw();
     192             : 
     193             :     // are we consuming yaw from a source which is *not* a compass
     194    41686716 :     results.using_noncompass_for_yaw = EKF2.isExtNavUsedForYaw();
     195             : 
     196             : #if AP_AHRS_GET_MAG_DATA_ENABLED
     197             :     // estimators can provide their predicted magnetic fields:
     198    41686716 :     EKF2.getMagNED(results.mag_field_NED);
     199    41686716 :     results.mag_field_NED_valid = true;
     200    41686716 :     EKF2.getMagXYZ(results.mag_field_corrections);
     201    41686716 :     results.mag_field_corrections_valid = true;
     202             : #endif  // AP_AHRS_GET_MAG_DATA_ENABLED
     203             : 
     204             :     /*
     205             :      * filter status and estimates quality values:
     206             :      */
     207    41686716 :     EKF2.getFilterStatus(results.filter_status);
     208    41686716 :     results.filter_status_valid = true;
     209             : 
     210    41686716 :     EKF2.getFilterFaults(results.filter_faults);
     211             : 
     212             :     // provides the innovations normalised between 0 and 1:
     213    41686716 :     Vector2f offset;
     214    41686716 :     results.variances_valid = EKF2.getVariances(results.velVar, results.posVar, results.hgtVar, results.magVar, results.tasVar, offset);
     215             : 
     216    41686716 :     results.terrain_alt_variance_valid = EKF2.getTerrainAltVariance(results.terrain_alt_variance);
     217             : 
     218    41686716 :     EKF2.getEkfControlLimits(results.control_ground_speed_limit_ms, results.control_gain_scaler_XY);
     219    41686716 :     results.control_gain_scaler_Z = 1;
     220             : 
     221    41686716 :     results.control_height_limit_valid = EKF2.getHeightControlLimit(results.control_height_limit_m);
     222    41686716 : }
     223             : 
     224          80 : bool AP_AHRS_NavEKF2::pre_arm_check(bool requires_position, char *failure_msg, uint8_t failure_msg_len) const
     225             : {
     226          80 :     if (!started) {
     227           0 :         hal.util->snprintf(failure_msg, failure_msg_len, "EKF2 not started");
     228           0 :         return false;
     229             :     }
     230          80 :     return EKF2.pre_arm_check(failure_msg, failure_msg_len);
     231             : }
     232             : 
     233             : #endif  // AP_AHRS_NAVEKF2_ENABLED

Generated by: LCOV version 1.14