LCOV - code coverage report
Current view: top level - AP_NavEKF - EKFGSF_yaw.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 320 321 99.7 %
Date: 2026-09-09 11:02:08 Functions: 15 15 100.0 %

          Line data    Source code
       1             : /*
       2             :   Definition of functions used to provide a backup estimate of yaw angle
       3             :   that doesn't use a magnetometer. Uses a bank of 3-state EKF's organised
       4             :   as a Gaussian sum filter where states are velocity N,E (m/s) and yaw angle (rad)
       5             : 
       6             :   Written by Paul Riseborough <[email protected]>
       7             : 
       8             :   This program is free software: you can redistribute it and/or modify
       9             :   it under the terms of the GNU General Public License as published by
      10             :   the Free Software Foundation, either version 3 of the License, or
      11             :   (at your option) any later version.
      12             : 
      13             :   This program is distributed in the hope that it will be useful,
      14             :   but WITHOUT ANY WARRANTY; without even the implied warranty of
      15             :   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      16             :   GNU General Public License for more details.
      17             : 
      18             :   You should have received a copy of the GNU General Public License
      19             :   along with this program.  If not, see <http://www.gnu.org/licenses/>.
      20             :  */
      21             : 
      22             : #include <AP_HAL/AP_HAL.h>
      23             : 
      24             : #include "AP_NavEKF/EKFGSF_yaw.h"
      25             : #include <AP_AHRS/AP_AHRS.h>
      26             : #include <GCS_MAVLink/GCS.h>
      27             : 
      28       29856 : EKFGSF_yaw::EKFGSF_yaw() {};
      29             : 
      30    23005950 : void EKFGSF_yaw::update(const Vector3F &delAng,
      31             :                         const Vector3F &delVel,
      32             :                         const ftype delAngDT,
      33             :                         const ftype delVelDT,
      34             :                         bool runEKF,
      35             :                         ftype TAS)
      36             : {
      37             : 
      38             :     // copy to class variables
      39    23005950 :     delta_angle = delAng;
      40    23005950 :     delta_velocity = delVel;
      41    23005950 :     angle_dt = delAngDT;
      42    23005950 :     velocity_dt = delVelDT;
      43    23005950 :     run_ekf_gsf = runEKF;
      44    23005950 :     true_airspeed = TAS;
      45             : 
      46             :     // Calculate a low pass filtered acceleration vector that will be used to keep the AHRS tilt aligned
      47             :     // The time constant of the filter is a fixed ratio relative to the time constant of the AHRS tilt correction loop
      48    23005950 :     const ftype filter_coef = fminF(EKFGSF_accelFiltRatio * delVelDT * EKFGSF_tiltGain, 1.0f);
      49    23005950 :     const Vector3F accel = delVel / fmaxF(delVelDT, 0.001f);
      50    23005950 :     ahrs_accel = ahrs_accel * (1.0f - filter_coef) + accel * filter_coef;
      51             : 
      52             :     // Iniitialise states and only when acceleration is close to 1g to prevent vehicle movement casuing a large initial tilt error
      53    23005950 :     if (!ahrs_tilt_aligned) {
      54       28643 :         const ftype accel_norm_sq = accel.length_squared();
      55             :         const ftype upper_accel_limit = GRAVITY_MSS * 1.1f;
      56             :         const ftype lower_accel_limit = GRAVITY_MSS * 0.9f;
      57       28643 :         const bool ok_to_align = ((accel_norm_sq > lower_accel_limit * lower_accel_limit &&
      58             :                                    accel_norm_sq < upper_accel_limit * upper_accel_limit));
      59             :         if (ok_to_align) {
      60        3627 :             alignTilt();
      61        3627 :             ahrs_tilt_aligned = true;
      62        3627 :             ahrs_accel = accel;
      63             :         }
      64       28643 :         return;
      65             :     }
      66             : 
      67             :     // Calculate common variables used by the AHRS prediction models
      68    22977307 :     ahrs_accel_norm = ahrs_accel.length();
      69             : 
      70             :     // Calculate AHRS acceleration fusion gain using a quadratic weighting function that is unity at 1g
      71             :     // and zero at the min and max g limits. This reduces the effect of large g transients on the attitude
      72             :     // estimates.
      73    22977307 :     ftype EKFGSF_ahrs_ng = ahrs_accel_norm / GRAVITY_MSS;
      74    22977307 :     if (EKFGSF_ahrs_ng > 1.0f) {
      75    19475551 :         if (is_positive(true_airspeed)) {
      76             :             // When flying in fixed wing mode we need to allow for more positive g due to coordinated turns
      77             :             // Gain varies from unity at 1g to zero at 2g
      78     3452148 :             accel_gain = EKFGSF_tiltGain * sq(MAX(2.0f - EKFGSF_ahrs_ng, 0.0f));
      79    16023403 :         } else if (EKFGSF_ahrs_ng <= 1.5f) {
      80             :             // Gain varies from unity at 1g to zero at 1.5g
      81    15831052 :             accel_gain = EKFGSF_tiltGain * sq(3.0f - 2.0f * EKFGSF_ahrs_ng);
      82             :         } else {
      83             :             // Gain is zero above max g
      84      192351 :             accel_gain = 0.0f;
      85             :         }
      86     3501756 :     } else if (EKFGSF_ahrs_ng > 0.5f) {
      87             :         // Gain varies from zero at 0.5g to unity at 1g
      88     3448951 :         accel_gain = EKFGSF_tiltGain * sq(2.0f * EKFGSF_ahrs_ng - 1.0f);
      89             :     } else {
      90             :         // Gain is zero below min g
      91       52805 :         accel_gain = 0.0f;
      92             :     }
      93             : 
      94             :     // Always run the AHRS prediction cycle for each model
      95   137863842 :     for (uint8_t mdl_idx = 0; mdl_idx < N_MODELS_EKFGSF; mdl_idx++) {
      96   114886535 :         predict(mdl_idx);
      97             :     }
      98             : 
      99    22977307 :     if (vel_fuse_running && !run_ekf_gsf) {
     100        1396 :         vel_fuse_running = false;
     101             :     }
     102             : 
     103             :     // Calculate a composite yaw as a weighted average of the states for each model.
     104             :     // To avoid issues with angle wrapping, the yaw state is converted to a vector with legnth
     105             :     // equal to the weighting value before it is summed.
     106    22977307 :     Vector2F yaw_vector;
     107   137863842 :     for (uint8_t mdl_idx = 0; mdl_idx < N_MODELS_EKFGSF; mdl_idx++) {
     108   114886535 :         yaw_vector[0] += GSF.weights[mdl_idx] * cosF(EKF[mdl_idx].X[2]);
     109   114886535 :         yaw_vector[1] += GSF.weights[mdl_idx] * sinF(EKF[mdl_idx].X[2]);
     110             :     }
     111    22977307 :     GSF.yaw = atan2F(yaw_vector[1],yaw_vector[0]);
     112             : 
     113             :     // Example for future reference showing how a full GSF covariance matrix could be calculated if required
     114             :     /*
     115             :     memset(&GSF.P, 0, sizeof(GSF.P));
     116             :     for (uint8_t mdl_idx = 0; mdl_idx < N_MODELS_EKFGSF; mdl_idx++) {
     117             :         ftype delta[3];
     118             :         for (uint8_t row = 0; row < 3; row++) {
     119             :             delta[row] = EKF[mdl_idx].X[row] - GSF.X[row];
     120             :         }
     121             :         for (uint8_t row = 0; row < 3; row++) {
     122             :             for (uint8_t col = 0; col < 3; col++) {
     123             :                 GSF.P[row][col] +=  GSF.weights[mdl_idx] * (EKF[mdl_idx].P[row][col] + delta[row] * delta[col]);
     124             :             }
     125             :         }
     126             :     }
     127             :     */
     128             : 
     129    22977307 :     GSF.yaw_variance = 0.0f;
     130   137863842 :     for (uint8_t mdl_idx = 0; mdl_idx < N_MODELS_EKFGSF; mdl_idx++) {
     131   114886535 :         ftype yawDelta = wrap_PI(EKF[mdl_idx].X[2] - GSF.yaw);
     132   114886535 :         GSF.yaw_variance +=  GSF.weights[mdl_idx] * (EKF[mdl_idx].P[2][2] + sq(yawDelta));
     133             :     }
     134             : }
     135             : 
     136     1232687 : void EKFGSF_yaw::fuseVelData(const Vector2F &vel, const ftype velAcc)
     137             : {
     138             :     // convert reported accuracy to a variance, but limit lower value to protect algorithm stability
     139     1232687 :     const ftype velObsVar = sq(fmaxF(velAcc, 0.5f));
     140             : 
     141             :     // The 3-state EKF models only run when flying to avoid corrupted estimates due to operator handling and GPS interference
     142     1232687 :     if (run_ekf_gsf) {
     143     1232687 :         if (!vel_fuse_running) {
     144             :             // Perform in-flight alignment
     145        3657 :             resetEKFGSF();
     146       21942 :             for (uint8_t mdl_idx = 0; mdl_idx < N_MODELS_EKFGSF; mdl_idx++) {
     147             :                 // Use the firstGPS  measurement to set the velocities and corresponding variances
     148       18285 :                 EKF[mdl_idx].X[0] = vel[0];
     149       18285 :                 EKF[mdl_idx].X[1] = vel[1];
     150       18285 :                 EKF[mdl_idx].P[0][0] = velObsVar;
     151       18285 :                 EKF[mdl_idx].P[1][1] = velObsVar;
     152             :             }
     153        3657 :             alignYaw();
     154        3657 :             vel_fuse_running = true;
     155             :         } else {
     156             :             ftype total_w = 0.0f;
     157             :             ftype newWeight[(uint8_t)N_MODELS_EKFGSF];
     158             :             bool state_update_failed = false;
     159     7374180 :             for (uint8_t mdl_idx = 0; mdl_idx < N_MODELS_EKFGSF; mdl_idx++) {
     160             :                 // Update states and covariances using GPS NE velocity measurements fused as direct state observations
     161     6145150 :                 if (!correct(mdl_idx, vel, velObsVar)) {
     162             :                     state_update_failed = true;
     163             :                 }
     164             :             }
     165             : 
     166     1229030 :             if (!state_update_failed) {
     167             :                 // Calculate weighting for each model assuming a normal error distribution
     168             :                 const ftype min_weight = 1e-5f;
     169     1229030 :                 n_clips = 0;
     170     7374180 :                 for (uint8_t mdl_idx = 0; mdl_idx < N_MODELS_EKFGSF; mdl_idx++) {
     171     6145150 :                     newWeight[mdl_idx] = gaussianDensity(mdl_idx) * GSF.weights[mdl_idx];
     172     6145150 :                     if (newWeight[mdl_idx] < min_weight) {
     173      319311 :                         n_clips++;
     174      319311 :                         newWeight[mdl_idx] = min_weight;
     175             :                     }
     176     6145150 :                     total_w += newWeight[mdl_idx];
     177             :                 }
     178             : 
     179             :                 // Normalise the sum of weights to unity
     180             :                 // Reset the filters if all weights have underflowed due to excessive innovation variances
     181     1229030 :                 if (vel_fuse_running && n_clips < N_MODELS_EKFGSF) {
     182     1227278 :                     ftype total_w_inv = 1.0f / total_w;
     183     7363668 :                     for (uint8_t mdl_idx = 0; mdl_idx < N_MODELS_EKFGSF; mdl_idx++) {
     184     6136390 :                         GSF.weights[mdl_idx]  = newWeight[mdl_idx] * total_w_inv;
     185             :                     }
     186             :                 } else {
     187        1752 :                     resetEKFGSF();
     188             :                 }
     189             :             }
     190             :         }
     191             :     }
     192     1232687 : }
     193             : 
     194   114886535 : void EKFGSF_yaw::predictAHRS(const uint8_t mdl_idx)
     195             : {
     196             :     // Generate attitude solution using simple complementary filter for the selected model
     197             : 
     198             :     // Calculate 'k' unit vector of earth frame rotated into body frame
     199   114886535 :     const Vector3F k{AHRS[mdl_idx].R[2][0], AHRS[mdl_idx].R[2][1], AHRS[mdl_idx].R[2][2]};
     200             : 
     201             :     // Calculate angular rate vector in rad/sec averaged across last sample interval
     202   114886535 :     const Vector3F ang_rate_delayed_raw { delta_angle / angle_dt };
     203             : 
     204             :     // Perform angular rate correction using accel data and reduce correction as accel magnitude moves away from 1 g (reduces drift when vehicle picked up and moved).
     205             :     // During fixed wing flight, compensate for centripetal acceleration assuming coordinated turns and X axis forward
     206             : 
     207   114886535 :     Vector3F tilt_error_gyro_correction; // (rad/sec)
     208             : 
     209   114886535 :     if (accel_gain > 0.0f) {
     210             : 
     211   113408375 :         Vector3F accel = ahrs_accel;
     212             : 
     213   113408375 :         if (is_positive(true_airspeed)) {
     214             :             // Calculate centripetal acceleration in body frame from cross product of body rate and body frame airspeed vector
     215             :             // NOTE: this assumes X axis is aligned with airspeed vector
     216             :             const Vector3F centripetal_accel_vec_bf {
     217             :                 0.0f,
     218    21774595 :                 ang_rate_delayed_raw[2] * true_airspeed,
     219    21774595 :                 - ang_rate_delayed_raw[1] * true_airspeed
     220    21774595 :             };
     221             :             // Correct measured accel for centripetal acceleration
     222    21774595 :             accel -= centripetal_accel_vec_bf;
     223             :         }
     224             : 
     225   113408375 :         tilt_error_gyro_correction = (k % accel) * (accel_gain / ahrs_accel_norm);
     226             : 
     227             :     }
     228             : 
     229             :     // Gyro bias estimation
     230             :     const ftype gyro_bias_limit = radians(5.0f);
     231   114886535 :     const ftype spinRate_squared = ang_rate_delayed_raw.length_squared();
     232   114886535 :     if (spinRate_squared < sq(0.175f)) {
     233    95509410 :         AHRS[mdl_idx].gyro_bias -= tilt_error_gyro_correction * (EKFGSF_gyroBiasGain * angle_dt);
     234             : 
     235             :         // sanity check
     236    95509410 :         if (AHRS[mdl_idx].gyro_bias.is_nan()) {
     237           0 :             AHRS[mdl_idx].gyro_bias.zero();
     238             :         }
     239             : 
     240   382037640 :         for (uint8_t i = 0; i < 3; i++) {
     241   286528230 :             AHRS[mdl_idx].gyro_bias[i] = constrain_ftype(AHRS[mdl_idx].gyro_bias[i], -gyro_bias_limit, gyro_bias_limit);
     242             :         }
     243             :     }
     244             : 
     245             :     // Calculate the corrected body frame rotation vector for the last sample interval and apply to the rotation matrix
     246   114886535 :     const Vector3F ahrs_delta_angle = delta_angle + (tilt_error_gyro_correction - AHRS[mdl_idx].gyro_bias) * angle_dt;
     247   114886535 :     AHRS[mdl_idx].R = updateRotMat(AHRS[mdl_idx].R, ahrs_delta_angle);
     248             : 
     249   114886535 : }
     250             : 
     251        3627 : void EKFGSF_yaw::alignTilt()
     252             : {
     253             :     // Rotation matrix is constructed directly from acceleration measurement and will be the same for
     254             :     // all models so only need to calculate it once. Assumptions are:
     255             :     // 1) Yaw angle is zero - yaw is aligned later for each model when velocity fusion commences.
     256             :     // 2) The vehicle is not accelerating so all of the measured acceleration is due to gravity.
     257             : 
     258             :     // Calculate earth frame Down axis unit vector rotated into body frame
     259        3627 :     Vector3F down_in_bf = -delta_velocity;
     260        3627 :     down_in_bf.normalize();
     261             : 
     262             :     // Calculate earth frame North axis unit vector rotated into body frame, orthogonal to 'down_in_bf'
     263             :     // * operator is overloaded to provide a dot product
     264        3627 :     const Vector3F i_vec_bf(1.0f,0.0f,0.0f);
     265        3627 :     Vector3F north_in_bf = i_vec_bf - down_in_bf * (i_vec_bf * down_in_bf);
     266        3627 :     north_in_bf.normalize();
     267             : 
     268             :     // Calculate earth frame East axis unit vector rotated into body frame, orthogonal to 'down_in_bf' and 'north_in_bf'
     269             :     // % operator is overloaded to provide a cross product
     270        3627 :     const Vector3F east_in_bf = down_in_bf % north_in_bf;
     271             : 
     272             :     // Each column in a rotation matrix from earth frame to body frame represents the projection of the
     273             :     // corresponding earth frame unit vector rotated into the body frame, eg 'north_in_bf' would be the first column.
     274             :     // We need the rotation matrix from body frame to earth frame so the earth frame unit vectors rotated into body
     275             :     // frame are copied into corresponding rows instead to create the transpose.
     276        3627 :     Matrix3F R;
     277       14508 :     for (uint8_t col=0; col<3; col++) {
     278       10881 :         R[0][col] = north_in_bf[col];
     279       10881 :         R[1][col] = east_in_bf[col];
     280       10881 :         R[2][col] = down_in_bf[col];
     281             :     }
     282             : 
     283             :     // record alignment
     284       21762 :     for (uint8_t mdl_idx = 0; mdl_idx < N_MODELS_EKFGSF; mdl_idx++) {
     285       18135 :         AHRS[mdl_idx].R = R;
     286       18135 :         AHRS[mdl_idx].aligned = true;
     287             :     }
     288        3627 : }
     289             : 
     290        3657 : void EKFGSF_yaw::alignYaw()
     291             : {
     292             :     // Align yaw angle for each model
     293       21942 :     for (uint8_t mdl_idx = 0; mdl_idx < N_MODELS_EKFGSF; mdl_idx++) {
     294       18285 :         if (fabsF(AHRS[mdl_idx].R[2][0]) < fabsF(AHRS[mdl_idx].R[2][1])) {
     295             :             // get the roll, pitch, yaw estimates from the rotation matrix using a  321 Tait-Bryan rotation sequence
     296             :             ftype roll,pitch,yaw;
     297       10090 :             AHRS[mdl_idx].R.to_euler(&roll, &pitch, &yaw);
     298             : 
     299             :             // set the yaw angle
     300       10090 :             yaw = wrap_PI(EKF[mdl_idx].X[2]);
     301             : 
     302             :             // update the body to earth frame rotation matrix
     303       10090 :             AHRS[mdl_idx].R.from_euler(roll, pitch, yaw);
     304             : 
     305             :         } else {
     306             :             // Calculate the 312 Tait-Bryan rotation sequence that rotates from earth to body frame
     307        8195 :             Vector3F euler312 = AHRS[mdl_idx].R.to_euler312();
     308        8195 :             euler312[2] = wrap_PI(EKF[mdl_idx].X[2]); // first rotation (yaw) taken from EKF model state
     309             : 
     310             :             // update the body to earth frame rotation matrix
     311        8195 :             AHRS[mdl_idx].R.from_euler312(euler312[0], euler312[1], euler312[2]);
     312             : 
     313             :         }
     314             :     }
     315        3657 : }
     316             : 
     317             : // predict states and covariance for specified model index
     318   114886535 : void EKFGSF_yaw::predict(const uint8_t mdl_idx)
     319             : {
     320             :     // generate an attitude reference using IMU data
     321   114886535 :     predictAHRS(mdl_idx);
     322             : 
     323             :     // we don't start running the EKF part of the algorithm until there are regular velocity observations
     324   114886535 :     if (!vel_fuse_running) {
     325    34231590 :         return;
     326             :     }
     327             : 
     328             :     // Calculate the yaw state using a projection onto the horizontal that avoids gimbal lock
     329    80654945 :     if (fabsF(AHRS[mdl_idx].R[2][0]) < fabsF(AHRS[mdl_idx].R[2][1])) {
     330             :         // use 321 Tait-Bryan rotation to define yaw state
     331    37589525 :         EKF[mdl_idx].X[2] = atan2F(AHRS[mdl_idx].R[1][0], AHRS[mdl_idx].R[0][0]);
     332             :     } else {
     333             :         // use 312 Tait-Bryan rotation to define yaw state
     334    43065420 :         EKF[mdl_idx].X[2] = atan2F(-AHRS[mdl_idx].R[0][1], AHRS[mdl_idx].R[1][1]); // first rotation (yaw)
     335             :     }
     336             : 
     337             :     // calculate delta velocity in a horizontal front-right frame
     338    80654945 :     const Vector3F del_vel_NED = AHRS[mdl_idx].R * delta_velocity;
     339    80654945 :     const ftype dvx =   del_vel_NED[0] * cosF(EKF[mdl_idx].X[2]) + del_vel_NED[1] * sinF(EKF[mdl_idx].X[2]);
     340    80654945 :     const ftype dvy = - del_vel_NED[0] * sinF(EKF[mdl_idx].X[2]) + del_vel_NED[1] * cosF(EKF[mdl_idx].X[2]);
     341             : 
     342             :     // sum delta velocities in earth frame:
     343    80654945 :     EKF[mdl_idx].X[0] += del_vel_NED[0];
     344    80654945 :     EKF[mdl_idx].X[1] += del_vel_NED[1];
     345             : 
     346             :     // predict covariance - autocode from https://github.com/priseborough/3_state_filter/blob/flightLogReplay-wip/calcPupdate.txt
     347             : 
     348             :     // Local short variable name copies required for readability
     349             :     // Compiler might be smart enough to optimise these out
     350    80654945 :     const ftype P00 = EKF[mdl_idx].P[0][0];
     351    80654945 :     const ftype P01 = EKF[mdl_idx].P[0][1];
     352    80654945 :     const ftype P02 = EKF[mdl_idx].P[0][2];
     353    80654945 :     const ftype P10 = EKF[mdl_idx].P[1][0];
     354    80654945 :     const ftype P11 = EKF[mdl_idx].P[1][1];
     355    80654945 :     const ftype P12 = EKF[mdl_idx].P[1][2];
     356    80654945 :     const ftype P20 = EKF[mdl_idx].P[2][0];
     357    80654945 :     const ftype P21 = EKF[mdl_idx].P[2][1];
     358    80654945 :     const ftype P22 = EKF[mdl_idx].P[2][2];
     359             : 
     360             :     // Use fixed values for delta velocity and delta angle process noise variances
     361    80654945 :     const ftype dvxVar = sq(EKFGSF_accelNoise * velocity_dt); // variance of forward delta velocity - (m/s)^2
     362             :     const ftype dvyVar = dvxVar; // variance of right delta velocity - (m/s)^2
     363    80654945 :     const ftype dazVar = sq(EKFGSF_gyroNoise * angle_dt); // variance of yaw delta angle - rad^2
     364             : 
     365             :     const ftype t2 = sinF(EKF[mdl_idx].X[2]);
     366             :     const ftype t3 = cosF(EKF[mdl_idx].X[2]);
     367    80654945 :     const ftype t4 = dvy*t3;
     368    80654945 :     const ftype t5 = dvx*t2;
     369    80654945 :     const ftype t6 = t4+t5;
     370    80654945 :     const ftype t8 = P22*t6;
     371    80654945 :     const ftype t7 = P02-t8;
     372    80654945 :     const ftype t9 = dvx*t3;
     373    80654945 :     const ftype t11 = dvy*t2;
     374    80654945 :     const ftype t10 = t9-t11;
     375    80654945 :     const ftype t12 = dvxVar*t2*t3;
     376    80654945 :     const ftype t13 = t2*t2;
     377    80654945 :     const ftype t14 = t3*t3;
     378    80654945 :     const ftype t15 = P22*t10;
     379    80654945 :     const ftype t16 = P12+t15;
     380             : 
     381             :     const ftype min_var = 1e-6f;
     382    80654945 :     EKF[mdl_idx].P[0][0] = fmaxF(P00-P20*t6+dvxVar*t14+dvyVar*t13-t6*t7, min_var);
     383    80654945 :     EKF[mdl_idx].P[0][1] = P01+t12-P21*t6+t7*t10-dvyVar*t2*t3;
     384    80654945 :     EKF[mdl_idx].P[0][2] = t7;
     385    80654945 :     EKF[mdl_idx].P[1][0] = P10+t12+P20*t10-t6*t16-dvyVar*t2*t3;
     386    80654945 :     EKF[mdl_idx].P[1][1] = fmaxF(P11+P21*t10+dvxVar*t13+dvyVar*t14+t10*t16, min_var);
     387    80654945 :     EKF[mdl_idx].P[1][2] = t16;
     388    80654945 :     EKF[mdl_idx].P[2][0] = P20-t8;
     389    80654945 :     EKF[mdl_idx].P[2][1] = P21+t15;
     390    80654945 :     EKF[mdl_idx].P[2][2] = fmaxF(P22+dazVar, min_var);
     391             : 
     392             :     // force symmetry
     393    80654945 :     forceSymmetry(mdl_idx);
     394             : }
     395             : 
     396             : // Update EKF states and covariance for specified model index using velocity measurement
     397             : // Returns false if the state and covariance correction failed
     398     6145150 : bool EKFGSF_yaw::correct(const uint8_t mdl_idx, const Vector2F &vel, const ftype velObsVar)
     399             : {
     400             :     // calculate velocity observation innovations
     401     6145150 :     EKF[mdl_idx].innov[0] = EKF[mdl_idx].X[0] - vel[0];
     402     6145150 :     EKF[mdl_idx].innov[1] = EKF[mdl_idx].X[1] - vel[1];
     403             : 
     404             :     // copy covariance matrix to temporary variables
     405     6145150 :     const ftype P00 = EKF[mdl_idx].P[0][0];
     406     6145150 :     const ftype P01 = EKF[mdl_idx].P[0][1];
     407     6145150 :     const ftype P02 = EKF[mdl_idx].P[0][2];
     408     6145150 :     const ftype P10 = EKF[mdl_idx].P[1][0];
     409     6145150 :     const ftype P11 = EKF[mdl_idx].P[1][1];
     410     6145150 :     const ftype P12 = EKF[mdl_idx].P[1][2];
     411     6145150 :     const ftype P20 = EKF[mdl_idx].P[2][0];
     412     6145150 :     const ftype P21 = EKF[mdl_idx].P[2][1];
     413     6145150 :     const ftype P22 = EKF[mdl_idx].P[2][2];
     414             : 
     415             :     // calculate innovation variance
     416     6145150 :     EKF[mdl_idx].S[0][0] = P00 + velObsVar;
     417     6145150 :     EKF[mdl_idx].S[1][1] = P11 + velObsVar;
     418     6145150 :     EKF[mdl_idx].S[0][1] = P01;
     419     6145150 :     EKF[mdl_idx].S[1][0] = P10;
     420             : 
     421             :     // Perform a chi-square innovation consistency test and calculate a compression scale factor that limits the magnitude of innovations to 5-sigma
     422     6145150 :     ftype S_det_inv = (EKF[mdl_idx].S[0][0]*EKF[mdl_idx].S[1][1] - EKF[mdl_idx].S[0][1]*EKF[mdl_idx].S[1][0]);
     423             :     ftype innov_comp_scale_factor = 1.0f;
     424     6145150 :     if (fabsF(S_det_inv) > 1E-6f) {
     425             :         // Calculate elements for innovation covariance inverse matrix assuming symmetry
     426     6145150 :         S_det_inv = 1.0f / S_det_inv;
     427     6145150 :         const ftype S_inv_NN = EKF[mdl_idx].S[1][1] * S_det_inv;
     428     6145150 :         const ftype S_inv_EE = EKF[mdl_idx].S[0][0] * S_det_inv;
     429     6145150 :         const ftype S_inv_NE = EKF[mdl_idx].S[0][1] * S_det_inv;
     430             : 
     431             :         // The following expression was derived symbolically from test ratio = transpose(innovation) * inverse(innovation variance) * innovation = [1x2] * [2,2] * [2,1] = [1,1]
     432     6145150 :         const ftype test_ratio = EKF[mdl_idx].innov[0]*(EKF[mdl_idx].innov[0]*S_inv_NN + EKF[mdl_idx].innov[1]*S_inv_NE) + EKF[mdl_idx].innov[1]*(EKF[mdl_idx].innov[0]*S_inv_NE + EKF[mdl_idx].innov[1]*S_inv_EE);
     433             : 
     434             :         // If the test ratio is greater than 25 (5 Sigma) then reduce the length of the innovation vector to clip it at 5-Sigma
     435             :         // This protects from large measurement spikes
     436     6145150 :         if (test_ratio > 25.0f) {
     437       87799 :             innov_comp_scale_factor = sqrtF(25.0f / test_ratio);
     438             :         }
     439             :     } else {
     440             :         // skip this fusion step because calculation is badly conditioned
     441             :         return false;
     442             :     }
     443             : 
     444             :     // calculate Kalman gain K  and covariance matrix P
     445             :     // autocode from https://github.com/priseborough/3_state_filter/blob/flightLogReplay-wip/calcK.txt
     446             :     // and https://github.com/priseborough/3_state_filter/blob/flightLogReplay-wip/calcPmat.txt
     447     6145150 :     const ftype t2 = P00*velObsVar;
     448     6145150 :     const ftype t3 = P11*velObsVar;
     449     6145150 :     const ftype t4 = velObsVar*velObsVar;
     450     6145150 :     const ftype t5 = P00*P11;
     451             :     const ftype t9 = P01*P10;
     452     6145150 :     const ftype t6 = t2+t3+t4+t5-t9;
     453             :     ftype t7;
     454     6145150 :     if (fabsF(t6) > 1e-6f) {
     455     6145150 :         t7 = 1.0f/t6;
     456             :     } else {
     457             :         // skip this fusion step
     458             :         return false;
     459             :     }
     460             :     const ftype t8 = P11+velObsVar;
     461             :     const ftype t10 = P00+velObsVar;
     462             :     ftype K[3][2];
     463             : 
     464     6145150 :     K[0][0] = -P01*P10*t7+P00*t7*t8;
     465     6145150 :     K[0][1] = -P00*P01*t7+P01*t7*t10;
     466     6145150 :     K[1][0] = -P10*P11*t7+P10*t7*t8;
     467     6145150 :     K[1][1] = -P01*P10*t7+P11*t7*t10;
     468     6145150 :     K[2][0] = -P10*P21*t7+P20*t7*t8;
     469     6145150 :     K[2][1] = -P01*P20*t7+P21*t7*t10;
     470             : 
     471     6145150 :     const ftype t11 = P00*P01*t7;
     472             :     const ftype t15 = P01*t7*t10;
     473     6145150 :     const ftype t12 = t11-t15;
     474             :     const ftype t13 = P01*P10*t7;
     475             :     const ftype t16 = P00*t7*t8;
     476     6145150 :     const ftype t14 = t13-t16;
     477     6145150 :     const ftype t17 = t8*t12;
     478     6145150 :     const ftype t18 = P01*t14;
     479     6145150 :     const ftype t19 = t17+t18;
     480     6145150 :     const ftype t20 = t10*t14;
     481     6145150 :     const ftype t21 = P10*t12;
     482     6145150 :     const ftype t22 = t20+t21;
     483             :     const ftype t27 = P11*t7*t10;
     484     6145150 :     const ftype t23 = t13-t27;
     485     6145150 :     const ftype t24 = P10*P11*t7;
     486             :     const ftype t26 = P10*t7*t8;
     487     6145150 :     const ftype t25 = t24-t26;
     488     6145150 :     const ftype t28 = t8*t23;
     489     6145150 :     const ftype t29 = P01*t25;
     490     6145150 :     const ftype t30 = t28+t29;
     491     6145150 :     const ftype t31 = t10*t25;
     492     6145150 :     const ftype t32 = P10*t23;
     493     6145150 :     const ftype t33 = t31+t32;
     494     6145150 :     const ftype t34 = P01*P20*t7;
     495             :     const ftype t38 = P21*t7*t10;
     496     6145150 :     const ftype t35 = t34-t38;
     497     6145150 :     const ftype t36 = P10*P21*t7;
     498             :     const ftype t39 = P20*t7*t8;
     499     6145150 :     const ftype t37 = t36-t39;
     500     6145150 :     const ftype t40 = t8*t35;
     501     6145150 :     const ftype t41 = P01*t37;
     502     6145150 :     const ftype t42 = t40+t41;
     503     6145150 :     const ftype t43 = t10*t37;
     504     6145150 :     const ftype t44 = P10*t35;
     505     6145150 :     const ftype t45 = t43+t44;
     506             : 
     507             :     const ftype min_var = 1e-6f;
     508     6145150 :     EKF[mdl_idx].P[0][0] = fmaxF(P00-t12*t19-t14*t22, min_var);
     509     6145150 :     EKF[mdl_idx].P[0][1] = P01-t19*t23-t22*t25;
     510     6145150 :     EKF[mdl_idx].P[0][2] = P02-t19*t35-t22*t37;
     511     6145150 :     EKF[mdl_idx].P[1][0] = P10-t12*t30-t14*t33;
     512     6145150 :     EKF[mdl_idx].P[1][1] = fmaxF(P11-t23*t30-t25*t33, min_var);
     513     6145150 :     EKF[mdl_idx].P[1][2] = P12-t30*t35-t33*t37;
     514     6145150 :     EKF[mdl_idx].P[2][0] = P20-t12*t42-t14*t45;
     515     6145150 :     EKF[mdl_idx].P[2][1] = P21-t23*t42-t25*t45;
     516     6145150 :     EKF[mdl_idx].P[2][2] = fmaxF(P22-t35*t42-t37*t45, min_var);
     517             : 
     518             :     // force symmetry
     519     6145150 :     forceSymmetry(mdl_idx);
     520             : 
     521             :     // Apply state corrections and capture change in yaw angle
     522     6145150 :     const ftype yaw_prev = EKF[mdl_idx].X[2];
     523    18435450 :     for (uint8_t obs_index = 0; obs_index < 2; obs_index++) {
     524             :         // apply the state corrections including the compression scale factor
     525    49161200 :         for (unsigned row = 0; row < 3; row++) {
     526    36870900 :             EKF[mdl_idx].X[row] -= K[row][obs_index] * EKF[mdl_idx].innov[obs_index] * innov_comp_scale_factor;
     527             :         }
     528             :     }
     529     6145150 :     const ftype yaw_delta = EKF[mdl_idx].X[2] - yaw_prev;
     530             : 
     531             :     // apply the change in yaw angle to the AHRS taking advantage of sparseness in the yaw rotation matrix
     532     6145150 :     const ftype cos_yaw = cosF(yaw_delta);
     533     6145150 :     const ftype sin_yaw = sinF(yaw_delta);
     534             :     ftype  R_prev[2][3];
     535     6145150 :     memcpy(&R_prev, &AHRS[mdl_idx].R, sizeof(R_prev)); // copy first two rows from 3x3
     536     6145150 :     AHRS[mdl_idx].R[0][0] = R_prev[0][0] * cos_yaw - R_prev[1][0] * sin_yaw;
     537     6145150 :     AHRS[mdl_idx].R[0][1] = R_prev[0][1] * cos_yaw - R_prev[1][1] * sin_yaw;
     538     6145150 :     AHRS[mdl_idx].R[0][2] = R_prev[0][2] * cos_yaw - R_prev[1][2] * sin_yaw;
     539     6145150 :     AHRS[mdl_idx].R[1][0] = R_prev[0][0] * sin_yaw + R_prev[1][0] * cos_yaw;
     540     6145150 :     AHRS[mdl_idx].R[1][1] = R_prev[0][1] * sin_yaw + R_prev[1][1] * cos_yaw;
     541     6145150 :     AHRS[mdl_idx].R[1][2] = R_prev[0][2] * sin_yaw + R_prev[1][2] * cos_yaw;
     542             : 
     543     6145150 :     return true;
     544             : }
     545             : 
     546        5409 : void EKFGSF_yaw::resetEKFGSF()
     547             : {
     548        5409 :     memset(&GSF, 0, sizeof(GSF));
     549        5409 :     vel_fuse_running = false;
     550        5409 :     run_ekf_gsf = false;
     551             : 
     552        5409 :     memset(&EKF, 0, sizeof(EKF));
     553             :     const ftype yaw_increment = M_2PI / (ftype)N_MODELS_EKFGSF;
     554       32454 :     for (uint8_t mdl_idx = 0; mdl_idx < N_MODELS_EKFGSF; mdl_idx++) {
     555             :         // evenly space initial yaw estimates in the region between +-Pi
     556       27045 :         EKF[mdl_idx].X[2] = -M_PI + (0.5f * yaw_increment) + ((ftype)mdl_idx * yaw_increment);
     557             : 
     558             :         // All filter models start with the same weight
     559       27045 :         GSF.weights[mdl_idx] = 1.0f / (ftype)N_MODELS_EKFGSF;
     560             : 
     561             :         // Use half yaw interval for yaw uncertainty as that is the maximum that the best model can be away from truth
     562       27045 :         GSF.yaw_variance = sq(0.5f * yaw_increment);
     563       27045 :         EKF[mdl_idx].P[2][2] = GSF.yaw_variance;
     564             :     }
     565        5409 : }
     566             : 
     567             : // returns the probability of a selected model output assuming a gaussian error distribution
     568     6145150 : ftype EKFGSF_yaw::gaussianDensity(const uint8_t mdl_idx) const
     569             : {
     570     6145150 :     const ftype t2 = EKF[mdl_idx].S[0][0] * EKF[mdl_idx].S[1][1];
     571     6145150 :     const ftype t5 = EKF[mdl_idx].S[0][1] * EKF[mdl_idx].S[1][0];
     572     6145150 :     const ftype t3 = t2 - t5; // determinant
     573     6145150 :     const ftype t4 = 1.0f / MAX(t3, 1e-12f); // determinant inverse
     574             : 
     575             :     // inv(S)
     576             :     ftype invMat[2][2];
     577     6145150 :     invMat[0][0] =   t4 * EKF[mdl_idx].S[1][1];
     578     6145150 :     invMat[1][1] =   t4 * EKF[mdl_idx].S[0][0];
     579     6145150 :     invMat[0][1] = - t4 * EKF[mdl_idx].S[0][1];
     580     6145150 :     invMat[1][0] = - t4 * EKF[mdl_idx].S[1][0];
     581             : 
     582             :     // inv(S) * innovation
     583             :     ftype tempVec[2];
     584     6145150 :     tempVec[0] = invMat[0][0] * EKF[mdl_idx].innov[0] + invMat[0][1] * EKF[mdl_idx].innov[1];
     585     6145150 :     tempVec[1] = invMat[1][0] * EKF[mdl_idx].innov[0] + invMat[1][1] * EKF[mdl_idx].innov[1];
     586             : 
     587             :     // transpose(innovation) * inv(S) * innovation
     588     6145150 :     ftype normDist = tempVec[0] * EKF[mdl_idx].innov[0] + tempVec[1] * EKF[mdl_idx].innov[1];
     589             : 
     590             :     // convert from a normalised variance to a probability assuming a Gaussian distribution
     591     6145150 :     normDist = expf(-0.5f * normDist);
     592     6145150 :     normDist *= sqrtF(t4)/ M_2PI;
     593     6145150 :     return normDist;
     594             : }
     595             : 
     596    86800095 : void EKFGSF_yaw::forceSymmetry(const uint8_t mdl_idx)
     597             : {
     598    86800095 :     ftype P01 = 0.5f * (EKF[mdl_idx].P[0][1] + EKF[mdl_idx].P[1][0]);
     599    86800095 :     ftype P02 = 0.5f * (EKF[mdl_idx].P[0][2] + EKF[mdl_idx].P[2][0]);
     600    86800095 :     ftype P12 = 0.5f * (EKF[mdl_idx].P[1][2] + EKF[mdl_idx].P[2][1]);
     601    86800095 :     EKF[mdl_idx].P[0][1] = EKF[mdl_idx].P[1][0] = P01;
     602    86800095 :     EKF[mdl_idx].P[0][2] = EKF[mdl_idx].P[2][0] = P02;
     603    86800095 :     EKF[mdl_idx].P[1][2] = EKF[mdl_idx].P[2][1] = P12;
     604    86800095 : }
     605             : 
     606             : // Apply a body frame delta angle to the body to earth frame rotation matrix using a small angle approximation
     607   114886535 : Matrix3F EKFGSF_yaw::updateRotMat(const Matrix3F &R, const Vector3F &g) const
     608             : {
     609   114886535 :     Matrix3F ret = R;
     610   114886535 :     ret[0][0] += R[0][1] * g[2] - R[0][2] * g[1];
     611   114886535 :     ret[0][1] += R[0][2] * g[0] - R[0][0] * g[2];
     612   114886535 :     ret[0][2] += R[0][0] * g[1] - R[0][1] * g[0];
     613   114886535 :     ret[1][0] += R[1][1] * g[2] - R[1][2] * g[1];
     614   114886535 :     ret[1][1] += R[1][2] * g[0] - R[1][0] * g[2];
     615   114886535 :     ret[1][2] += R[1][0] * g[1] - R[1][1] * g[0];
     616   114886535 :     ret[2][0] += R[2][1] * g[2] - R[2][2] * g[1];
     617   114886535 :     ret[2][1] += R[2][2] * g[0] - R[2][0] * g[2];
     618   114886535 :     ret[2][2] += R[2][0] * g[1] - R[2][1] * g[0];
     619             : 
     620             :     // Renormalise rows
     621             :     ftype rowLengthSq;
     622   459546140 :     for (uint8_t r = 0; r < 3; r++) {
     623   344659605 :         rowLengthSq = ret[r][0] * ret[r][0] + ret[r][1] * ret[r][1] + ret[r][2] * ret[r][2];
     624   344659605 :         if (is_positive(rowLengthSq)) {
     625             :             // Use linear approximation for inverse sqrt taking advantage of the row length being close to 1.0
     626   344659605 :             const ftype rowLengthInv = 1.5f - 0.5f * rowLengthSq;
     627             :             Vector3F &row = ret[r];
     628   344659605 :             row *= rowLengthInv;
     629             :         }
     630             :     }
     631             : 
     632   114886535 :     return ret;
     633             : }
     634             : 
     635             : // returns true if a yaw estimate is available.  yaw and its variance
     636             : // is returned, as well as the number of models which are *not* being
     637             : // used to snthesise the yaw.
     638     1012328 : bool EKFGSF_yaw::getYawData(ftype &yaw, ftype &yawVariance, uint8_t *_n_clips) const
     639             : {
     640     1012328 :     if (!vel_fuse_running) {
     641             :         return false;
     642             :     }
     643     1010038 :     yaw = GSF.yaw;
     644     1010038 :     yawVariance = GSF.yaw_variance;
     645     1010038 :     if (_n_clips != nullptr) {
     646         479 :         *_n_clips = n_clips;
     647             :     }
     648             :     return true;
     649             : }
     650             : 
     651      343950 : bool EKFGSF_yaw::getVelInnovLength(ftype &velInnovLength) const
     652             : {
     653      343950 :     if (!vel_fuse_running) {
     654             :         return false;
     655             :     }
     656      343950 :     velInnovLength = 0.0f;
     657     2063700 :     for (uint8_t mdl_idx = 0; mdl_idx < N_MODELS_EKFGSF; mdl_idx++) {
     658     1719750 :         velInnovLength += GSF.weights[mdl_idx] * sqrtF((sq(EKF[mdl_idx].innov[0]) + sq(EKF[mdl_idx].innov[1])));
     659             :     }
     660             :     return true;
     661             : }
     662             : 
     663        2545 : void EKFGSF_yaw::setGyroBias(Vector3f &gyroBias)
     664             : {
     665       15270 :     for (uint8_t mdl_idx = 0; mdl_idx < N_MODELS_EKFGSF; mdl_idx++) {
     666       12725 :         AHRS[mdl_idx].gyro_bias = gyroBias.toftype();
     667             :     }
     668        2545 : }

Generated by: LCOV version 1.14