LCOV - code coverage report
Current view: top level - AP_NavEKF3 - AP_NavEKF3_core.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 1336 1362 98.1 %
Date: 2026-09-09 11:02:08 Functions: 26 27 96.3 %

          Line data    Source code
       1             : #include <AP_HAL/AP_HAL.h>
       2             : 
       3             : #include "AP_NavEKF3.h"
       4             : #include "AP_NavEKF3_core.h"
       5             : #include <GCS_MAVLink/GCS.h>
       6             : #include <AP_VisualOdom/AP_VisualOdom.h>
       7             : #include <AP_Logger/AP_Logger.h>
       8             : #include <AP_DAL/AP_DAL.h>
       9             : 
      10             : // constructor
      11        3989 : NavEKF3_core::NavEKF3_core(NavEKF3 *_frontend, AP_DAL &_dal) :
      12        3989 :     dal(_dal),
      13        3989 :     frontend(_frontend),
      14        3989 :     public_origin(frontend->common_EKF_origin)
      15             : {
      16        3989 :     firstInitTime_ms = 0;
      17        3989 :     lastInitFailReport_ms = 0;
      18        3989 : }
      19             : 
      20             : // setup this core backend
      21       48273 : bool NavEKF3_core::setup_core(uint8_t _imu_index, uint8_t _core_index)
      22             : {
      23       48273 :     imu_index = _imu_index;
      24       48273 :     gyro_index_active = imu_index;
      25       48273 :     accel_index_active = imu_index;
      26       48273 :     core_index = _core_index;
      27             : 
      28             :     /*
      29             :       The imu_buffer_length needs to cope with the worst case sensor delay at the
      30             :       target EKF state prediction rate. Non-IMU data coming in faster is downsampled.
      31             :      */
      32             : 
      33             :     // Calculate the expected EKF time step
      34       48273 :     if (dal.ins().get_loop_rate_hz() > 0) {
      35       48273 :         dtEkfAvg = 1.0f / dal.ins().get_loop_rate_hz();
      36       48273 :         dtEkfAvg = MAX(dtEkfAvg,EKF_TARGET_DT);
      37             :     } else {
      38             :         return false;
      39             :     }
      40             : 
      41             :     // find the maximum time delay for all potential sensors
      42       48273 :     uint16_t maxTimeDelay_ms = MAX(frontend->_hgtDelay_ms ,
      43       48273 :             MAX(frontend->_flowDelay_ms ,
      44       48273 :                 MAX(frontend->_rngBcnDelay_ms ,
      45       48273 :                     MAX(frontend->magDelay_ms ,
      46       48273 :                         (uint16_t)(EKF_TARGET_DT_MS)
      47       48273 :                                   ))));
      48             : 
      49             :     // GPS sensing can have large delays and should not be included if disabled
      50       48273 :     if (frontend->sources.usingGPS(core_index)) {
      51             :         // Wait for the configuration of all GPS units to be confirmed. Until this has occurred the GPS driver cannot provide a correct time delay
      52       48231 :         float gps_delay_sec = 0;
      53       48231 :         if (!dal.gps().get_lag(selected_gps, gps_delay_sec)) {
      54             : #if HAL_GCS_ENABLED
      55       44318 :             const uint32_t now = dal.millis();
      56       44318 :             if (now - lastInitFailReport_ms > 10000) {
      57          16 :                 lastInitFailReport_ms = now;
      58             :                 // provide an escalating series of messages
      59             :                 MAV_SEVERITY severity = MAV_SEVERITY_INFO;
      60          16 :                 if (now > 30000) {
      61             :                     severity = MAV_SEVERITY_ERROR;
      62          16 :                 } else if (now > 15000) {
      63             :                     severity = MAV_SEVERITY_WARNING;
      64             :                 }
      65          16 :                 GCS_SEND_TEXT(severity, "EKF3 waiting for GPS config data");
      66             :             }
      67             : #endif
      68       44318 :             return false;
      69             :         }
      70             :         // limit the time delay value from the GPS library to a max of 250 msec which is the max value the EKF has been tested for.
      71        3913 :         maxTimeDelay_ms = MAX(maxTimeDelay_ms , MIN((uint16_t)(gps_delay_sec * 1000.0f),250));
      72             :     }
      73             : 
      74             :     // airspeed sensing can have large delays and should not be included if disabled
      75        3955 :     if (dal.airspeed_sensor_enabled()) {
      76         912 :         maxTimeDelay_ms = MAX(maxTimeDelay_ms , frontend->tasDelay_ms);
      77             :     }
      78             : 
      79             : #if HAL_VISUALODOM_ENABLED
      80             :     // include delay from visual odometry if enabled
      81        3955 :     const auto *visual_odom = dal.visualodom();
      82        3955 :     if ((visual_odom != nullptr) && visual_odom->enabled()) {
      83          20 :         maxTimeDelay_ms = MAX(maxTimeDelay_ms, MIN(visual_odom->get_delay_ms(), 250));
      84             :     }
      85             : #endif
      86             : 
      87             :     // calculate the IMU buffer length required to accommodate the maximum delay with some allowance for jitter
      88        3955 :     imu_buffer_length = (maxTimeDelay_ms / (uint16_t)(EKF_TARGET_DT_MS)) + 1;
      89             : 
      90             :     // set the observation buffer length to handle the minimum time of arrival between observations in combination
      91             :     // with the worst case delay from current time to ekf fusion time
      92             :     // allow for worst case 50% extension of the ekf fusion time horizon delay due to timing jitter
      93        3955 :     uint16_t ekf_delay_ms = maxTimeDelay_ms + (int)(ceilF((ftype)maxTimeDelay_ms * 0.5f));
      94        3955 :     obs_buffer_length = (ekf_delay_ms / frontend->sensorIntervalMin_ms) + 1;
      95             : 
      96             :     // limit to be no longer than the IMU buffer (we can't process data faster than the EKF prediction rate)
      97        3955 :     obs_buffer_length = MIN(obs_buffer_length,imu_buffer_length);
      98             : 
      99             :     // calculate buffer size for optical flow data
     100        3955 :     const uint8_t flow_buffer_length = MIN((ekf_delay_ms / frontend->flowIntervalMin_ms) + 1, imu_buffer_length);
     101             : 
     102             : #if EK3_FEATURE_EXTERNAL_NAV
     103             :     // calculate buffer size for external nav data
     104        3955 :     const uint8_t extnav_buffer_length = MIN((ekf_delay_ms / frontend->extNavIntervalMin_ms) + 1, imu_buffer_length);
     105             : #endif // EK3_FEATURE_EXTERNAL_NAV
     106             : 
     107        3955 :     if(!storedGPS.init(obs_buffer_length)) {
     108             :         return false;
     109             :     }
     110        3955 :     if(!storedMag.init(obs_buffer_length)) {
     111             :         return false;
     112             :     }
     113        3955 :     if(!storedBaro.init(obs_buffer_length)) {
     114             :         return false;
     115             :     }
     116        3955 :     if(dal.airspeed() && !storedTAS.init(obs_buffer_length)) {
     117             :         return false;
     118             :     }
     119        3955 :     if(dal.opticalflow_enabled() && !storedOF.init(flow_buffer_length)) {
     120             :         return false;
     121             :     }
     122             : #if EK3_FEATURE_BODY_ODOM
     123        3955 :     if(frontend->sources.ext_nav_enabled() && !storedBodyOdm.init(obs_buffer_length)) {
     124             :         return false;
     125             :     }
     126        3955 :     if(frontend->sources.wheel_encoder_enabled() && !storedWheelOdm.init(imu_buffer_length)) {
     127             :         // initialise to same length of IMU to allow for multiple wheel sensors
     128             :         return false;
     129             :     }
     130             : #endif // EK3_FEATURE_BODY_ODOM
     131        3955 :     if(frontend->sources.gps_yaw_enabled() && !storedYawAng.init(obs_buffer_length)) {
     132             :         return false;
     133             :     }
     134             : #if AP_RANGEFINDER_ENABLED
     135             :     // Note: the use of dual range finders potentially doubles the amount of data to be stored
     136        3955 :     if(dal.rangefinder() && !storedRange.init(MIN(2*obs_buffer_length , imu_buffer_length))) {
     137           0 :         return false;
     138             :     }
     139             : #endif
     140             :     // Note: range beacon data is read one beacon at a time and can arrive at a high rate
     141             : #if EK3_FEATURE_BEACON_FUSION
     142        3955 :     if(dal.beacon() && !rngBcn.storedRange.init(imu_buffer_length+1)) {
     143             :         return false;
     144             :     }
     145             : #endif
     146             : #if EK3_FEATURE_EXTERNAL_NAV
     147        3955 :     if (frontend->sources.ext_nav_enabled() && !storedExtNav.init(extnav_buffer_length)) {
     148             :         return false;
     149             :     }
     150        3955 :     if (frontend->sources.ext_nav_enabled() && !storedExtNavVel.init(extnav_buffer_length)) {
     151             :         return false;
     152             :     }
     153        3955 :     if(frontend->sources.ext_nav_enabled() && !storedExtNavYawAng.init(extnav_buffer_length)) {
     154             :         return false;
     155             :     }
     156             : #endif // EK3_FEATURE_EXTERNAL_NAV
     157        3955 :     if(!storedIMU.init(imu_buffer_length)) {
     158             :         return false;
     159             :     }
     160        3955 :     if(!storedOutput.init(imu_buffer_length)) {
     161             :         return false;
     162             :     }
     163             : #if EK3_FEATURE_DRAG_FUSION
     164        3955 :     if (!storedDrag.init(obs_buffer_length)) {
     165             :         return false;
     166             :     }
     167             : #endif
     168             :  
     169        3955 :     if ((yawEstimator == nullptr) && (frontend->_gsfRunMask & (1U<<core_index))) {
     170             :         // check if there is enough memory to create the EKF-GSF object
     171        3954 :         if (dal.available_memory() < sizeof(EKFGSF_yaw) + 1024) {
     172           0 :             GCS_SEND_TEXT(MAV_SEVERITY_CRITICAL, "EKF3 IMU%u GSF: not enough memory",(unsigned)imu_index);
     173           0 :             return false;
     174             :         }
     175             : 
     176             :         // try to instantiate
     177        3954 :         yawEstimator = NEW_NOTHROW EKFGSF_yaw();
     178        3954 :         if (yawEstimator == nullptr) {
     179           0 :             GCS_SEND_TEXT(MAV_SEVERITY_CRITICAL, "EKF3 IMU%uGSF: allocation failed",(unsigned)imu_index);
     180           0 :             return false;
     181             :         }
     182             :     }
     183             : 
     184             :     return true;
     185             : }
     186             :     
     187             : 
     188             : /********************************************************
     189             : *                   INIT FUNCTIONS                      *
     190             : ********************************************************/
     191             : 
     192             : // Use a function call rather than a constructor to initialise variables because it enables the filter to be re-started in flight if necessary.
     193        3144 : void NavEKF3_core::InitialiseVariables()
     194             : {
     195             :     // calculate the nominal filter update rate
     196        3144 :     const auto &ins = dal.ins();
     197        3144 :     localFilterTimeStep_ms = (uint8_t)(1000*ins.get_loop_delta_t());
     198        3144 :     localFilterTimeStep_ms = MAX(localFilterTimeStep_ms, (uint8_t)EKF_TARGET_DT_MS);
     199             : 
     200             :     // initialise time stamps
     201        3144 :     imuSampleTime_ms = frontend->imuSampleTime_us / 1000;
     202        3144 :     prevTasStep_ms = imuSampleTime_ms;
     203        3144 :     prevBetaDragStep_ms = imuSampleTime_ms;
     204        3144 :     lastBaroReceived_ms = imuSampleTime_ms;
     205        3144 :     lastVelPassTime_ms = 0;
     206        3144 :     lastGpsPosPassTime_ms = 0;
     207        3144 :     lastHgtPassTime_ms = 0;
     208        3144 :     lastTasPassTime_ms = 0;
     209        3144 :     lastSynthYawTime_ms = 0;
     210        3144 :     lastTimeGpsReceived_ms = 0;
     211        3144 :     timeAtLastAuxEKF_ms = imuSampleTime_ms;
     212        3144 :     flowValidMeaTime_ms = imuSampleTime_ms;
     213        3144 :     rngValidMeaTime_ms = imuSampleTime_ms;
     214        3144 :     flowMeaTime_ms = 0;
     215        3144 :     prevFlowFuseTime_ms = 0;
     216        3144 :     gndHgtValidTime_ms = 0;
     217        3144 :     ekfStartTime_ms = imuSampleTime_ms;
     218        3144 :     lastGpsVelFail_ms = 0;
     219        3144 :     lastGpsAidBadTime_ms = 0;
     220        3144 :     timeTasReceived_ms = 0;
     221        3144 :     lastPreAlignGpsCheckTime_ms = imuSampleTime_ms;
     222        3144 :     posNEResetCount = 0;
     223        3144 :     posDResetCount = 0;
     224        3144 :     lastRngMeasTime_ms = 0;
     225             : 
     226             :     // initialise other variables
     227        3144 :     memset(&dvelBiasAxisInhibit, 0, sizeof(dvelBiasAxisInhibit));
     228        3144 :         dvelBiasAxisVarPrev.zero();
     229        3144 :     gpsNoiseScaler = 1.0f;
     230        3144 :     hgtTimeout = true;
     231        3144 :     tasTimeout = true;
     232        3144 :     dragTimeout = true;
     233        3144 :     badIMUdata = false;
     234        3144 :     badIMUdata_ms = 0;
     235        3144 :     goodIMUdata_ms = 0;
     236        3144 :     vertVelVarClipCounter = 0;
     237        3144 :     finalInflightYawInit = false;
     238        3144 :     dtIMUavg = ins.get_loop_delta_t();
     239        3144 :     dtEkfAvg = EKF_TARGET_DT;
     240        3144 :     dt = 0;
     241        3144 :     velDotNEDfilt.zero();
     242        3144 :     lastKnownPositionNE.zero();
     243        3144 :     lastKnownPositionD = 0;
     244        3144 :     prevTnb.zero();
     245        3144 :     memset(&P[0][0], 0, sizeof(P));
     246        3144 :     memset(&KHP[0][0], 0, sizeof(KHP));
     247        3144 :     flowDataValid = false;
     248        3144 :     rangeDataToFuse  = false;
     249             : #if EK3_FEATURE_OPTFLOW_FUSION
     250        3144 :     Popt = 0.0f;
     251             : #endif
     252        3144 :     terrainState = 0.0f;
     253        3144 :     prevPosN = stateStruct.position.x;
     254        3144 :     prevPosE = stateStruct.position.y;
     255        3144 :     inhibitGndState = false;
     256        3144 :     flowGyroBias.x = 0;
     257        3144 :     flowGyroBias.y = 0;
     258        3144 :     PV_AidingMode = AID_NONE;
     259        3144 :     PV_AidingModePrev = AID_NONE;
     260        3144 :     posTimeout = true;
     261        3144 :     velTimeout = true;
     262        3144 :     velAiding = false;
     263        3144 :     waitingForGpsChecks = false;
     264        3144 :     memset(&faultStatus, 0, sizeof(faultStatus));
     265        3144 :     hgtRate = 0.0f;
     266        3144 :     onGround = true;
     267        3144 :     prevOnGround = true;
     268        3144 :     inFlight = false;
     269        3144 :     prevInFlight = false;
     270        3144 :     manoeuvring = false;
     271        3144 :     fusingStationaryZeroVel = false;
     272        3144 :     inhibitWindStates = true;
     273        3144 :     windStateIsObservable = false;
     274        3144 :     treatWindStatesAsTruth = false;
     275        3144 :     lastAspdEstIsValid = false;
     276        3144 :     windStatesAligned = false;
     277        3144 :     inhibitDelVelBiasStates = true;
     278        3144 :     inhibitDelAngBiasStates = true;
     279        3144 :     gndOffsetValid =  false;
     280        3144 :     validOrigin = false;
     281        3144 :     gpsSpdAccuracy = 0.0f;
     282        3144 :     gpsPosAccuracy = 0.0f;
     283        3144 :     gpsHgtAccuracy = 0.0f;
     284        3144 :     baroHgtOffset = 0.0f;
     285        3144 :     rngOnGnd = 0.05f;
     286             : #if EK3_FEATURE_OPTFLOW_AGL_KF
     287             :     // 2-state AGL KF initialisation
     288             :     // Start with generous uncertainty; the first valid RF measurement will hard-reset the state
     289        3144 :     aglKfH = rngOnGnd;      // assume sitting on ground at minimum range
     290        3144 :     aglKfV = 0.0f;
     291        3144 :     aglKfP[0][0] = 25.0f;   // 5 m initial std-dev in height
     292        3144 :     aglKfP[0][1] = 0.0f;
     293        3144 :     aglKfP[1][0] = 0.0f;
     294        3144 :     aglKfP[1][1] = 1.0f;    // 1 m/s initial std-dev in velocity
     295        3144 :     aglKfValid = false;
     296        3144 :     lastAglRngFuseTime_ms = 0;
     297             : #endif
     298        3144 :     yawResetCount = 0;
     299        3144 :     tiltErrorVariance = sq(M_2PI);
     300        3144 :     tiltAlignComplete = false;
     301        3144 :     yawAlignComplete = false;
     302        3144 :     yawAlignGpsValidCount = 0;
     303        3144 :     have_table_earth_field = false;
     304        3144 :     stateIndexLim = 23;
     305        3144 :     last_gps_idx = 0;
     306        3144 :     delAngCorrection.zero();
     307        3144 :     velErrintegral.zero();
     308        3144 :     posErrintegral.zero();
     309        3144 :     gpsGoodToAlign = false;
     310        3144 :     gpsIsInUse = false;
     311        3144 :     motorsArmed = false;
     312        3144 :     prevMotorsArmed = false;
     313        3144 :     memset(&gpsCheckStatus, 0, sizeof(gpsCheckStatus));
     314        3144 :     gpsSpdAccPass = false;
     315        3144 :     ekfInnovationsPass = false;
     316        3144 :     sAccFilterState1 = 0.0f;
     317        3144 :     sAccFilterState2 = 0.0f;
     318        3144 :     lastGpsCheckTime_ms = 0;
     319        3144 :     lastGpsInnovPassTime_ms = 0;
     320        3144 :     lastGpsInnovFailTime_ms = 0;
     321        3144 :     lastGpsVertAccPassTime_ms = 0;
     322        3144 :     lastGpsVertAccFailTime_ms = 0;
     323        3144 :     gpsAccuracyGood = false;
     324        3144 :     gpsAccuracyGoodForAltitude = false;
     325        3144 :     gpsloc_prev = {};
     326        3144 :     gpsDriftNE = 0.0f;
     327        3144 :     gpsVertVelFilt = 0.0f;
     328        3144 :     gpsHorizVelFilt = 0.0f;
     329        3144 :     ZERO_FARRAY(statesArray);
     330        3144 :     memset(&vertCompFiltState, 0, sizeof(vertCompFiltState));
     331        3144 :     posVelFusionDelayed = false;
     332             : #if EK3_FEATURE_OPTFLOW_FUSION
     333        3144 :     optFlowFusionDelayed = false;
     334             : #endif
     335        3144 :     flowFusionActive = false;
     336        3144 :     airSpdFusionDelayed = false;
     337        3144 :     sideSlipFusionDelayed = false;
     338        3144 :     airDataFusionWindOnly = false;
     339        3144 :     posResetNE.zero();
     340        3144 :     posResetD = 0.0f;
     341        3144 :     hgtInnovFiltState = 0.0f;
     342        3144 :     imuDataDownSampledNew.delAng.zero();
     343        3144 :     imuDataDownSampledNew.delVel.zero();
     344        3144 :     imuDataDownSampledNew.delAngDT = 0.0f;
     345        3144 :     imuDataDownSampledNew.delVelDT = 0.0f;
     346        3144 :     imuDataDownSampledNew.gyro_index = gyro_index_active;
     347        3144 :     imuDataDownSampledNew.accel_index = accel_index_active;
     348        3144 :     runUpdates = false;
     349        3144 :     framesSincePredict = 0;
     350        3144 :     gpsYawResetRequest = false;
     351        3144 :     delAngBiasLearned = false;
     352        3144 :     memset(&filterStatus, 0, sizeof(filterStatus));
     353        3144 :     activeHgtSource = AP_NavEKF_Source::SourceZ::BARO;
     354        3144 :     prevHgtSource = activeHgtSource;
     355             : #if EK3_FEATURE_RANGEFINDER_MEASUREMENTS
     356        3144 :     memset(&rngMeasIndex, 0, sizeof(rngMeasIndex));
     357        3144 :     memset(&storedRngMeasTime_ms, 0, sizeof(storedRngMeasTime_ms));
     358        3144 :     memset(&storedRngMeas, 0, sizeof(storedRngMeas));
     359             : #endif
     360        3144 :     terrainHgtStable = true;
     361        3144 :     ekfOriginHgtVar = 0.0f;
     362        3144 :     ekfGpsRefHgt = 0.0;
     363        3144 :     velOffsetNED.zero();
     364        3144 :     posOffsetNED.zero();
     365        3144 :     ZERO_FARRAY(velPosObs);
     366             : 
     367             :     // range beacon fusion variables
     368             : #if EK3_FEATURE_BEACON_FUSION
     369        3144 :     rngBcn.InitialiseVariables();
     370             : #endif  // EK3_FEATURE_BEACON_FUSION
     371             : 
     372             : #if EK3_FEATURE_BODY_ODOM
     373             :     // body frame displacement fusion
     374        3144 :     memset((void *)&bodyOdmDataNew, 0, sizeof(bodyOdmDataNew));
     375        3144 :     memset((void *)&bodyOdmDataDelayed, 0, sizeof(bodyOdmDataDelayed));
     376             : #endif
     377        3144 :     lastbodyVelPassTime_ms = 0;
     378        3144 :     ZERO_FARRAY(bodyVelTestRatio);
     379        3144 :     ZERO_FARRAY(varInnovBodyVel);
     380        3144 :     ZERO_FARRAY(innovBodyVel);
     381        3144 :     prevBodyVelFuseTime_ms = 0;
     382        3144 :     bodyOdmMeasTime_ms = 0;
     383        3144 :     bodyVelFusionDelayed = false;
     384        3144 :     bodyVelFusionActive = false;
     385             : 
     386             :     // yaw sensor fusion
     387        3144 :     yawMeasTime_ms = 0;
     388        3144 :     memset((void *)&yawAngDataNew, 0, sizeof(yawAngDataNew));
     389        3144 :     memset((void *)&yawAngDataDelayed, 0, sizeof(yawAngDataDelayed));
     390             : 
     391             : #if EK3_FEATURE_EXTERNAL_NAV
     392             :     // external nav data fusion
     393        3144 :     extNavDataDelayed = {};
     394        3144 :     extNavMeasTime_ms = 0;
     395        3144 :     extNavLastPosResetTime_ms = 0;
     396        3144 :     extNavDataToFuse = false;
     397        3144 :     extNavUsedForPos = false;
     398        3144 :     extNavVelDelayed = {};
     399        3144 :     extNavVelToFuse = false;
     400        3144 :     useExtNavVel = false;
     401        3144 :     extNavVelMeasTime_ms = 0;
     402             : #endif
     403             : 
     404             :     // zero data buffers
     405        3144 :     storedIMU.reset();
     406        3144 :     storedGPS.reset();
     407        3144 :     storedBaro.reset();
     408        3144 :     storedTAS.reset();
     409             : #if EK3_FEATURE_RANGEFINDER_MEASUREMENTS
     410        3144 :     storedRange.reset();
     411             : #endif
     412        3144 :     storedOutput.reset();
     413             : #if EK3_FEATURE_BEACON_FUSION
     414        3144 :     rngBcn.storedRange.reset();
     415             : #endif
     416             : #if EK3_FEATURE_BODY_ODOM
     417        3144 :     storedBodyOdm.reset();
     418        3144 :     storedWheelOdm.reset();
     419             : #endif
     420             : #if EK3_FEATURE_EXTERNAL_NAV
     421        3144 :     storedExtNav.reset();
     422        3144 :     storedExtNavVel.reset();
     423             : #endif
     424             : 
     425             :     // initialise pre-arm message
     426        3144 :     dal.snprintf(prearm_fail_string, sizeof(prearm_fail_string), "EKF3 still initialising");
     427             : 
     428        3144 :     InitialiseVariablesMag();
     429             : 
     430             :     // emergency reset of yaw to EKFGSF estimate
     431        3144 :     EKFGSF_yaw_reset_ms = 0;
     432        3144 :     EKFGSF_yaw_reset_request_ms = 0;
     433        3144 :     EKFGSF_yaw_reset_count = 0;
     434        3144 :     EKFGSF_run_filterbank = false;
     435        3144 :     EKFGSF_yaw_valid_count = 0;
     436             : 
     437        3144 :     effectiveMagCal = effective_magCal();
     438        3144 : }
     439             : 
     440             : // Use a function call rather than a constructor to initialise variables because it enables the filter to be re-started in flight if necessary.
     441        3144 : void NavEKF3_core::InitialiseVariablesMag()
     442             : {
     443        3144 :     lastHealthyMagTime_ms = imuSampleTime_ms;
     444        3144 :     lastMagUpdate_us = 0;
     445        3144 :     magYawResetTimer_ms = imuSampleTime_ms;
     446        3144 :     magTimeout = false;
     447        3144 :     allMagSensorsFailed = false;
     448        3144 :     finalInflightMagInit = false;
     449        3144 :     inhibitMagStates = true;
     450        3144 :     magSelectIndex = dal.compass().get_first_usable();
     451        3144 :     lastMagOffsetsValid = false;
     452        3144 :     magStateResetRequest = false;
     453        3144 :     magStateInitComplete = false;
     454        3144 :     magYawResetRequest = false;
     455        3144 :     posDownAtLastMagReset = stateStruct.position.z;
     456        3144 :     yawInnovAtLastMagReset = 0.0f;
     457        3144 :     stateStruct.quat.initialise();
     458        3144 :     quatAtLastMagReset = stateStruct.quat;
     459        3144 :     magFieldLearned = false;
     460        3144 :     storedMag.reset();
     461        3144 :     storedYawAng.reset();
     462             : #if EK3_FEATURE_EXTERNAL_NAV
     463        3144 :     storedExtNavYawAng.reset();
     464             : #endif
     465        3144 :     needMagBodyVarReset = false;
     466        3144 :     needEarthBodyVarReset = false;
     467        3144 :     magFusionSel = MagFuseSel::NOT_FUSING;
     468        3144 : }
     469             : 
     470             : /*
     471             : Initialise the states from accelerometer data. This assumes measured acceleration 
     472             : is dominated by gravity. If this assumption is not true then the EKF will require
     473             : timee to reduce the resulting tilt error. Yaw alignment is not performed by this
     474             : function, but is perfomred later and initiated the SelectMagFusion() function
     475             : after the tilt has stabilised.
     476             : */
     477             : 
     478     1466953 : bool NavEKF3_core::InitialiseFilterBootstrap(void)
     479             : {
     480             :     // update sensor selection (for affinity)
     481     1466953 :     update_sensor_selection();
     482             : 
     483             :     // If we are a plane and don't have GPS lock then don't initialise
     484     1466953 :     if (assume_zero_sideslip() && dal.gps().status(preferred_gps) < AP_GPS_FixType::FIX_3D) {
     485      561320 :         dal.snprintf(prearm_fail_string,
     486             :                      sizeof(prearm_fail_string),
     487             :                      "EKF3 init failure: No GPS lock");
     488      561320 :         statesInitialised = false;
     489      561320 :         return false;
     490             :     }
     491             : 
     492             :     // read all the sensors required to start the EKF the states
     493      905633 :     readIMUData(false);  // don't allow prediction
     494      905633 :     readMagData();
     495      905633 :     readGpsData();
     496      905633 :     readGpsYawData();
     497      905633 :     readBaroData();
     498             : 
     499      905633 :     if (statesInitialised) {
     500             :         // we are initialised, but we don't return true until the IMU
     501             :         // buffer has been filled. This prevents a timing
     502             :         // vulnerability with a pause in IMU data during filter startup
     503        3191 :         return storedIMU.is_filled();
     504             :     }
     505             : 
     506             :     // accumulate enough sensor data to fill the buffers
     507      902442 :     if (firstInitTime_ms == 0) {
     508        3329 :         firstInitTime_ms = imuSampleTime_ms;
     509        3329 :         return false;
     510      899113 :     } else if (imuSampleTime_ms - firstInitTime_ms < 1000) {
     511             :         return false;
     512             :     }
     513             : 
     514             :     // set re-used variables to zero
     515        3144 :     InitialiseVariables();
     516             : 
     517             :     // acceleration vector in XYZ body axes measured by the IMU (m/s^2)
     518        3144 :     Vector3F initAccVec;
     519             : 
     520             :     // TODO we should average accel readings over several cycles
     521        3144 :     initAccVec = dal.ins().get_accel(accel_index_active).toftype();
     522             : 
     523             :     // normalise the acceleration vector
     524             :     ftype pitch=0, roll=0;
     525        3144 :     if (initAccVec.length() > 0.001f) {
     526        3144 :         initAccVec.normalize();
     527             : 
     528             :         // calculate initial pitch angle
     529        3144 :         pitch = asinF(initAccVec.x);
     530             : 
     531             :         // calculate initial roll angle
     532        3144 :         roll = atan2F(-initAccVec.y , -initAccVec.z);
     533             :     }
     534             : 
     535             :     // calculate initial roll and pitch orientation
     536        3144 :     stateStruct.quat.from_euler(roll, pitch, 0.0f);
     537             : 
     538             :     // initialise dynamic states
     539        3144 :     stateStruct.velocity.zero();
     540        3144 :     stateStruct.position.zero();
     541             : 
     542             :     // initialise static process model states
     543        3144 :     stateStruct.gyro_bias.zero();
     544        3144 :     stateStruct.accel_bias.zero();
     545        3144 :     stateStruct.wind_vel.zero();
     546        3144 :     stateStruct.earth_magfield.zero();
     547        3144 :     stateStruct.body_magfield.zero();
     548             : 
     549             :     // set the position, velocity and height
     550        3144 :     ResetVelocity(resetDataSource::DEFAULT);
     551        3144 :     ResetPosition(resetDataSource::DEFAULT);
     552        3144 :     ResetHeight();
     553             : 
     554             :     // initialise sources
     555        3144 :     posxy_source_last = frontend->sources.getPosXYSource(core_index);
     556        3144 :     yaw_source_last = frontend->sources.getYawSource(core_index);
     557             : 
     558             :     // define Earth rotation vector in the NED navigation frame
     559        3144 :     calcEarthRateNED(earthRateNED, dal.get_home().lat);
     560             : 
     561             :     // initialise the covariance matrix
     562        3144 :     CovarianceInit();
     563             : 
     564             :     // reset the output predictor states
     565        3144 :     StoreOutputReset();
     566             : 
     567             :     // set to true now that states have be initialised
     568        3144 :     statesInitialised = true;
     569             : 
     570             :     // reset inactive biases
     571       12576 :     for (uint8_t i=0; i<INS_MAX_INSTANCES; i++) {
     572        9432 :         inactiveBias[i].gyro_bias.zero();
     573        9432 :         inactiveBias[i].accel_bias.zero();
     574             :     }
     575             : 
     576             :     // restore the navigation origin from the public origin if possible:
     577        3144 :     if (public_origin.initialised()) {
     578           9 :         setOriginLLH(public_origin);
     579             :     }
     580             : 
     581        3144 :     GCS_SEND_TEXT(MAV_SEVERITY_INFO, "EKF3 IMU%u initialised",(unsigned)imu_index);
     582             : 
     583             :     // we initially return false to wait for the IMU buffer to fill
     584        3144 :     return false;
     585             : }
     586             : 
     587             : // initialise the covariance matrix
     588        3144 : void NavEKF3_core::CovarianceInit()
     589             : {
     590             :     // zero the matrix
     591        3144 :     memset(&P[0][0], 0, sizeof(P));
     592             : 
     593             :     // define the initial angle uncertainty as variances for a rotation vector
     594             :     Vector3F rot_vec_var;
     595        3144 :     rot_vec_var.x = rot_vec_var.y = rot_vec_var.z = sq(0.1f);
     596             : 
     597             :     // reset the quaternion state covariances
     598        3144 :     CovariancePrediction(&rot_vec_var);
     599             : 
     600             :     // velocities
     601        3144 :     P[4][4]   = sq(frontend->_gpsHorizVelNoise);
     602        3144 :     P[5][5]   = P[4][4];
     603        3144 :     P[6][6]   = sq(frontend->_gpsVertVelNoise);
     604             :     // positions
     605        3144 :     P[7][7]   = sq(frontend->_gpsHorizPosNoise);
     606        3144 :     P[8][8]   = P[7][7];
     607        3144 :     P[9][9]   = sq(frontend->_baroAltNoise);
     608             :     // gyro delta angle biases
     609        3144 :     P[10][10] = sq(radians(InitialGyroBiasUncertainty() * dtEkfAvg));
     610        3144 :     P[11][11] = P[10][10];
     611        3144 :     P[12][12] = P[10][10];
     612             :     // delta velocity biases
     613        3144 :     P[13][13] = sq(ACCEL_BIAS_LIM_SCALER * frontend->_accBiasLim * dtEkfAvg);
     614        3144 :     P[14][14] = P[13][13];
     615        3144 :     P[15][15] = P[13][13];
     616             :     // earth magnetic field
     617        3144 :     P[16][16] = sq(frontend->_magNoise);
     618        3144 :     P[17][17] = P[16][16];
     619        3144 :     P[18][18] = P[16][16];
     620             :     // body magnetic field
     621        3144 :     P[19][19] = sq(frontend->_magNoise);
     622        3144 :     P[20][20] = P[19][19];
     623        3144 :     P[21][21] = P[19][19];
     624             :     // wind velocities
     625        3144 :     P[22][22] = 0.0f;
     626        3144 :     P[23][23]  = P[22][22];
     627             : 
     628             : 
     629             : #if EK3_FEATURE_OPTFLOW_FUSION
     630             :     // optical flow ground height covariance
     631        3144 :     Popt = 0.25f;
     632             : #endif
     633             : 
     634        3144 : }
     635             : 
     636             : /********************************************************
     637             : *                 UPDATE FUNCTIONS                      *
     638             : ********************************************************/
     639             : // Update Filter States - this should be called whenever new IMU data is available
     640    78317950 : void NavEKF3_core::UpdateFilter(bool predict)
     641             : {
     642             :     // don't run filter updates if states have not been initialised
     643    78317950 :     if (!statesInitialised) {
     644             :         return;
     645             :     }
     646             : 
     647    78317950 :     fill_scratch_variables();
     648             : 
     649             :     // update sensor selection (for affinity)
     650    78317950 :     update_sensor_selection();
     651             : 
     652             :     // TODO - in-flight restart method
     653             : 
     654             :     // Check arm status and perform required checks and mode changes
     655    78317950 :     controlFilterModes();
     656             : 
     657             :     // read IMU data as delta angles and velocities
     658    78317950 :     readIMUData(predict);
     659             : 
     660             :     // Run the EKF equations to estimate at the fusion time horizon if new IMU data is available in the buffer
     661    78317950 :     if (runUpdates) {
     662             :         // Predict states using IMU data from the delayed time horizon
     663    19965274 :         UpdateStrapdownEquationsNED();
     664             : 
     665             :         // Predict the covariance growth
     666    19965274 :         CovariancePrediction(nullptr);
     667             : 
     668             :         // Run the IMU prediction step for the GSF yaw estimator algorithm
     669             :         // using IMU and optionally true airspeed data.
     670             :         // Must be run before SelectMagFusion() to provide an up to date yaw estimate
     671    19965274 :         runYawEstimatorPrediction();
     672             : 
     673             :         // Update states using  magnetometer or external yaw sensor data
     674    19965274 :         SelectMagFusion();
     675             : 
     676             :         // Update states using GPS and altimeter data
     677    19965274 :         SelectVelPosFusion();
     678             : 
     679             :         // Run the GPS velocity correction step for the GSF yaw estimator algorithm
     680             :         // and use the yaw estimate to reset the main EKF yaw if requested
     681             :         // Muat be run after SelectVelPosFusion() so that fresh GPS data is available
     682    19965274 :         runYawEstimatorCorrection();
     683             : 
     684             : #if EK3_FEATURE_BEACON_FUSION
     685             :         // Update states using range beacon data
     686    19965274 :         SelectRngBcnFusion();
     687             : #endif
     688             : 
     689             : #if EK3_FEATURE_OPTFLOW_FUSION
     690             :         // Update states using optical flow data
     691    19965274 :         SelectFlowFusion();
     692             : #endif
     693             : 
     694             : #if EK3_FEATURE_BODY_ODOM
     695             :         // Update states using body frame odometry data
     696    19965274 :         SelectBodyOdomFusion();
     697             : #endif
     698             : 
     699             :         // Update states using airspeed data
     700    19965274 :         SelectTasFusion();
     701             : 
     702             :         // Update states using sideslip constraint assumption for fly-forward vehicles or body drag for multicopters
     703    19965274 :         SelectBetaDragFusion();
     704             : 
     705             :         // Update the filter status
     706    19965274 :         updateFilterStatus();
     707             : 
     708    19965274 :         if (imuSampleTime_ms - last_oneHz_ms >= 1000) {
     709             :             // 1Hz tasks
     710      280584 :             last_oneHz_ms = imuSampleTime_ms;
     711      280584 :             moveEKFOrigin();
     712      280584 :             checkUpdateEarthField();
     713             :         }
     714             :     }
     715             : 
     716             :     // Wind output forward from the fusion to output time horizon
     717    78317950 :     calcOutputStates();
     718             : 
     719             :     /*
     720             :       this is a check to cope with a vehicle sitting idle on the
     721             :       ground and getting over-confident of the state. The symptoms
     722             :       would be "gyros still settling" when the user tries to arm. In
     723             :       that state the EKF can't recover, so we do a hard reset and let
     724             :       it try again.
     725             :      */
     726    78317950 :     if (filterStatus.value != 0) {
     727    77470025 :         last_filter_ok_ms = dal.millis();
     728             :     }
     729      847925 :     if (filterStatus.value == 0 &&
     730      847925 :         last_filter_ok_ms != 0 &&
     731    78319427 :         dal.millis() - last_filter_ok_ms > 5000 &&
     732           1 :         !dal.get_armed()) {
     733             :         // we've been unhealthy for 5 seconds after being healthy, reset the filter
     734           1 :         GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "EKF3 IMU%u forced reset",(unsigned)imu_index);
     735           1 :         last_filter_ok_ms = 0;
     736           1 :         statesInitialised = false;
     737           1 :         InitialiseFilterBootstrap();
     738             :     }
     739             : }
     740             : 
     741    98396519 : void NavEKF3_core::correctDeltaAngle(Vector3F &delAng, ftype delAngDT, uint8_t gyro_index)
     742             : {
     743    98396519 :     delAng -= inactiveBias[gyro_index].gyro_bias * (delAngDT / dtEkfAvg);
     744    98396519 : }
     745             : 
     746    98396519 : void NavEKF3_core::correctDeltaVelocity(Vector3F &delVel, ftype delVelDT, uint8_t accel_index)
     747             : {
     748    98396519 :     delVel -= inactiveBias[accel_index].accel_bias * (delVelDT / dtEkfAvg);
     749    98396519 : }
     750             : 
     751             : /*
     752             :  * Update the quaternion, velocity and position states using delayed IMU measurements
     753             :  * because the EKF is running on a delayed time horizon. Note that the quaternion is
     754             :  * not used by the EKF equations, which instead estimate the error in the attitude of
     755             :  * the vehicle when each observation is fused. This attitude error is then used to correct
     756             :  * the quaternion.
     757             : */
     758    19965274 : void NavEKF3_core::UpdateStrapdownEquationsNED()
     759             : {
     760             :     // update the quaternion states by rotating from the previous attitude through
     761             :     // the delta angle rotation quaternion and normalise
     762             :     // apply correction for earth's rotation rate
     763             :     // % * - and + operators have been overloaded
     764    19965274 :     stateStruct.quat.rotate(delAngCorrected - prevTnb * earthRateNED*imuDataDelayed.delAngDT);
     765             : 
     766    19965274 :     stateStruct.quat.normalize();
     767             : 
     768             :     // transform body delta velocities to delta velocities in the nav frame
     769             :     // use the nav frame from previous time step as the delta velocities
     770             :     // have been rotated into that frame
     771             :     // * and + operators have been overloaded
     772    19965274 :     Vector3F delVelNav;  // delta velocity vector in earth axes
     773    19965274 :     delVelNav  = prevTnb.mul_transpose(delVelCorrected);
     774    19965274 :     delVelNav.z += GRAVITY_MSS*imuDataDelayed.delVelDT;
     775             : 
     776             :     // calculate the nav to body cosine matrix
     777    19965274 :     stateStruct.quat.inverse().rotation_matrix(prevTnb);
     778             : 
     779             :     // calculate the rate of change of velocity (used for launch detect and other functions)
     780    19965274 :     velDotNED = delVelNav / imuDataDelayed.delVelDT;
     781             : 
     782             :     // apply a first order lowpass filter
     783    19965274 :     velDotNEDfilt = velDotNED * 0.05f + velDotNEDfilt * 0.95f;
     784             : 
     785             :     // calculate a magnitude of the filtered nav acceleration (required for GPS
     786             :     // variance estimation)
     787    19965274 :     accNavMag = velDotNEDfilt.length();
     788    19965274 :     accNavMagHoriz = velDotNEDfilt.xy().length();
     789             : 
     790             :     // if we are not aiding, then limit the horizontal magnitude of acceleration
     791             :     // to prevent large manoeuvre transients disturbing the attitude
     792    19965274 :     if ((PV_AidingMode == AID_NONE) && (accNavMagHoriz > 5.0f)) {
     793        2063 :         ftype gain = 5.0f/accNavMagHoriz;
     794        2063 :         delVelNav.x *= gain;
     795        2063 :         delVelNav.y *= gain;
     796             :     }
     797             : 
     798             :     // save velocity for use in trapezoidal integration for position calcuation
     799    19965274 :     Vector3F lastVelocity = stateStruct.velocity;
     800             : 
     801             :     // sum delta velocities to get velocity
     802    19965274 :     stateStruct.velocity += delVelNav;
     803             : 
     804             :     // apply a trapezoidal integration to velocities to calculate position
     805    19965274 :     stateStruct.position += (stateStruct.velocity + lastVelocity) * (imuDataDelayed.delVelDT*0.5f);
     806             : 
     807             :     // accumulate the bias delta angle and time since last reset by an OF measurement arrival
     808    19965274 :     delAngBodyOF += delAngCorrected;
     809    19965274 :     delTimeOF += imuDataDelayed.delAngDT;
     810             : 
     811             :     // limit states to protect against divergence
     812    19965274 :     ConstrainStates();
     813             : 
     814             : #if EK3_FEATURE_BEACON_FUSION
     815             :     // If main filter velocity states are valid, update the range beacon receiver position states
     816    19965274 :     if (filterStatus.flags.horiz_vel) {
     817    19624288 :         rngBcn.receiverPos += (stateStruct.velocity + lastVelocity) * (imuDataDelayed.delVelDT*0.5f);
     818             :     }
     819             : #endif
     820    19965274 : }
     821             : 
     822             : /*
     823             :  * Propagate PVA solution forward from the fusion time horizon to the current time horizon
     824             :  * using simple observer which performs two functions:
     825             :  * 1) Corrects for the delayed time horizon used by the EKF.
     826             :  * 2) Applies a LPF to state corrections to prevent 'stepping' in states due to measurement
     827             :  * fusion introducing unwanted noise into the control loops.
     828             :  * The inspiration for using a complementary filter to correct for time delays in the EKF
     829             :  * is based on the work by A Khosravian.
     830             :  *
     831             :  * "Recursive Attitude Estimation in the Presence of Multi-rate and Multi-delay Vector Measurements"
     832             :  * A Khosravian, J Trumpf, R Mahony, T Hamel, Australian National University
     833             : */
     834    78317950 : void NavEKF3_core::calcOutputStates()
     835             : {
     836             :     // apply corrections to the IMU data
     837    78317950 :     Vector3F delAngNewCorrected = imuDataNew.delAng;
     838    78317950 :     Vector3F delVelNewCorrected = imuDataNew.delVel;
     839    78317950 :     correctDeltaAngle(delAngNewCorrected, imuDataNew.delAngDT, imuDataNew.gyro_index);
     840    78317950 :     correctDeltaVelocity(delVelNewCorrected, imuDataNew.delVelDT, imuDataNew.accel_index);
     841             : 
     842             :     // apply corrections to track EKF solution
     843    78317950 :     Vector3F delAng = delAngNewCorrected + delAngCorrection;
     844             : 
     845             :     // convert the rotation vector to its equivalent quaternion
     846    78317950 :     QuaternionF deltaQuat;
     847    78317950 :     deltaQuat.from_axis_angle(delAng);
     848             : 
     849             :     // update the quaternion states by rotating from the previous attitude through
     850             :     // the delta angle rotation quaternion and normalise
     851    78317950 :     outputDataNew.quat *= deltaQuat;
     852    78317950 :     outputDataNew.quat.normalize();
     853             : 
     854             :     // calculate the body to nav cosine matrix
     855    78317950 :     Matrix3F Tbn_temp;
     856    78317950 :     outputDataNew.quat.rotation_matrix(Tbn_temp);
     857             : 
     858             :     // transform body delta velocities to delta velocities in the nav frame
     859    78317950 :     Vector3F delVelNav  = Tbn_temp*delVelNewCorrected;
     860    78317950 :     delVelNav.z += GRAVITY_MSS*imuDataNew.delVelDT;
     861             : 
     862             :     // save velocity for use in trapezoidal integration for position calcuation
     863    78317950 :     Vector3F lastVelocity = outputDataNew.velocity;
     864             : 
     865             :     // sum delta velocities to get velocity
     866    78317950 :     outputDataNew.velocity += delVelNav;
     867             : 
     868             :     // Implement third order complementary filter for height and height rate
     869             :     // Reference Paper :
     870             :     // Optimizing the Gains of the Baro-Inertial Vertical Channel
     871             :     // Widnall W.S, Sinha P.K,
     872             :     // AIAA Journal of Guidance and Control, 78-1307R
     873             : 
     874             :     // Perform filter calculation using backwards Euler integration
     875             :     // Coefficients selected to place all three filter poles at omega
     876    78317950 :     const ftype CompFiltOmega = M_2PI * constrain_ftype(frontend->_hrt_filt_freq, 0.1f, 30.0f);
     877    78317950 :     ftype omega2 = CompFiltOmega * CompFiltOmega;
     878    78317950 :     ftype pos_err = constrain_ftype(outputDataNew.position.z - vertCompFiltState.pos, -1e5, 1e5);
     879    78317950 :     ftype integ1_input = pos_err * omega2 * CompFiltOmega * imuDataNew.delVelDT;
     880    78317950 :     vertCompFiltState.acc += integ1_input;
     881    78317950 :     ftype integ2_input = delVelNav.z + (vertCompFiltState.acc + pos_err * omega2 * 3.0f) * imuDataNew.delVelDT;
     882    78317950 :     vertCompFiltState.vel += integ2_input;
     883    78317950 :     ftype integ3_input = (vertCompFiltState.vel + pos_err * CompFiltOmega * 3.0f) * imuDataNew.delVelDT;
     884    78317950 :     vertCompFiltState.pos += integ3_input; 
     885             : 
     886             :     // apply a trapezoidal integration to velocities to calculate position
     887    78317950 :     outputDataNew.position += (outputDataNew.velocity + lastVelocity) * (imuDataNew.delVelDT*0.5f);
     888             : 
     889             :     // If the IMU accelerometer is offset from the body frame origin, then calculate corrections
     890             :     // that can be added to the EKF velocity and position outputs so that they represent the velocity
     891             :     // and position of the body frame origin.
     892             :     // Note the * operator has been overloaded to operate as a dot product
     893    78317950 :     if (!accelPosOffset.is_zero()) {
     894             :         // calculate the average angular rate across the last IMU update
     895             :         // note delAngDT is prevented from being zero in readIMUData()
     896      127686 :         Vector3F angRate = dal.ins().get_gyro(gyro_index_active).toftype();
     897             : 
     898             :         // Calculate the velocity of the body frame origin relative to the IMU in body frame
     899             :         // and rotate into earth frame. Note % operator has been overloaded to perform a cross product
     900      127686 :         Vector3F velBodyRelIMU = angRate % (- accelPosOffset);
     901      127686 :         velOffsetNED = Tbn_temp * velBodyRelIMU;
     902             : 
     903             :         // calculate the earth frame position of the body frame origin relative to the IMU
     904      127686 :         posOffsetNED = Tbn_temp * (- accelPosOffset);
     905             :     } else {
     906    78190264 :         velOffsetNED.zero();
     907    78190264 :         posOffsetNED.zero();
     908             :     }
     909             : 
     910             :     // Detect fixed wing launch acceleration using latest data from IMU to enable early startup of filter functions
     911             :     // that use launch acceleration to detect start of flight
     912    78317950 :     if (!inFlight && !dal.get_takeoff_expected() && assume_zero_sideslip()) {
     913     2082027 :         const ftype launchDelVel = imuDataNew.delVel.x + GRAVITY_MSS * imuDataNew.delVelDT * Tbn_temp.c.x;
     914     2082027 :         if (launchDelVel > GRAVITY_MSS * imuDataNew.delVelDT) {
     915          19 :             dal.set_takeoff_expected();
     916             :         }
     917             :     }
     918             : 
     919             :     // store INS states in a ring buffer that with the same length and time coordinates as the IMU data buffer
     920    78317950 :     if (runUpdates) {
     921             :         // store the states at the output time horizon
     922    19965274 :         storedOutput[storedIMU.get_youngest_index()] = outputDataNew;
     923             : 
     924             :         // recall the states from the fusion time horizon
     925    19965274 :         outputDataDelayed = storedOutput[storedIMU.get_oldest_index()];
     926             : 
     927             :         // compare quaternion data with EKF quaternion at the fusion time horizon and calculate correction
     928             : 
     929             :         // divide the demanded quaternion by the estimated to get the error
     930    19965274 :         QuaternionF quatErr = stateStruct.quat / outputDataDelayed.quat;
     931             : 
     932             :         // Convert to a delta rotation using a small angle approximation
     933    19965274 :         quatErr.normalize();
     934    19965274 :         Vector3F deltaAngErr;
     935             :         ftype scaler;
     936    19965274 :         if (quatErr[0] >= 0.0f) {
     937             :             scaler = 2.0f;
     938             :         } else {
     939             :             scaler = -2.0f;
     940             :         }
     941    19965274 :         deltaAngErr.x = scaler * quatErr[1];
     942    19965274 :         deltaAngErr.y = scaler * quatErr[2];
     943    19965274 :         deltaAngErr.z = scaler * quatErr[3];
     944             : 
     945             :         // calculate a gain that provides tight tracking of the estimator states and
     946             :         // adjust for changes in time delay to maintain consistent damping ratio of ~0.7
     947    19965274 :         ftype timeDelay = 1e-3f * (ftype)(imuDataNew.time_ms - imuDataDelayed.time_ms);
     948    19965274 :         timeDelay = MAX(timeDelay, dtIMUavg);
     949    19965274 :         ftype errorGain = 0.5f / timeDelay;
     950             : 
     951             :         // calculate a correction to the delta angle
     952             :         // that will cause the INS to track the EKF quaternions
     953    19965274 :         delAngCorrection = deltaAngErr * errorGain * dtIMUavg;
     954             : 
     955             :         // calculate velocity and position tracking errors
     956    19965274 :         Vector3F velErr = (stateStruct.velocity - outputDataDelayed.velocity);
     957    19965274 :         Vector3F posErr = (stateStruct.position - outputDataDelayed.position);
     958             : 
     959    19965274 :         if (badIMUdata) {
     960             :             // When IMU accel is bad,  calculate an integral that will be used to drive the difference
     961             :             // between the output state and internal EKF state at the delayed time horizon to zero.
     962       17546 :             badImuVelErrIntegral += (stateStruct.velocity.z - outputDataNew.velocity.z);
     963             :         } else {
     964    19947728 :             badImuVelErrIntegral = velErrintegral.z;
     965             :         }
     966             : 
     967             :         // collect magnitude tracking error for diagnostics
     968    19965274 :         outputTrackError.x = deltaAngErr.length();
     969    19965274 :         outputTrackError.y = velErr.length();
     970    19965274 :         outputTrackError.z = posErr.length();
     971             : 
     972             :         // convert user specified time constant from centi-seconds to seconds
     973    19965274 :         ftype tauPosVel = constrain_ftype(0.01f*(ftype)frontend->_tauVelPosOutput, 0.1f, 0.5f);
     974             : 
     975             :         // calculate a gain to track the EKF position states with the specified time constant
     976    19965274 :         ftype velPosGain = dtEkfAvg / constrain_ftype(tauPosVel, dtEkfAvg, 10.0f);
     977             : 
     978             :         // use a PI feedback to calculate a correction that will be applied to the output state history
     979    19965274 :         posErrintegral += posErr;
     980    19965274 :         velErrintegral += velErr;
     981    19965274 :         Vector3F posCorrection = posErr * velPosGain + posErrintegral * sq(velPosGain) * 0.1F;
     982    19965274 :         Vector3F velCorrection;
     983    19965274 :         velCorrection.x = velErr.x * velPosGain + velErrintegral.x * sq(velPosGain) * 0.1F;
     984    19965274 :         velCorrection.y = velErr.y * velPosGain + velErrintegral.y * sq(velPosGain) * 0.1F;
     985    19965274 :         if (badIMUdata) {
     986       17546 :             velCorrection.z = velErr.z * velPosGain + badImuVelErrIntegral * sq(velPosGain) * 0.07F;
     987       17546 :             velErrintegral.z = badImuVelErrIntegral;
     988             :         } else {
     989    19947728 :             velCorrection.z = velErr.z * velPosGain + velErrintegral.z * sq(velPosGain) * 0.1F;
     990             :         }
     991             : 
     992             :         // loop through the output filter state history and apply the corrections to the velocity and position states
     993             :         // this method is too expensive to use for the attitude states due to the quaternion operations required
     994             :         // but does not introduce a time delay in the 'correction loop' and allows smaller tracking time constants
     995             :         // to be used
     996    19965274 :         output_elements outputStates;
     997   200243343 :         for (unsigned index=0; index < imu_buffer_length; index++) {
     998   180278069 :             outputStates = storedOutput[index];
     999             : 
    1000             :             // a constant  velocity correction is applied
    1001   180278069 :             outputStates.velocity += velCorrection;
    1002             : 
    1003             :             // a constant position correction is applied
    1004   180278069 :             outputStates.position += posCorrection;
    1005             : 
    1006             :             // push the updated data to the buffer
    1007   180278069 :             storedOutput[index] = outputStates;
    1008             :         }
    1009             : 
    1010             :         // update output state to corrected values
    1011    19965274 :         outputDataNew = storedOutput[storedIMU.get_youngest_index()];
    1012             : 
    1013             :     }
    1014    78317950 : }
    1015             : 
    1016             : /*
    1017             :  * Calculate the predicted state covariance matrix using algebraic equations generated using SymPy
    1018             :  * See derivation/generate_1.py for derivation
    1019             :  * Output for change reference: derivation/generated/covariance_generated.cpp
    1020             :  * Argument rotVarVecPtr is pointer to a vector defining the earth frame uncertainty variance of the quaternion states
    1021             :  * used to perform a reset of the quaternion state covariances only. Set to null for normal operation.
    1022             : */
    1023    19972655 : void NavEKF3_core::CovariancePrediction(Vector3F *rotVarVecPtr)
    1024             : {
    1025             :     ftype daxVar;       // X axis delta angle noise variance rad^2
    1026             :     ftype dayVar;       // Y axis delta angle noise variance rad^2
    1027             :     ftype dazVar;       // Z axis delta angle noise variance rad^2
    1028             :     ftype dvxVar;       // X axis delta velocity variance noise (m/s)^2
    1029             :     ftype dvyVar;       // Y axis delta velocity variance noise (m/s)^2
    1030             :     ftype dvzVar;       // Z axis delta velocity variance noise (m/s)^2
    1031             :     ftype dvx;          // X axis delta velocity (m/s)
    1032             :     ftype dvy;          // Y axis delta velocity (m/s)
    1033             :     ftype dvz;          // Z axis delta velocity (m/s)
    1034             :     ftype dax;          // X axis delta angle (rad)
    1035             :     ftype day;          // Y axis delta angle (rad)
    1036             :     ftype daz;          // Z axis delta angle (rad)
    1037             :     ftype q0;           // attitude quaternion
    1038             :     ftype q1;           // attitude quaternion
    1039             :     ftype q2;           // attitude quaternion
    1040             :     ftype q3;           // attitude quaternion
    1041             :     ftype dax_b;        // X axis delta angle measurement bias (rad)
    1042             :     ftype day_b;        // Y axis delta angle measurement bias (rad)
    1043             :     ftype daz_b;        // Z axis delta angle measurement bias (rad)
    1044             :     ftype dvx_b;        // X axis delta velocity measurement bias (rad)
    1045             :     ftype dvy_b;        // Y axis delta velocity measurement bias (rad)
    1046             :     ftype dvz_b;        // Z axis delta velocity measurement bias (rad)
    1047             : 
    1048             :     // Calculate the time step used by the covariance prediction as an average of the gyro and accel integration period
    1049             :     // Constrain to prevent bad timing jitter causing numerical conditioning problems with the covariance prediction
    1050    19972655 :     dt = constrain_ftype(0.5f*(imuDataDelayed.delAngDT+imuDataDelayed.delVelDT),0.5f * dtEkfAvg, 2.0f * dtEkfAvg);
    1051             : 
    1052             :     // use filtered height rate to increase wind process noise when climbing or descending
    1053             :     // this allows for wind gradient effects.Filter height rate using a 10 second time constant filter
    1054    19972655 :     ftype alpha = 0.1f * dt;
    1055    19972655 :     hgtRate = hgtRate * (1.0f - alpha) - stateStruct.velocity.z * alpha;
    1056             : 
    1057             :     // calculate covariance prediction process noise added to diagonals of predicted covariance matrix
    1058             :     // error growth of first 10 kinematic states is built into auto-code for covariance prediction and driven by IMU noise parameters
    1059    19972655 :     Vector14 processNoiseVariance = {};
    1060             : 
    1061    19972655 :     if (!inhibitDelAngBiasStates) {
    1062    19661807 :         ftype dAngBiasVar = sq(sq(dt) * constrain_ftype(frontend->_gyroBiasProcessNoise, 0.0, 1.0));
    1063    78647228 :         for (uint8_t i=0; i<=2; i++) processNoiseVariance[i] = dAngBiasVar;
    1064             :     }
    1065             : 
    1066    19972655 :     if (!inhibitDelVelBiasStates) {
    1067             :         // default process noise (m/s)^2
    1068    19661807 :         ftype dVelBiasVar = sq(sq(dt) * constrain_ftype(frontend->_accelBiasProcessNoise, 0.0, 1.0));
    1069    78647228 :         for (uint8_t i=3; i<=5; i++) {
    1070    58985421 :             processNoiseVariance[i] = dVelBiasVar;
    1071             :         }
    1072             :     }
    1073             : 
    1074    19972655 :     if (!inhibitMagStates && lastInhibitMagStates) {
    1075             :         // when starting 3D fusion we want to reset mag variances
    1076        1363 :         needMagBodyVarReset = true;
    1077        1363 :         needEarthBodyVarReset = true;
    1078             :     }
    1079             : 
    1080    19972655 :     if (needMagBodyVarReset) {
    1081             :         // reset body mag variances
    1082        1451 :         needMagBodyVarReset = false;
    1083        1451 :         zeroStatesVarCov(19, 21);
    1084        1451 :         P[19][19] = sq(frontend->_magNoise);
    1085        1451 :         P[20][20] = P[19][19];
    1086        1451 :         P[21][21] = P[19][19];
    1087             :     }
    1088             : 
    1089    19972655 :     if (needEarthBodyVarReset) {
    1090             :         // reset mag earth field variances
    1091        1363 :         needEarthBodyVarReset = false;
    1092        1363 :         zeroStatesVarCov(16, 18);
    1093        1363 :         P[16][16] = sq(frontend->_magNoise);
    1094        1363 :         P[17][17] = P[16][16];
    1095        1363 :         P[18][18] = P[16][16];
    1096             :         // Fusing the declinaton angle as an observaton with a 20 deg uncertainty helps
    1097             :         // to stabilise the earth field.
    1098        1363 :         FuseDeclination(radians(20.0f));
    1099             :     }
    1100             : 
    1101    19972655 :     if (!inhibitMagStates) {
    1102    12067284 :         ftype magEarthVar = sq(dt * constrain_ftype(frontend->_magEarthProcessNoise, 0.0f, 1.0f));
    1103    12067284 :         ftype magBodyVar  = sq(dt * constrain_ftype(frontend->_magBodyProcessNoise, 0.0f, 1.0f));
    1104    48269136 :         for (uint8_t i=6; i<=8; i++) processNoiseVariance[i] = magEarthVar;
    1105    48269136 :         for (uint8_t i=9; i<=11; i++) processNoiseVariance[i] = magBodyVar;
    1106             :     }
    1107    19972655 :     lastInhibitMagStates = inhibitMagStates;
    1108             : 
    1109    19972655 :     if (!inhibitWindStates) {
    1110     3552707 :         const bool isDragFusionDeadReckoning = filterStatus.flags.dead_reckoning && !dragTimeout;
    1111     3539606 :         const bool newTreatWindStatesAsTruth = isDragFusionDeadReckoning || !windStateIsObservable;
    1112             :         if (newTreatWindStatesAsTruth) {
    1113       37289 :             treatWindStatesAsTruth = true;
    1114       37289 :             zeroStatesVarCov(22, 23);
    1115             :         } else {
    1116     3515418 :             if (treatWindStatesAsTruth) {
    1117          19 :                 treatWindStatesAsTruth = false;
    1118             :                 if (windStateIsObservable) {
    1119             :                     // allow EKF to relearn wind states rapidly
    1120          19 :                     P[23][23] = P[22][22] = sq(WIND_VEL_VARIANCE_MAX);
    1121             :                 }
    1122             :             }
    1123     3515418 :                 ftype windVelVar  = sq(dt * constrain_ftype(frontend->_windVelProcessNoise, 0.0f, 1.0f) * (1.0f + constrain_ftype(frontend->_wndVarHgtRateScale, 0.0f, 1.0f) * fabsF(hgtRate)));
    1124     3515418 :                 if (!tasDataDelayed.allowFusion) {
    1125             :                     // Allow wind states to recover faster when using sideslip fusion with a failed airspeed sesnor
    1126      119803 :                     windVelVar *= 10.0f;
    1127             :                 }
    1128    10546254 :                 for (uint8_t i=12; i<=13; i++) processNoiseVariance[i] = windVelVar;
    1129             :         }
    1130             :     }
    1131             : 
    1132             :     // set variables used to calculate covariance growth
    1133    19972655 :     dvx = imuDataDelayed.delVel.x;
    1134    19972655 :     dvy = imuDataDelayed.delVel.y;
    1135    19972655 :     dvz = imuDataDelayed.delVel.z;
    1136    19972655 :     dax = imuDataDelayed.delAng.x;
    1137    19972655 :     day = imuDataDelayed.delAng.y;
    1138    19972655 :     daz = imuDataDelayed.delAng.z;
    1139    19972655 :     q0 = stateStruct.quat[0];
    1140    19972655 :     q1 = stateStruct.quat[1];
    1141    19972655 :     q2 = stateStruct.quat[2];
    1142    19972655 :     q3 = stateStruct.quat[3];
    1143    19972655 :     dax_b = stateStruct.gyro_bias.x;
    1144    19972655 :     day_b = stateStruct.gyro_bias.y;
    1145    19972655 :     daz_b = stateStruct.gyro_bias.z;
    1146    19972655 :     dvx_b = stateStruct.accel_bias.x;
    1147    19972655 :     dvy_b = stateStruct.accel_bias.y;
    1148    19972655 :     dvz_b = stateStruct.accel_bias.z;
    1149             : 
    1150             :     bool quatCovResetOnly = false;
    1151    19972655 :     if (rotVarVecPtr != nullptr) {
    1152             :         // Handle special case where we are initialising the quaternion covariances using an earth frame
    1153             :         // vector defining the variance of the angular alignment uncertainty. Convert he varaince vector
    1154             :         // to a matrix and rotate into body frame. Use the exisiting gyro error propagation mechanism to
    1155             :         // propagate the body frame angular uncertainty variances.
    1156             :         const Vector3F &rotVarVec = *rotVarVecPtr;
    1157             :         Matrix3F R_ef = Matrix3F (
    1158        7381 :             rotVarVec.x, 0.0f, 0.0f,
    1159        7381 :             0.0f, rotVarVec.y, 0.0f,
    1160        7381 :             0.0f, 0.0f, rotVarVec.z);
    1161        7381 :         Matrix3F Tnb;
    1162        7381 :         stateStruct.quat.inverse().rotation_matrix(Tnb);
    1163        7381 :         Matrix3F R_bf = Tnb * R_ef * Tnb.transposed();
    1164        7381 :         daxVar = R_bf.a.x;
    1165        7381 :         dayVar = R_bf.b.y;
    1166        7381 :         dazVar = R_bf.c.z;
    1167             :         quatCovResetOnly = true;
    1168        7381 :         zeroStatesVarCov(0, 3);
    1169             :     } else {
    1170    19965274 :         ftype _gyrNoise = constrain_ftype(frontend->_gyrNoise, 0.0f, 1.0f);
    1171    19965274 :         daxVar = dayVar = dazVar = sq(dt*_gyrNoise);
    1172             :     }
    1173    19972655 :     ftype _accNoise = badIMUdata ? BAD_IMU_DATA_ACC_P_NSE : constrain_ftype(frontend->_accNoise, 0.0f, BAD_IMU_DATA_ACC_P_NSE);
    1174    19972655 :     dvxVar = dvyVar = dvzVar = sq(dt*_accNoise);
    1175             : 
    1176    19972655 :     if (!inhibitDelVelBiasStates) {
    1177    78647228 :         for (uint8_t stateIndex = 13; stateIndex <= 15; stateIndex++) {
    1178    58985421 :             const uint8_t index = stateIndex - 13;
    1179             : 
    1180             :             // Don't attempt learning of IMU delta velocity bias if on ground.
    1181             :             // In flight: all axes are observable from velocity/position aiding.
    1182             :             // On ground and stationary: only the gravity-aligned axis (Z for a level
    1183             :             // vehicle) is observable. XY biases remain unobservable until the vehicle
    1184             :             // accelerates horizontally in flight.
    1185             :             // On ground and moving (e.g. carried or on a boat): inhibit all axes
    1186             :             // to prevent learning biases from external motion accelerations.
    1187    58985421 :             const bool is_bias_observable = (fabsF(prevTnb[index][2]) > 0.8f && onGroundNotMoving) || !onGround;
    1188             : 
    1189    12081819 :             if (!is_bias_observable && !dvelBiasAxisInhibit[index]) {
    1190             :                 // store variances to be reinstated wben learning can commence later
    1191        9995 :                 dvelBiasAxisVarPrev[index] = P[stateIndex][stateIndex];
    1192        9995 :                 dvelBiasAxisInhibit[index] = true;
    1193    58975426 :             } else if (is_bias_observable && dvelBiasAxisInhibit[index]) {
    1194        4468 :                 P[stateIndex][stateIndex] = dvelBiasAxisVarPrev[index];
    1195        4468 :                 dvelBiasAxisInhibit[index] = false;
    1196             :             }
    1197             :         }
    1198             :     }
    1199             : 
    1200             :     // nextP is a temporary only used in this function, and KHP is a temporary
    1201             :     // the same size only used outside of it. save memory by using KHP as nextP.
    1202             :     auto& nextP = KHP;
    1203             : 
    1204             :     // calculate the predicted covariance due to inertial sensor error propagation
    1205             :     // we calculate the lower diagonal and copy to take advantage of symmetry
    1206             : 
    1207             :     // intermediate calculations
    1208    19972655 :     const ftype PS0 = sq(q1);
    1209    19972655 :     const ftype PS1 = 0.25F*daxVar;
    1210    19972655 :     const ftype PS2 = sq(q2);
    1211    19972655 :     const ftype PS3 = 0.25F*dayVar;
    1212    19972655 :     const ftype PS4 = sq(q3);
    1213    19972655 :     const ftype PS5 = 0.25F*dazVar;
    1214    19972655 :     const ftype PS6 = 0.5F*q1;
    1215    19972655 :     const ftype PS7 = 0.5F*q2;
    1216    19972655 :     const ftype PS8 = PS7*P[10][11];
    1217    19972655 :     const ftype PS9 = 0.5F*q3;
    1218    19972655 :     const ftype PS10 = PS9*P[10][12];
    1219    19972655 :     const ftype PS11 = 0.5F*dax - 0.5F*dax_b;
    1220    19972655 :     const ftype PS12 = 0.5F*day - 0.5F*day_b;
    1221    19972655 :     const ftype PS13 = 0.5F*daz - 0.5F*daz_b;
    1222    19972655 :     const ftype PS14 = PS10 - PS11*P[1][10] - PS12*P[2][10] - PS13*P[3][10] + PS6*P[10][10] + PS8 + P[0][10];
    1223    19972655 :     const ftype PS15 = PS6*P[10][11];
    1224    19972655 :     const ftype PS16 = PS9*P[11][12];
    1225    19972655 :     const ftype PS17 = -PS11*P[1][11] - PS12*P[2][11] - PS13*P[3][11] + PS15 + PS16 + PS7*P[11][11] + P[0][11];
    1226    19972655 :     const ftype PS18 = PS6*P[10][12];
    1227    19972655 :     const ftype PS19 = PS7*P[11][12];
    1228    19972655 :     const ftype PS20 = -PS11*P[1][12] - PS12*P[2][12] - PS13*P[3][12] + PS18 + PS19 + PS9*P[12][12] + P[0][12];
    1229    19972655 :     const ftype PS21 = PS12*P[1][2];
    1230    19972655 :     const ftype PS22 = -PS13*P[1][3];
    1231    19972655 :     const ftype PS23 = -PS11*P[1][1] - PS21 + PS22 + PS6*P[1][10] + PS7*P[1][11] + PS9*P[1][12] + P[0][1];
    1232    19972655 :     const ftype PS24 = -PS11*P[1][2];
    1233    19972655 :     const ftype PS25 = PS13*P[2][3];
    1234    19972655 :     const ftype PS26 = -PS12*P[2][2] + PS24 - PS25 + PS6*P[2][10] + PS7*P[2][11] + PS9*P[2][12] + P[0][2];
    1235    19972655 :     const ftype PS27 = PS11*P[1][3];
    1236    19972655 :     const ftype PS28 = -PS12*P[2][3];
    1237    19972655 :     const ftype PS29 = -PS13*P[3][3] - PS27 + PS28 + PS6*P[3][10] + PS7*P[3][11] + PS9*P[3][12] + P[0][3];
    1238    19972655 :     const ftype PS30 = PS11*P[0][1];
    1239    19972655 :     const ftype PS31 = PS12*P[0][2];
    1240    19972655 :     const ftype PS32 = PS13*P[0][3];
    1241    19972655 :     const ftype PS33 = -PS30 - PS31 - PS32 + PS6*P[0][10] + PS7*P[0][11] + PS9*P[0][12] + P[0][0];
    1242    19972655 :     const ftype PS34 = 0.5F*q0;
    1243    19972655 :     const ftype PS35 = q2*q3;
    1244    19972655 :     const ftype PS36 = q0*q1;
    1245    19972655 :     const ftype PS37 = q1*q3;
    1246    19972655 :     const ftype PS38 = q0*q2;
    1247    19972655 :     const ftype PS39 = q1*q2;
    1248    19972655 :     const ftype PS40 = q0*q3;
    1249    19972655 :     const ftype PS41 = 2*PS2;
    1250    19972655 :     const ftype PS42 = 2*PS4 - 1;
    1251    19972655 :     const ftype PS43 = PS41 + PS42;
    1252    19972655 :     const ftype PS44 = -PS11*P[1][13] - PS12*P[2][13] - PS13*P[3][13] + PS6*P[10][13] + PS7*P[11][13] + PS9*P[12][13] + P[0][13];
    1253    19972655 :     const ftype PS45 = PS37 + PS38;
    1254    19972655 :     const ftype PS46 = -PS11*P[1][15] - PS12*P[2][15] - PS13*P[3][15] + PS6*P[10][15] + PS7*P[11][15] + PS9*P[12][15] + P[0][15];
    1255    19972655 :     const ftype PS47 = 2*PS46;
    1256    19972655 :     const ftype PS48 = dvy - dvy_b;
    1257    19972655 :     const ftype PS49 = PS48*q0;
    1258    19972655 :     const ftype PS50 = dvz - dvz_b;
    1259    19972655 :     const ftype PS51 = PS50*q1;
    1260    19972655 :     const ftype PS52 = dvx - dvx_b;
    1261    19972655 :     const ftype PS53 = PS52*q3;
    1262    19972655 :     const ftype PS54 = PS49 - PS51 + 2*PS53;
    1263    19972655 :     const ftype PS55 = 2*PS29;
    1264    19972655 :     const ftype PS56 = -PS39 + PS40;
    1265    19972655 :     const ftype PS57 = -PS11*P[1][14] - PS12*P[2][14] - PS13*P[3][14] + PS6*P[10][14] + PS7*P[11][14] + PS9*P[12][14] + P[0][14];
    1266    19972655 :     const ftype PS58 = 2*PS57;
    1267    19972655 :     const ftype PS59 = PS48*q2;
    1268    19972655 :     const ftype PS60 = PS50*q3;
    1269    19972655 :     const ftype PS61 = PS59 + PS60;
    1270    19972655 :     const ftype PS62 = 2*PS23;
    1271    19972655 :     const ftype PS63 = PS50*q2;
    1272    19972655 :     const ftype PS64 = PS48*q3;
    1273             :     const ftype PS65 = -PS64;
    1274             :     const ftype PS66 = PS63 + PS65;
    1275    19972655 :     const ftype PS67 = 2*PS33;
    1276    19972655 :     const ftype PS68 = PS50*q0;
    1277    19972655 :     const ftype PS69 = PS48*q1;
    1278    19972655 :     const ftype PS70 = PS52*q2;
    1279    19972655 :     const ftype PS71 = PS68 + PS69 - 2*PS70;
    1280    19972655 :     const ftype PS72 = 2*PS26;
    1281    19972655 :     const ftype PS73 = -PS11*P[1][4] - PS12*P[2][4] - PS13*P[3][4] + PS6*P[4][10] + PS7*P[4][11] + PS9*P[4][12] + P[0][4];
    1282    19972655 :     const ftype PS74 = 2*PS0;
    1283    19972655 :     const ftype PS75 = PS42 + PS74;
    1284    19972655 :     const ftype PS76 = PS39 + PS40;
    1285    19972655 :     const ftype PS77 = 2*PS44;
    1286    19972655 :     const ftype PS78 = PS51 - PS53;
    1287             :     const ftype PS79 = -PS70;
    1288    19972655 :     const ftype PS80 = PS68 + 2*PS69 + PS79;
    1289    19972655 :     const ftype PS81 = -PS35 + PS36;
    1290    19972655 :     const ftype PS82 = PS52*q1;
    1291    19972655 :     const ftype PS83 = PS60 + PS82;
    1292    19972655 :     const ftype PS84 = PS52*q0;
    1293    19972655 :     const ftype PS85 = PS63 - 2*PS64 + PS84;
    1294    19972655 :     const ftype PS86 = -PS11*P[1][5] - PS12*P[2][5] - PS13*P[3][5] + PS6*P[5][10] + PS7*P[5][11] + PS9*P[5][12] + P[0][5];
    1295    19972655 :     const ftype PS87 = PS41 + PS74 - 1;
    1296    19972655 :     const ftype PS88 = PS35 + PS36;
    1297    19972655 :     const ftype PS89 = 2*PS63 + PS65 + PS84;
    1298    19972655 :     const ftype PS90 = -PS37 + PS38;
    1299    19972655 :     const ftype PS91 = PS59 + PS82;
    1300             :     const ftype PS92 = PS69 + PS79;
    1301    19972655 :     const ftype PS93 = PS49 - 2*PS51 + PS53;
    1302    19972655 :     const ftype PS94 = -PS11*P[1][6] - PS12*P[2][6] - PS13*P[3][6] + PS6*P[6][10] + PS7*P[6][11] + PS9*P[6][12] + P[0][6];
    1303    19972655 :     const ftype PS95 = sq(q0);
    1304    19972655 :     const ftype PS96 = -PS34*P[10][11];
    1305    19972655 :     const ftype PS97 = PS11*P[0][11] - PS12*P[3][11] + PS13*P[2][11] - PS19 + PS9*P[11][11] + PS96 + P[1][11];
    1306    19972655 :     const ftype PS98 = PS13*P[0][2];
    1307    19972655 :     const ftype PS99 = PS12*P[0][3];
    1308    19972655 :     const ftype PS100 = PS11*P[0][0] - PS34*P[0][10] - PS7*P[0][12] + PS9*P[0][11] + PS98 - PS99 + P[0][1];
    1309    19972655 :     const ftype PS101 = PS11*P[0][2];
    1310    19972655 :     const ftype PS102 = PS101 + PS13*P[2][2] + PS28 - PS34*P[2][10] - PS7*P[2][12] + PS9*P[2][11] + P[1][2];
    1311    19972655 :     const ftype PS103 = PS9*P[10][11];
    1312    19972655 :     const ftype PS104 = PS7*P[10][12];
    1313    19972655 :     const ftype PS105 = PS103 - PS104 + PS11*P[0][10] - PS12*P[3][10] + PS13*P[2][10] - PS34*P[10][10] + P[1][10];
    1314    19972655 :     const ftype PS106 = -PS34*P[10][12];
    1315    19972655 :     const ftype PS107 = PS106 + PS11*P[0][12] - PS12*P[3][12] + PS13*P[2][12] + PS16 - PS7*P[12][12] + P[1][12];
    1316    19972655 :     const ftype PS108 = PS11*P[0][3];
    1317    19972655 :     const ftype PS109 = PS108 - PS12*P[3][3] + PS25 - PS34*P[3][10] - PS7*P[3][12] + PS9*P[3][11] + P[1][3];
    1318    19972655 :     const ftype PS110 = PS13*P[1][2];
    1319    19972655 :     const ftype PS111 = PS12*P[1][3];
    1320    19972655 :     const ftype PS112 = PS110 - PS111 + PS30 - PS34*P[1][10] - PS7*P[1][12] + PS9*P[1][11] + P[1][1];
    1321    19972655 :     const ftype PS113 = PS11*P[0][13] - PS12*P[3][13] + PS13*P[2][13] - PS34*P[10][13] - PS7*P[12][13] + PS9*P[11][13] + P[1][13];
    1322    19972655 :     const ftype PS114 = PS11*P[0][15] - PS12*P[3][15] + PS13*P[2][15] - PS34*P[10][15] - PS7*P[12][15] + PS9*P[11][15] + P[1][15];
    1323    19972655 :     const ftype PS115 = 2*PS114;
    1324    19972655 :     const ftype PS116 = 2*PS109;
    1325    19972655 :     const ftype PS117 = PS11*P[0][14] - PS12*P[3][14] + PS13*P[2][14] - PS34*P[10][14] - PS7*P[12][14] + PS9*P[11][14] + P[1][14];
    1326    19972655 :     const ftype PS118 = 2*PS117;
    1327    19972655 :     const ftype PS119 = 2*PS112;
    1328    19972655 :     const ftype PS120 = 2*PS100;
    1329    19972655 :     const ftype PS121 = 2*PS102;
    1330    19972655 :     const ftype PS122 = PS11*P[0][4] - PS12*P[3][4] + PS13*P[2][4] - PS34*P[4][10] - PS7*P[4][12] + PS9*P[4][11] + P[1][4];
    1331    19972655 :     const ftype PS123 = 2*PS113;
    1332    19972655 :     const ftype PS124 = PS11*P[0][5] - PS12*P[3][5] + PS13*P[2][5] - PS34*P[5][10] - PS7*P[5][12] + PS9*P[5][11] + P[1][5];
    1333    19972655 :     const ftype PS125 = PS11*P[0][6] - PS12*P[3][6] + PS13*P[2][6] - PS34*P[6][10] - PS7*P[6][12] + PS9*P[6][11] + P[1][6];
    1334    19972655 :     const ftype PS126 = -PS34*P[11][12];
    1335    19972655 :     const ftype PS127 = -PS10 + PS11*P[3][12] + PS12*P[0][12] + PS126 - PS13*P[1][12] + PS6*P[12][12] + P[2][12];
    1336    19972655 :     const ftype PS128 = PS11*P[3][3] + PS22 - PS34*P[3][11] + PS6*P[3][12] - PS9*P[3][10] + PS99 + P[2][3];
    1337    19972655 :     const ftype PS129 = PS13*P[0][1];
    1338    19972655 :     const ftype PS130 = PS108 + PS12*P[0][0] - PS129 - PS34*P[0][11] + PS6*P[0][12] - PS9*P[0][10] + P[0][2];
    1339    19972655 :     const ftype PS131 = PS6*P[11][12];
    1340    19972655 :     const ftype PS132 = -PS103 + PS11*P[3][11] + PS12*P[0][11] - PS13*P[1][11] + PS131 - PS34*P[11][11] + P[2][11];
    1341    19972655 :     const ftype PS133 = PS11*P[3][10] + PS12*P[0][10] - PS13*P[1][10] + PS18 - PS9*P[10][10] + PS96 + P[2][10];
    1342    19972655 :     const ftype PS134 = PS12*P[0][1];
    1343    19972655 :     const ftype PS135 = -PS13*P[1][1] + PS134 + PS27 - PS34*P[1][11] + PS6*P[1][12] - PS9*P[1][10] + P[1][2];
    1344    19972655 :     const ftype PS136 = PS11*P[2][3];
    1345    19972655 :     const ftype PS137 = -PS110 + PS136 + PS31 - PS34*P[2][11] + PS6*P[2][12] - PS9*P[2][10] + P[2][2];
    1346    19972655 :     const ftype PS138 = PS11*P[3][13] + PS12*P[0][13] - PS13*P[1][13] - PS34*P[11][13] + PS6*P[12][13] - PS9*P[10][13] + P[2][13];
    1347    19972655 :     const ftype PS139 = PS11*P[3][15] + PS12*P[0][15] - PS13*P[1][15] - PS34*P[11][15] + PS6*P[12][15] - PS9*P[10][15] + P[2][15];
    1348    19972655 :     const ftype PS140 = 2*PS139;
    1349    19972655 :     const ftype PS141 = 2*PS128;
    1350    19972655 :     const ftype PS142 = PS11*P[3][14] + PS12*P[0][14] - PS13*P[1][14] - PS34*P[11][14] + PS6*P[12][14] - PS9*P[10][14] + P[2][14];
    1351    19972655 :     const ftype PS143 = 2*PS142;
    1352    19972655 :     const ftype PS144 = 2*PS135;
    1353    19972655 :     const ftype PS145 = 2*PS130;
    1354    19972655 :     const ftype PS146 = 2*PS137;
    1355    19972655 :     const ftype PS147 = PS11*P[3][4] + PS12*P[0][4] - PS13*P[1][4] - PS34*P[4][11] + PS6*P[4][12] - PS9*P[4][10] + P[2][4];
    1356    19972655 :     const ftype PS148 = 2*PS138;
    1357    19972655 :     const ftype PS149 = PS11*P[3][5] + PS12*P[0][5] - PS13*P[1][5] - PS34*P[5][11] + PS6*P[5][12] - PS9*P[5][10] + P[2][5];
    1358    19972655 :     const ftype PS150 = PS11*P[3][6] + PS12*P[0][6] - PS13*P[1][6] - PS34*P[6][11] + PS6*P[6][12] - PS9*P[6][10] + P[2][6];
    1359    19972655 :     const ftype PS151 = PS106 - PS11*P[2][10] + PS12*P[1][10] + PS13*P[0][10] - PS15 + PS7*P[10][10] + P[3][10];
    1360    19972655 :     const ftype PS152 = PS12*P[1][1] + PS129 + PS24 - PS34*P[1][12] - PS6*P[1][11] + PS7*P[1][10] + P[1][3];
    1361    19972655 :     const ftype PS153 = -PS101 + PS13*P[0][0] + PS134 - PS34*P[0][12] - PS6*P[0][11] + PS7*P[0][10] + P[0][3];
    1362    19972655 :     const ftype PS154 = PS104 - PS11*P[2][12] + PS12*P[1][12] + PS13*P[0][12] - PS131 - PS34*P[12][12] + P[3][12];
    1363    19972655 :     const ftype PS155 = -PS11*P[2][11] + PS12*P[1][11] + PS126 + PS13*P[0][11] - PS6*P[11][11] + PS8 + P[3][11];
    1364    19972655 :     const ftype PS156 = -PS11*P[2][2] + PS21 - PS34*P[2][12] - PS6*P[2][11] + PS7*P[2][10] + PS98 + P[2][3];
    1365    19972655 :     const ftype PS157 = PS111 - PS136 + PS32 - PS34*P[3][12] - PS6*P[3][11] + PS7*P[3][10] + P[3][3];
    1366    19972655 :     const ftype PS158 = -PS11*P[2][13] + PS12*P[1][13] + PS13*P[0][13] - PS34*P[12][13] - PS6*P[11][13] + PS7*P[10][13] + P[3][13];
    1367    19972655 :     const ftype PS159 = -PS11*P[2][15] + PS12*P[1][15] + PS13*P[0][15] - PS34*P[12][15] - PS6*P[11][15] + PS7*P[10][15] + P[3][15];
    1368    19972655 :     const ftype PS160 = 2*PS159;
    1369    19972655 :     const ftype PS161 = 2*PS157;
    1370    19972655 :     const ftype PS162 = -PS11*P[2][14] + PS12*P[1][14] + PS13*P[0][14] - PS34*P[12][14] - PS6*P[11][14] + PS7*P[10][14] + P[3][14];
    1371    19972655 :     const ftype PS163 = 2*PS162;
    1372    19972655 :     const ftype PS164 = 2*PS152;
    1373    19972655 :     const ftype PS165 = 2*PS153;
    1374    19972655 :     const ftype PS166 = 2*PS156;
    1375    19972655 :     const ftype PS167 = -PS11*P[2][4] + PS12*P[1][4] + PS13*P[0][4] - PS34*P[4][12] - PS6*P[4][11] + PS7*P[4][10] + P[3][4];
    1376    19972655 :     const ftype PS168 = 2*PS158;
    1377    19972655 :     const ftype PS169 = -PS11*P[2][5] + PS12*P[1][5] + PS13*P[0][5] - PS34*P[5][12] - PS6*P[5][11] + PS7*P[5][10] + P[3][5];
    1378    19972655 :     const ftype PS170 = -PS11*P[2][6] + PS12*P[1][6] + PS13*P[0][6] - PS34*P[6][12] - PS6*P[6][11] + PS7*P[6][10] + P[3][6];
    1379    19972655 :     const ftype PS171 = 2*PS45;
    1380    19972655 :     const ftype PS172 = 2*PS56;
    1381    19972655 :     const ftype PS173 = 2*PS61;
    1382    19972655 :     const ftype PS174 = 2*PS66;
    1383    19972655 :     const ftype PS175 = 2*PS71;
    1384    19972655 :     const ftype PS176 = 2*PS54;
    1385    19972655 :     const ftype PS177 = -PS171*P[13][15] + PS172*P[13][14] + PS173*P[1][13] + PS174*P[0][13] + PS175*P[2][13] - PS176*P[3][13] + PS43*P[13][13] + P[4][13];
    1386    19972655 :     const ftype PS178 = -PS171*P[15][15] + PS172*P[14][15] + PS173*P[1][15] + PS174*P[0][15] + PS175*P[2][15] - PS176*P[3][15] + PS43*P[13][15] + P[4][15];
    1387    19972655 :     const ftype PS179 = -PS171*P[3][15] + PS172*P[3][14] + PS173*P[1][3] + PS174*P[0][3] + PS175*P[2][3] - PS176*P[3][3] + PS43*P[3][13] + P[3][4];
    1388    19972655 :     const ftype PS180 = -PS171*P[14][15] + PS172*P[14][14] + PS173*P[1][14] + PS174*P[0][14] + PS175*P[2][14] - PS176*P[3][14] + PS43*P[13][14] + P[4][14];
    1389    19972655 :     const ftype PS181 = -PS171*P[1][15] + PS172*P[1][14] + PS173*P[1][1] + PS174*P[0][1] + PS175*P[1][2] - PS176*P[1][3] + PS43*P[1][13] + P[1][4];
    1390    19972655 :     const ftype PS182 = -PS171*P[0][15] + PS172*P[0][14] + PS173*P[0][1] + PS174*P[0][0] + PS175*P[0][2] - PS176*P[0][3] + PS43*P[0][13] + P[0][4];
    1391    19972655 :     const ftype PS183 = -PS171*P[2][15] + PS172*P[2][14] + PS173*P[1][2] + PS174*P[0][2] + PS175*P[2][2] - PS176*P[2][3] + PS43*P[2][13] + P[2][4];
    1392    19972655 :     const ftype PS184 = 4*dvyVar;
    1393             :     const ftype PS185 = 4*dvzVar;
    1394    19972655 :     const ftype PS186 = -PS171*P[4][15] + PS172*P[4][14] + PS173*P[1][4] + PS174*P[0][4] + PS175*P[2][4] - PS176*P[3][4] + PS43*P[4][13] + P[4][4];
    1395    19972655 :     const ftype PS187 = 2*PS177;
    1396    19972655 :     const ftype PS188 = 2*PS182;
    1397    19972655 :     const ftype PS189 = 2*PS181;
    1398    19972655 :     const ftype PS190 = 2*PS81;
    1399    19972655 :     const ftype PS191 = 2*PS183;
    1400    19972655 :     const ftype PS192 = 2*PS179;
    1401    19972655 :     const ftype PS193 = 2*PS76;
    1402    19972655 :     const ftype PS194 = PS43*dvxVar;
    1403    19972655 :     const ftype PS195 = PS75*dvyVar;
    1404    19972655 :     const ftype PS196 = -PS171*P[5][15] + PS172*P[5][14] + PS173*P[1][5] + PS174*P[0][5] + PS175*P[2][5] - PS176*P[3][5] + PS43*P[5][13] + P[4][5];
    1405    19972655 :     const ftype PS197 = 2*PS88;
    1406    19972655 :     const ftype PS198 = PS87*dvzVar;
    1407    19972655 :     const ftype PS199 = 2*PS90;
    1408    19972655 :     const ftype PS200 = -PS171*P[6][15] + PS172*P[6][14] + PS173*P[1][6] + PS174*P[0][6] + PS175*P[2][6] - PS176*P[3][6] + PS43*P[6][13] + P[4][6];
    1409    19972655 :     const ftype PS201 = 2*PS83;
    1410    19972655 :     const ftype PS202 = 2*PS78;
    1411    19972655 :     const ftype PS203 = 2*PS85;
    1412    19972655 :     const ftype PS204 = 2*PS80;
    1413    19972655 :     const ftype PS205 = PS190*P[14][15] - PS193*P[13][14] + PS201*P[2][14] - PS202*P[0][14] + PS203*P[3][14] - PS204*P[1][14] + PS75*P[14][14] + P[5][14];
    1414    19972655 :     const ftype PS206 = PS190*P[13][15] - PS193*P[13][13] + PS201*P[2][13] - PS202*P[0][13] + PS203*P[3][13] - PS204*P[1][13] + PS75*P[13][14] + P[5][13];
    1415    19972655 :     const ftype PS207 = PS190*P[0][15] - PS193*P[0][13] + PS201*P[0][2] - PS202*P[0][0] + PS203*P[0][3] - PS204*P[0][1] + PS75*P[0][14] + P[0][5];
    1416    19972655 :     const ftype PS208 = PS190*P[1][15] - PS193*P[1][13] + PS201*P[1][2] - PS202*P[0][1] + PS203*P[1][3] - PS204*P[1][1] + PS75*P[1][14] + P[1][5];
    1417    19972655 :     const ftype PS209 = PS190*P[15][15] - PS193*P[13][15] + PS201*P[2][15] - PS202*P[0][15] + PS203*P[3][15] - PS204*P[1][15] + PS75*P[14][15] + P[5][15];
    1418    19972655 :     const ftype PS210 = PS190*P[2][15] - PS193*P[2][13] + PS201*P[2][2] - PS202*P[0][2] + PS203*P[2][3] - PS204*P[1][2] + PS75*P[2][14] + P[2][5];
    1419    19972655 :     const ftype PS211 = PS190*P[3][15] - PS193*P[3][13] + PS201*P[2][3] - PS202*P[0][3] + PS203*P[3][3] - PS204*P[1][3] + PS75*P[3][14] + P[3][5];
    1420             :     const ftype PS212 = 4*dvxVar;
    1421    19972655 :     const ftype PS213 = PS190*P[5][15] - PS193*P[5][13] + PS201*P[2][5] - PS202*P[0][5] + PS203*P[3][5] - PS204*P[1][5] + PS75*P[5][14] + P[5][5];
    1422    19972655 :     const ftype PS214 = 2*PS89;
    1423    19972655 :     const ftype PS215 = 2*PS91;
    1424    19972655 :     const ftype PS216 = 2*PS92;
    1425    19972655 :     const ftype PS217 = 2*PS93;
    1426    19972655 :     const ftype PS218 = PS190*P[6][15] - PS193*P[6][13] + PS201*P[2][6] - PS202*P[0][6] + PS203*P[3][6] - PS204*P[1][6] + PS75*P[6][14] + P[5][6];
    1427    19972655 :     const ftype PS219 = -PS197*P[14][15] + PS199*P[13][15] - PS214*P[2][15] + PS215*P[3][15] + PS216*P[0][15] + PS217*P[1][15] + PS87*P[15][15] + P[6][15];
    1428    19972655 :     const ftype PS220 = -PS197*P[14][14] + PS199*P[13][14] - PS214*P[2][14] + PS215*P[3][14] + PS216*P[0][14] + PS217*P[1][14] + PS87*P[14][15] + P[6][14];
    1429    19972655 :     const ftype PS221 = -PS197*P[13][14] + PS199*P[13][13] - PS214*P[2][13] + PS215*P[3][13] + PS216*P[0][13] + PS217*P[1][13] + PS87*P[13][15] + P[6][13];
    1430    19972655 :     const ftype PS222 = -PS197*P[6][14] + PS199*P[6][13] - PS214*P[2][6] + PS215*P[3][6] + PS216*P[0][6] + PS217*P[1][6] + PS87*P[6][15] + P[6][6];
    1431             : 
    1432    19972655 :     nextP[0][0] = PS0*PS1 - PS11*PS23 - PS12*PS26 - PS13*PS29 + PS14*PS6 + PS17*PS7 + PS2*PS3 + PS20*PS9 + PS33 + PS4*PS5;
    1433    19972655 :     nextP[0][1] = -PS1*PS36 + PS11*PS33 - PS12*PS29 + PS13*PS26 - PS14*PS34 + PS17*PS9 - PS20*PS7 + PS23 + PS3*PS35 - PS35*PS5;
    1434    19972655 :     nextP[1][1] = PS1*PS95 + PS100*PS11 + PS102*PS13 - PS105*PS34 - PS107*PS7 - PS109*PS12 + PS112 + PS2*PS5 + PS3*PS4 + PS9*PS97;
    1435    19972655 :     nextP[0][2] = -PS1*PS37 + PS11*PS29 + PS12*PS33 - PS13*PS23 - PS14*PS9 - PS17*PS34 + PS20*PS6 + PS26 - PS3*PS38 + PS37*PS5;
    1436    19972655 :     nextP[1][2] = PS1*PS40 + PS100*PS12 + PS102 - PS105*PS9 + PS107*PS6 + PS109*PS11 - PS112*PS13 - PS3*PS40 - PS34*PS97 - PS39*PS5;
    1437    19972655 :     nextP[2][2] = PS0*PS5 + PS1*PS4 + PS11*PS128 + PS12*PS130 + PS127*PS6 - PS13*PS135 - PS132*PS34 - PS133*PS9 + PS137 + PS3*PS95;
    1438    19972655 :     nextP[0][3] = PS1*PS39 - PS11*PS26 + PS12*PS23 + PS13*PS33 + PS14*PS7 - PS17*PS6 - PS20*PS34 + PS29 - PS3*PS39 - PS40*PS5;
    1439    19972655 :     nextP[1][3] = -PS1*PS38 + PS100*PS13 - PS102*PS11 + PS105*PS7 - PS107*PS34 + PS109 + PS112*PS12 - PS3*PS37 + PS38*PS5 - PS6*PS97;
    1440    19972655 :     nextP[2][3] = -PS1*PS35 - PS11*PS137 + PS12*PS135 - PS127*PS34 + PS128 + PS13*PS130 - PS132*PS6 + PS133*PS7 + PS3*PS36 - PS36*PS5;
    1441    19972655 :     nextP[3][3] = PS0*PS3 + PS1*PS2 - PS11*PS156 + PS12*PS152 + PS13*PS153 + PS151*PS7 - PS154*PS34 - PS155*PS6 + PS157 + PS5*PS95;
    1442             : 
    1443    19972655 :     if (quatCovResetOnly) {
    1444             :         // covariance matrix is symmetrical, so copy diagonals and copy lower half in nextP
    1445             :         // to lower and upper half in P
    1446       36905 :         for (uint8_t row = 0; row <= 3; row++) {
    1447             :             // copy diagonals
    1448       29524 :             P[row][row] = constrain_ftype(nextP[row][row], 0.0f, 1.0f);
    1449             :             // copy off diagonals
    1450       73810 :             for (uint8_t column = 0 ; column < row; column++) {
    1451       44286 :                 P[row][column] = P[column][row] = nextP[column][row];
    1452             :             }
    1453             :         }
    1454        7381 :         calcTiltErrorVariance();
    1455        7381 :         return;
    1456             :     }
    1457             : 
    1458    19965274 :     nextP[0][4] = PS43*PS44 - PS45*PS47 - PS54*PS55 + PS56*PS58 + PS61*PS62 + PS66*PS67 + PS71*PS72 + PS73;
    1459    19965274 :     nextP[1][4] = PS113*PS43 - PS115*PS45 - PS116*PS54 + PS118*PS56 + PS119*PS61 + PS120*PS66 + PS121*PS71 + PS122;
    1460    19965274 :     nextP[2][4] = PS138*PS43 - PS140*PS45 - PS141*PS54 + PS143*PS56 + PS144*PS61 + PS145*PS66 + PS146*PS71 + PS147;
    1461    19965274 :     nextP[3][4] = PS158*PS43 - PS160*PS45 - PS161*PS54 + PS163*PS56 + PS164*PS61 + PS165*PS66 + PS166*PS71 + PS167;
    1462    19965274 :     nextP[4][4] = -PS171*PS178 + PS172*PS180 + PS173*PS181 + PS174*PS182 + PS175*PS183 - PS176*PS179 + PS177*PS43 + PS184*sq(PS56) + PS185*sq(PS45) + PS186 + sq(PS43)*dvxVar;
    1463    19965274 :     nextP[0][5] = PS47*PS81 + PS55*PS85 + PS57*PS75 - PS62*PS80 - PS67*PS78 + PS72*PS83 - PS76*PS77 + PS86;
    1464    19965274 :     nextP[1][5] = PS115*PS81 + PS116*PS85 + PS117*PS75 - PS119*PS80 - PS120*PS78 + PS121*PS83 - PS123*PS76 + PS124;
    1465    19965274 :     nextP[2][5] = PS140*PS81 + PS141*PS85 + PS142*PS75 - PS144*PS80 - PS145*PS78 + PS146*PS83 - PS148*PS76 + PS149;
    1466    19965274 :     nextP[3][5] = PS160*PS81 + PS161*PS85 + PS162*PS75 - PS164*PS80 - PS165*PS78 + PS166*PS83 - PS168*PS76 + PS169;
    1467    19965274 :     nextP[4][5] = PS172*PS195 + PS178*PS190 + PS180*PS75 - PS185*PS45*PS81 - PS187*PS76 - PS188*PS78 - PS189*PS80 + PS191*PS83 + PS192*PS85 - PS193*PS194 + PS196;
    1468    19965274 :     nextP[5][5] = PS185*sq(PS81) + PS190*PS209 - PS193*PS206 + PS201*PS210 - PS202*PS207 + PS203*PS211 - PS204*PS208 + PS205*PS75 + PS212*sq(PS76) + PS213 + sq(PS75)*dvyVar;
    1469    19965274 :     nextP[0][6] = PS46*PS87 + PS55*PS91 - PS58*PS88 + PS62*PS93 + PS67*PS92 - PS72*PS89 + PS77*PS90 + PS94;
    1470    19965274 :     nextP[1][6] = PS114*PS87 + PS116*PS91 - PS118*PS88 + PS119*PS93 + PS120*PS92 - PS121*PS89 + PS123*PS90 + PS125;
    1471    19965274 :     nextP[2][6] = PS139*PS87 + PS141*PS91 - PS143*PS88 + PS144*PS93 + PS145*PS92 - PS146*PS89 + PS148*PS90 + PS150;
    1472    19965274 :     nextP[3][6] = PS159*PS87 + PS161*PS91 - PS163*PS88 + PS164*PS93 + PS165*PS92 - PS166*PS89 + PS168*PS90 + PS170;
    1473    19965274 :     nextP[4][6] = -PS171*PS198 + PS178*PS87 - PS180*PS197 - PS184*PS56*PS88 + PS187*PS90 + PS188*PS92 + PS189*PS93 - PS191*PS89 + PS192*PS91 + PS194*PS199 + PS200;
    1474    19965274 :     nextP[5][6] = PS190*PS198 - PS195*PS197 - PS197*PS205 + PS199*PS206 + PS207*PS216 + PS208*PS217 + PS209*PS87 - PS210*PS214 + PS211*PS215 - PS212*PS76*PS90 + PS218;
    1475    19965274 :     nextP[6][6] = PS184*sq(PS88) - PS197*PS220 + PS199*PS221 + PS212*sq(PS90) - PS214*(-PS197*P[2][14] + PS199*P[2][13] - PS214*P[2][2] + PS215*P[2][3] + PS216*P[0][2] + PS217*P[1][2] + PS87*P[2][15] + P[2][6]) + PS215*(-PS197*P[3][14] + PS199*P[3][13] - PS214*P[2][3] + PS215*P[3][3] + PS216*P[0][3] + PS217*P[1][3] + PS87*P[3][15] + P[3][6]) + PS216*(-PS197*P[0][14] + PS199*P[0][13] - PS214*P[0][2] + PS215*P[0][3] + PS216*P[0][0] + PS217*P[0][1] + PS87*P[0][15] + P[0][6]) + PS217*(-PS197*P[1][14] + PS199*P[1][13] - PS214*P[1][2] + PS215*P[1][3] + PS216*P[0][1] + PS217*P[1][1] + PS87*P[1][15] + P[1][6]) + PS219*PS87 + PS222 + sq(PS87)*dvzVar;
    1476    19965274 :     nextP[0][7] = -PS11*P[1][7] - PS12*P[2][7] - PS13*P[3][7] + PS6*P[7][10] + PS7*P[7][11] + PS73*dt + PS9*P[7][12] + P[0][7];
    1477    19965274 :     nextP[1][7] = PS11*P[0][7] - PS12*P[3][7] + PS122*dt + PS13*P[2][7] - PS34*P[7][10] - PS7*P[7][12] + PS9*P[7][11] + P[1][7];
    1478    19965274 :     nextP[2][7] = PS11*P[3][7] + PS12*P[0][7] - PS13*P[1][7] + PS147*dt - PS34*P[7][11] + PS6*P[7][12] - PS9*P[7][10] + P[2][7];
    1479    19965274 :     nextP[3][7] = -PS11*P[2][7] + PS12*P[1][7] + PS13*P[0][7] + PS167*dt - PS34*P[7][12] - PS6*P[7][11] + PS7*P[7][10] + P[3][7];
    1480    19965274 :     nextP[4][7] = -PS171*P[7][15] + PS172*P[7][14] + PS173*P[1][7] + PS174*P[0][7] + PS175*P[2][7] - PS176*P[3][7] + PS186*dt + PS43*P[7][13] + P[4][7];
    1481    19965274 :     nextP[5][7] = PS190*P[7][15] - PS193*P[7][13] + PS201*P[2][7] - PS202*P[0][7] + PS203*P[3][7] - PS204*P[1][7] + PS75*P[7][14] + P[5][7] + dt*(PS190*P[4][15] - PS193*P[4][13] + PS201*P[2][4] - PS202*P[0][4] + PS203*P[3][4] - PS204*P[1][4] + PS75*P[4][14] + P[4][5]);
    1482    19965274 :     nextP[6][7] = -PS197*P[7][14] + PS199*P[7][13] - PS214*P[2][7] + PS215*P[3][7] + PS216*P[0][7] + PS217*P[1][7] + PS87*P[7][15] + P[6][7] + dt*(-PS197*P[4][14] + PS199*P[4][13] - PS214*P[2][4] + PS215*P[3][4] + PS216*P[0][4] + PS217*P[1][4] + PS87*P[4][15] + P[4][6]);
    1483    19965274 :     nextP[7][7] = P[4][7]*dt + P[7][7] + dt*(P[4][4]*dt + P[4][7]);
    1484    19965274 :     nextP[0][8] = -PS11*P[1][8] - PS12*P[2][8] - PS13*P[3][8] + PS6*P[8][10] + PS7*P[8][11] + PS86*dt + PS9*P[8][12] + P[0][8];
    1485    19965274 :     nextP[1][8] = PS11*P[0][8] - PS12*P[3][8] + PS124*dt + PS13*P[2][8] - PS34*P[8][10] - PS7*P[8][12] + PS9*P[8][11] + P[1][8];
    1486    19965274 :     nextP[2][8] = PS11*P[3][8] + PS12*P[0][8] - PS13*P[1][8] + PS149*dt - PS34*P[8][11] + PS6*P[8][12] - PS9*P[8][10] + P[2][8];
    1487    19965274 :     nextP[3][8] = -PS11*P[2][8] + PS12*P[1][8] + PS13*P[0][8] + PS169*dt - PS34*P[8][12] - PS6*P[8][11] + PS7*P[8][10] + P[3][8];
    1488    19965274 :     nextP[4][8] = -PS171*P[8][15] + PS172*P[8][14] + PS173*P[1][8] + PS174*P[0][8] + PS175*P[2][8] - PS176*P[3][8] + PS196*dt + PS43*P[8][13] + P[4][8];
    1489    19965274 :     nextP[5][8] = PS190*P[8][15] - PS193*P[8][13] + PS201*P[2][8] - PS202*P[0][8] + PS203*P[3][8] - PS204*P[1][8] + PS213*dt + PS75*P[8][14] + P[5][8];
    1490    19965274 :     nextP[6][8] = -PS197*P[8][14] + PS199*P[8][13] - PS214*P[2][8] + PS215*P[3][8] + PS216*P[0][8] + PS217*P[1][8] + PS87*P[8][15] + P[6][8] + dt*(-PS197*P[5][14] + PS199*P[5][13] - PS214*P[2][5] + PS215*P[3][5] + PS216*P[0][5] + PS217*P[1][5] + PS87*P[5][15] + P[5][6]);
    1491    19965274 :     nextP[7][8] = P[4][8]*dt + P[7][8] + dt*(P[4][5]*dt + P[5][7]);
    1492    19965274 :     nextP[8][8] = P[5][8]*dt + P[8][8] + dt*(P[5][5]*dt + P[5][8]);
    1493    19965274 :     nextP[0][9] = -PS11*P[1][9] - PS12*P[2][9] - PS13*P[3][9] + PS6*P[9][10] + PS7*P[9][11] + PS9*P[9][12] + PS94*dt + P[0][9];
    1494    19965274 :     nextP[1][9] = PS11*P[0][9] - PS12*P[3][9] + PS125*dt + PS13*P[2][9] - PS34*P[9][10] - PS7*P[9][12] + PS9*P[9][11] + P[1][9];
    1495    19965274 :     nextP[2][9] = PS11*P[3][9] + PS12*P[0][9] - PS13*P[1][9] + PS150*dt - PS34*P[9][11] + PS6*P[9][12] - PS9*P[9][10] + P[2][9];
    1496    19965274 :     nextP[3][9] = -PS11*P[2][9] + PS12*P[1][9] + PS13*P[0][9] + PS170*dt - PS34*P[9][12] - PS6*P[9][11] + PS7*P[9][10] + P[3][9];
    1497    19965274 :     nextP[4][9] = -PS171*P[9][15] + PS172*P[9][14] + PS173*P[1][9] + PS174*P[0][9] + PS175*P[2][9] - PS176*P[3][9] + PS200*dt + PS43*P[9][13] + P[4][9];
    1498    19965274 :     nextP[5][9] = PS190*P[9][15] - PS193*P[9][13] + PS201*P[2][9] - PS202*P[0][9] + PS203*P[3][9] - PS204*P[1][9] + PS218*dt + PS75*P[9][14] + P[5][9];
    1499    19965274 :     nextP[6][9] = -PS197*P[9][14] + PS199*P[9][13] - PS214*P[2][9] + PS215*P[3][9] + PS216*P[0][9] + PS217*P[1][9] + PS222*dt + PS87*P[9][15] + P[6][9];
    1500    19965274 :     nextP[7][9] = P[4][9]*dt + P[7][9] + dt*(P[4][6]*dt + P[6][7]);
    1501    19965274 :     nextP[8][9] = P[5][9]*dt + P[8][9] + dt*(P[5][6]*dt + P[6][8]);
    1502    19965274 :     nextP[9][9] = P[6][9]*dt + P[9][9] + dt*(P[6][6]*dt + P[6][9]);
    1503             : 
    1504    19965274 :     if (stateIndexLim > 9) {
    1505    19658046 :         nextP[0][10] = PS14;
    1506    19658046 :         nextP[1][10] = PS105;
    1507    19658046 :         nextP[2][10] = PS133;
    1508    19658046 :         nextP[3][10] = PS151;
    1509    19658046 :         nextP[4][10] = -PS171*P[10][15] + PS172*P[10][14] + PS173*P[1][10] + PS174*P[0][10] + PS175*P[2][10] - PS176*P[3][10] + PS43*P[10][13] + P[4][10];
    1510    19658046 :         nextP[5][10] = PS190*P[10][15] - PS193*P[10][13] + PS201*P[2][10] - PS202*P[0][10] + PS203*P[3][10] - PS204*P[1][10] + PS75*P[10][14] + P[5][10];
    1511    19658046 :         nextP[6][10] = -PS197*P[10][14] + PS199*P[10][13] - PS214*P[2][10] + PS215*P[3][10] + PS216*P[0][10] + PS217*P[1][10] + PS87*P[10][15] + P[6][10];
    1512    19658046 :         nextP[7][10] = P[4][10]*dt + P[7][10];
    1513    19658046 :         nextP[8][10] = P[5][10]*dt + P[8][10];
    1514    19658046 :         nextP[9][10] = P[6][10]*dt + P[9][10];
    1515    19658046 :         nextP[10][10] = P[10][10];
    1516    19658046 :         nextP[0][11] = PS17;
    1517    19658046 :         nextP[1][11] = PS97;
    1518    19658046 :         nextP[2][11] = PS132;
    1519    19658046 :         nextP[3][11] = PS155;
    1520    19658046 :         nextP[4][11] = -PS171*P[11][15] + PS172*P[11][14] + PS173*P[1][11] + PS174*P[0][11] + PS175*P[2][11] - PS176*P[3][11] + PS43*P[11][13] + P[4][11];
    1521    19658046 :         nextP[5][11] = PS190*P[11][15] - PS193*P[11][13] + PS201*P[2][11] - PS202*P[0][11] + PS203*P[3][11] - PS204*P[1][11] + PS75*P[11][14] + P[5][11];
    1522    19658046 :         nextP[6][11] = -PS197*P[11][14] + PS199*P[11][13] - PS214*P[2][11] + PS215*P[3][11] + PS216*P[0][11] + PS217*P[1][11] + PS87*P[11][15] + P[6][11];
    1523    19658046 :         nextP[7][11] = P[4][11]*dt + P[7][11];
    1524    19658046 :         nextP[8][11] = P[5][11]*dt + P[8][11];
    1525    19658046 :         nextP[9][11] = P[6][11]*dt + P[9][11];
    1526    19658046 :         nextP[10][11] = P[10][11];
    1527    19658046 :         nextP[11][11] = P[11][11];
    1528    19658046 :         nextP[0][12] = PS20;
    1529    19658046 :         nextP[1][12] = PS107;
    1530    19658046 :         nextP[2][12] = PS127;
    1531    19658046 :         nextP[3][12] = PS154;
    1532    19658046 :         nextP[4][12] = -PS171*P[12][15] + PS172*P[12][14] + PS173*P[1][12] + PS174*P[0][12] + PS175*P[2][12] - PS176*P[3][12] + PS43*P[12][13] + P[4][12];
    1533    19658046 :         nextP[5][12] = PS190*P[12][15] - PS193*P[12][13] + PS201*P[2][12] - PS202*P[0][12] + PS203*P[3][12] - PS204*P[1][12] + PS75*P[12][14] + P[5][12];
    1534    19658046 :         nextP[6][12] = -PS197*P[12][14] + PS199*P[12][13] - PS214*P[2][12] + PS215*P[3][12] + PS216*P[0][12] + PS217*P[1][12] + PS87*P[12][15] + P[6][12];
    1535    19658046 :         nextP[7][12] = P[4][12]*dt + P[7][12];
    1536    19658046 :         nextP[8][12] = P[5][12]*dt + P[8][12];
    1537    19658046 :         nextP[9][12] = P[6][12]*dt + P[9][12];
    1538    19658046 :         nextP[10][12] = P[10][12];
    1539    19658046 :         nextP[11][12] = P[11][12];
    1540    19658046 :         nextP[12][12] = P[12][12];
    1541             : 
    1542    19658046 :         if (stateIndexLim > 12) {
    1543    19658046 :             nextP[0][13] = PS44;
    1544    19658046 :             nextP[1][13] = PS113;
    1545    19658046 :             nextP[2][13] = PS138;
    1546    19658046 :             nextP[3][13] = PS158;
    1547    19658046 :             nextP[4][13] = PS177;
    1548    19658046 :             nextP[5][13] = PS206;
    1549    19658046 :             nextP[6][13] = PS221;
    1550    19658046 :             nextP[7][13] = P[4][13]*dt + P[7][13];
    1551    19658046 :             nextP[8][13] = P[5][13]*dt + P[8][13];
    1552    19658046 :             nextP[9][13] = P[6][13]*dt + P[9][13];
    1553    19658046 :             nextP[10][13] = P[10][13];
    1554    19658046 :             nextP[11][13] = P[11][13];
    1555    19658046 :             nextP[12][13] = P[12][13];
    1556    19658046 :             nextP[13][13] = P[13][13];
    1557    19658046 :             nextP[0][14] = PS57;
    1558    19658046 :             nextP[1][14] = PS117;
    1559    19658046 :             nextP[2][14] = PS142;
    1560    19658046 :             nextP[3][14] = PS162;
    1561    19658046 :             nextP[4][14] = PS180;
    1562    19658046 :             nextP[5][14] = PS205;
    1563    19658046 :             nextP[6][14] = PS220;
    1564    19658046 :             nextP[7][14] = P[4][14]*dt + P[7][14];
    1565    19658046 :             nextP[8][14] = P[5][14]*dt + P[8][14];
    1566    19658046 :             nextP[9][14] = P[6][14]*dt + P[9][14];
    1567    19658046 :             nextP[10][14] = P[10][14];
    1568    19658046 :             nextP[11][14] = P[11][14];
    1569    19658046 :             nextP[12][14] = P[12][14];
    1570    19658046 :             nextP[13][14] = P[13][14];
    1571    19658046 :             nextP[14][14] = P[14][14];
    1572    19658046 :             nextP[0][15] = PS46;
    1573    19658046 :             nextP[1][15] = PS114;
    1574    19658046 :             nextP[2][15] = PS139;
    1575    19658046 :             nextP[3][15] = PS159;
    1576    19658046 :             nextP[4][15] = PS178;
    1577    19658046 :             nextP[5][15] = PS209;
    1578    19658046 :             nextP[6][15] = PS219;
    1579    19658046 :             nextP[7][15] = P[4][15]*dt + P[7][15];
    1580    19658046 :             nextP[8][15] = P[5][15]*dt + P[8][15];
    1581    19658046 :             nextP[9][15] = P[6][15]*dt + P[9][15];
    1582    19658046 :             nextP[10][15] = P[10][15];
    1583    19658046 :             nextP[11][15] = P[11][15];
    1584    19658046 :             nextP[12][15] = P[12][15];
    1585    19658046 :             nextP[13][15] = P[13][15];
    1586    19658046 :             nextP[14][15] = P[14][15];
    1587    19658046 :             nextP[15][15] = P[15][15];
    1588             : 
    1589    19658046 :             if (stateIndexLim > 15) {
    1590    12095658 :                 nextP[0][16] = -PS11*P[1][16] - PS12*P[2][16] - PS13*P[3][16] + PS6*P[10][16] + PS7*P[11][16] + PS9*P[12][16] + P[0][16];
    1591    12095658 :                 nextP[1][16] = PS11*P[0][16] - PS12*P[3][16] + PS13*P[2][16] - PS34*P[10][16] - PS7*P[12][16] + PS9*P[11][16] + P[1][16];
    1592    12095658 :                 nextP[2][16] = PS11*P[3][16] + PS12*P[0][16] - PS13*P[1][16] - PS34*P[11][16] + PS6*P[12][16] - PS9*P[10][16] + P[2][16];
    1593    12095658 :                 nextP[3][16] = -PS11*P[2][16] + PS12*P[1][16] + PS13*P[0][16] - PS34*P[12][16] - PS6*P[11][16] + PS7*P[10][16] + P[3][16];
    1594    12095658 :                 nextP[4][16] = -PS171*P[15][16] + PS172*P[14][16] + PS173*P[1][16] + PS174*P[0][16] + PS175*P[2][16] - PS176*P[3][16] + PS43*P[13][16] + P[4][16];
    1595    12095658 :                 nextP[5][16] = PS190*P[15][16] - PS193*P[13][16] + PS201*P[2][16] - PS202*P[0][16] + PS203*P[3][16] - PS204*P[1][16] + PS75*P[14][16] + P[5][16];
    1596    12095658 :                 nextP[6][16] = -PS197*P[14][16] + PS199*P[13][16] - PS214*P[2][16] + PS215*P[3][16] + PS216*P[0][16] + PS217*P[1][16] + PS87*P[15][16] + P[6][16];
    1597    12095658 :                 nextP[7][16] = P[4][16]*dt + P[7][16];
    1598    12095658 :                 nextP[8][16] = P[5][16]*dt + P[8][16];
    1599    12095658 :                 nextP[9][16] = P[6][16]*dt + P[9][16];
    1600    12095658 :                 nextP[10][16] = P[10][16];
    1601    12095658 :                 nextP[11][16] = P[11][16];
    1602    12095658 :                 nextP[12][16] = P[12][16];
    1603    12095658 :                 nextP[13][16] = P[13][16];
    1604    12095658 :                 nextP[14][16] = P[14][16];
    1605    12095658 :                 nextP[15][16] = P[15][16];
    1606    12095658 :                 nextP[16][16] = P[16][16];
    1607    12095658 :                 nextP[0][17] = -PS11*P[1][17] - PS12*P[2][17] - PS13*P[3][17] + PS6*P[10][17] + PS7*P[11][17] + PS9*P[12][17] + P[0][17];
    1608    12095658 :                 nextP[1][17] = PS11*P[0][17] - PS12*P[3][17] + PS13*P[2][17] - PS34*P[10][17] - PS7*P[12][17] + PS9*P[11][17] + P[1][17];
    1609    12095658 :                 nextP[2][17] = PS11*P[3][17] + PS12*P[0][17] - PS13*P[1][17] - PS34*P[11][17] + PS6*P[12][17] - PS9*P[10][17] + P[2][17];
    1610    12095658 :                 nextP[3][17] = -PS11*P[2][17] + PS12*P[1][17] + PS13*P[0][17] - PS34*P[12][17] - PS6*P[11][17] + PS7*P[10][17] + P[3][17];
    1611    12095658 :                 nextP[4][17] = -PS171*P[15][17] + PS172*P[14][17] + PS173*P[1][17] + PS174*P[0][17] + PS175*P[2][17] - PS176*P[3][17] + PS43*P[13][17] + P[4][17];
    1612    12095658 :                 nextP[5][17] = PS190*P[15][17] - PS193*P[13][17] + PS201*P[2][17] - PS202*P[0][17] + PS203*P[3][17] - PS204*P[1][17] + PS75*P[14][17] + P[5][17];
    1613    12095658 :                 nextP[6][17] = -PS197*P[14][17] + PS199*P[13][17] - PS214*P[2][17] + PS215*P[3][17] + PS216*P[0][17] + PS217*P[1][17] + PS87*P[15][17] + P[6][17];
    1614    12095658 :                 nextP[7][17] = P[4][17]*dt + P[7][17];
    1615    12095658 :                 nextP[8][17] = P[5][17]*dt + P[8][17];
    1616    12095658 :                 nextP[9][17] = P[6][17]*dt + P[9][17];
    1617    12095658 :                 nextP[10][17] = P[10][17];
    1618    12095658 :                 nextP[11][17] = P[11][17];
    1619    12095658 :                 nextP[12][17] = P[12][17];
    1620    12095658 :                 nextP[13][17] = P[13][17];
    1621    12095658 :                 nextP[14][17] = P[14][17];
    1622    12095658 :                 nextP[15][17] = P[15][17];
    1623    12095658 :                 nextP[16][17] = P[16][17];
    1624    12095658 :                 nextP[17][17] = P[17][17];
    1625    12095658 :                 nextP[0][18] = -PS11*P[1][18] - PS12*P[2][18] - PS13*P[3][18] + PS6*P[10][18] + PS7*P[11][18] + PS9*P[12][18] + P[0][18];
    1626    12095658 :                 nextP[1][18] = PS11*P[0][18] - PS12*P[3][18] + PS13*P[2][18] - PS34*P[10][18] - PS7*P[12][18] + PS9*P[11][18] + P[1][18];
    1627    12095658 :                 nextP[2][18] = PS11*P[3][18] + PS12*P[0][18] - PS13*P[1][18] - PS34*P[11][18] + PS6*P[12][18] - PS9*P[10][18] + P[2][18];
    1628    12095658 :                 nextP[3][18] = -PS11*P[2][18] + PS12*P[1][18] + PS13*P[0][18] - PS34*P[12][18] - PS6*P[11][18] + PS7*P[10][18] + P[3][18];
    1629    12095658 :                 nextP[4][18] = -PS171*P[15][18] + PS172*P[14][18] + PS173*P[1][18] + PS174*P[0][18] + PS175*P[2][18] - PS176*P[3][18] + PS43*P[13][18] + P[4][18];
    1630    12095658 :                 nextP[5][18] = PS190*P[15][18] - PS193*P[13][18] + PS201*P[2][18] - PS202*P[0][18] + PS203*P[3][18] - PS204*P[1][18] + PS75*P[14][18] + P[5][18];
    1631    12095658 :                 nextP[6][18] = -PS197*P[14][18] + PS199*P[13][18] - PS214*P[2][18] + PS215*P[3][18] + PS216*P[0][18] + PS217*P[1][18] + PS87*P[15][18] + P[6][18];
    1632    12095658 :                 nextP[7][18] = P[4][18]*dt + P[7][18];
    1633    12095658 :                 nextP[8][18] = P[5][18]*dt + P[8][18];
    1634    12095658 :                 nextP[9][18] = P[6][18]*dt + P[9][18];
    1635    12095658 :                 nextP[10][18] = P[10][18];
    1636    12095658 :                 nextP[11][18] = P[11][18];
    1637    12095658 :                 nextP[12][18] = P[12][18];
    1638    12095658 :                 nextP[13][18] = P[13][18];
    1639    12095658 :                 nextP[14][18] = P[14][18];
    1640    12095658 :                 nextP[15][18] = P[15][18];
    1641    12095658 :                 nextP[16][18] = P[16][18];
    1642    12095658 :                 nextP[17][18] = P[17][18];
    1643    12095658 :                 nextP[18][18] = P[18][18];
    1644    12095658 :                 nextP[0][19] = -PS11*P[1][19] - PS12*P[2][19] - PS13*P[3][19] + PS6*P[10][19] + PS7*P[11][19] + PS9*P[12][19] + P[0][19];
    1645    12095658 :                 nextP[1][19] = PS11*P[0][19] - PS12*P[3][19] + PS13*P[2][19] - PS34*P[10][19] - PS7*P[12][19] + PS9*P[11][19] + P[1][19];
    1646    12095658 :                 nextP[2][19] = PS11*P[3][19] + PS12*P[0][19] - PS13*P[1][19] - PS34*P[11][19] + PS6*P[12][19] - PS9*P[10][19] + P[2][19];
    1647    12095658 :                 nextP[3][19] = -PS11*P[2][19] + PS12*P[1][19] + PS13*P[0][19] - PS34*P[12][19] - PS6*P[11][19] + PS7*P[10][19] + P[3][19];
    1648    12095658 :                 nextP[4][19] = -PS171*P[15][19] + PS172*P[14][19] + PS173*P[1][19] + PS174*P[0][19] + PS175*P[2][19] - PS176*P[3][19] + PS43*P[13][19] + P[4][19];
    1649    12095658 :                 nextP[5][19] = PS190*P[15][19] - PS193*P[13][19] + PS201*P[2][19] - PS202*P[0][19] + PS203*P[3][19] - PS204*P[1][19] + PS75*P[14][19] + P[5][19];
    1650    12095658 :                 nextP[6][19] = -PS197*P[14][19] + PS199*P[13][19] - PS214*P[2][19] + PS215*P[3][19] + PS216*P[0][19] + PS217*P[1][19] + PS87*P[15][19] + P[6][19];
    1651    12095658 :                 nextP[7][19] = P[4][19]*dt + P[7][19];
    1652    12095658 :                 nextP[8][19] = P[5][19]*dt + P[8][19];
    1653    12095658 :                 nextP[9][19] = P[6][19]*dt + P[9][19];
    1654    12095658 :                 nextP[10][19] = P[10][19];
    1655    12095658 :                 nextP[11][19] = P[11][19];
    1656    12095658 :                 nextP[12][19] = P[12][19];
    1657    12095658 :                 nextP[13][19] = P[13][19];
    1658    12095658 :                 nextP[14][19] = P[14][19];
    1659    12095658 :                 nextP[15][19] = P[15][19];
    1660    12095658 :                 nextP[16][19] = P[16][19];
    1661    12095658 :                 nextP[17][19] = P[17][19];
    1662    12095658 :                 nextP[18][19] = P[18][19];
    1663    12095658 :                 nextP[19][19] = P[19][19];
    1664    12095658 :                 nextP[0][20] = -PS11*P[1][20] - PS12*P[2][20] - PS13*P[3][20] + PS6*P[10][20] + PS7*P[11][20] + PS9*P[12][20] + P[0][20];
    1665    12095658 :                 nextP[1][20] = PS11*P[0][20] - PS12*P[3][20] + PS13*P[2][20] - PS34*P[10][20] - PS7*P[12][20] + PS9*P[11][20] + P[1][20];
    1666    12095658 :                 nextP[2][20] = PS11*P[3][20] + PS12*P[0][20] - PS13*P[1][20] - PS34*P[11][20] + PS6*P[12][20] - PS9*P[10][20] + P[2][20];
    1667    12095658 :                 nextP[3][20] = -PS11*P[2][20] + PS12*P[1][20] + PS13*P[0][20] - PS34*P[12][20] - PS6*P[11][20] + PS7*P[10][20] + P[3][20];
    1668    12095658 :                 nextP[4][20] = -PS171*P[15][20] + PS172*P[14][20] + PS173*P[1][20] + PS174*P[0][20] + PS175*P[2][20] - PS176*P[3][20] + PS43*P[13][20] + P[4][20];
    1669    12095658 :                 nextP[5][20] = PS190*P[15][20] - PS193*P[13][20] + PS201*P[2][20] - PS202*P[0][20] + PS203*P[3][20] - PS204*P[1][20] + PS75*P[14][20] + P[5][20];
    1670    12095658 :                 nextP[6][20] = -PS197*P[14][20] + PS199*P[13][20] - PS214*P[2][20] + PS215*P[3][20] + PS216*P[0][20] + PS217*P[1][20] + PS87*P[15][20] + P[6][20];
    1671    12095658 :                 nextP[7][20] = P[4][20]*dt + P[7][20];
    1672    12095658 :                 nextP[8][20] = P[5][20]*dt + P[8][20];
    1673    12095658 :                 nextP[9][20] = P[6][20]*dt + P[9][20];
    1674    12095658 :                 nextP[10][20] = P[10][20];
    1675    12095658 :                 nextP[11][20] = P[11][20];
    1676    12095658 :                 nextP[12][20] = P[12][20];
    1677    12095658 :                 nextP[13][20] = P[13][20];
    1678    12095658 :                 nextP[14][20] = P[14][20];
    1679    12095658 :                 nextP[15][20] = P[15][20];
    1680    12095658 :                 nextP[16][20] = P[16][20];
    1681    12095658 :                 nextP[17][20] = P[17][20];
    1682    12095658 :                 nextP[18][20] = P[18][20];
    1683    12095658 :                 nextP[19][20] = P[19][20];
    1684    12095658 :                 nextP[20][20] = P[20][20];
    1685    12095658 :                 nextP[0][21] = -PS11*P[1][21] - PS12*P[2][21] - PS13*P[3][21] + PS6*P[10][21] + PS7*P[11][21] + PS9*P[12][21] + P[0][21];
    1686    12095658 :                 nextP[1][21] = PS11*P[0][21] - PS12*P[3][21] + PS13*P[2][21] - PS34*P[10][21] - PS7*P[12][21] + PS9*P[11][21] + P[1][21];
    1687    12095658 :                 nextP[2][21] = PS11*P[3][21] + PS12*P[0][21] - PS13*P[1][21] - PS34*P[11][21] + PS6*P[12][21] - PS9*P[10][21] + P[2][21];
    1688    12095658 :                 nextP[3][21] = -PS11*P[2][21] + PS12*P[1][21] + PS13*P[0][21] - PS34*P[12][21] - PS6*P[11][21] + PS7*P[10][21] + P[3][21];
    1689    12095658 :                 nextP[4][21] = -PS171*P[15][21] + PS172*P[14][21] + PS173*P[1][21] + PS174*P[0][21] + PS175*P[2][21] - PS176*P[3][21] + PS43*P[13][21] + P[4][21];
    1690    12095658 :                 nextP[5][21] = PS190*P[15][21] - PS193*P[13][21] + PS201*P[2][21] - PS202*P[0][21] + PS203*P[3][21] - PS204*P[1][21] + PS75*P[14][21] + P[5][21];
    1691    12095658 :                 nextP[6][21] = -PS197*P[14][21] + PS199*P[13][21] - PS214*P[2][21] + PS215*P[3][21] + PS216*P[0][21] + PS217*P[1][21] + PS87*P[15][21] + P[6][21];
    1692    12095658 :                 nextP[7][21] = P[4][21]*dt + P[7][21];
    1693    12095658 :                 nextP[8][21] = P[5][21]*dt + P[8][21];
    1694    12095658 :                 nextP[9][21] = P[6][21]*dt + P[9][21];
    1695    12095658 :                 nextP[10][21] = P[10][21];
    1696    12095658 :                 nextP[11][21] = P[11][21];
    1697    12095658 :                 nextP[12][21] = P[12][21];
    1698    12095658 :                 nextP[13][21] = P[13][21];
    1699    12095658 :                 nextP[14][21] = P[14][21];
    1700    12095658 :                 nextP[15][21] = P[15][21];
    1701    12095658 :                 nextP[16][21] = P[16][21];
    1702    12095658 :                 nextP[17][21] = P[17][21];
    1703    12095658 :                 nextP[18][21] = P[18][21];
    1704    12095658 :                 nextP[19][21] = P[19][21];
    1705    12095658 :                 nextP[20][21] = P[20][21];
    1706    12095658 :                 nextP[21][21] = P[21][21];
    1707             : 
    1708    12095658 :                 if (stateIndexLim > 21) {
    1709     3552705 :                     nextP[0][22] = -PS11*P[1][22] - PS12*P[2][22] - PS13*P[3][22] + PS6*P[10][22] + PS7*P[11][22] + PS9*P[12][22] + P[0][22];
    1710     3552705 :                     nextP[1][22] = PS11*P[0][22] - PS12*P[3][22] + PS13*P[2][22] - PS34*P[10][22] - PS7*P[12][22] + PS9*P[11][22] + P[1][22];
    1711     3552705 :                     nextP[2][22] = PS11*P[3][22] + PS12*P[0][22] - PS13*P[1][22] - PS34*P[11][22] + PS6*P[12][22] - PS9*P[10][22] + P[2][22];
    1712     3552705 :                     nextP[3][22] = -PS11*P[2][22] + PS12*P[1][22] + PS13*P[0][22] - PS34*P[12][22] - PS6*P[11][22] + PS7*P[10][22] + P[3][22];
    1713     3552705 :                     nextP[4][22] = -PS171*P[15][22] + PS172*P[14][22] + PS173*P[1][22] + PS174*P[0][22] + PS175*P[2][22] - PS176*P[3][22] + PS43*P[13][22] + P[4][22];
    1714     3552705 :                     nextP[5][22] = PS190*P[15][22] - PS193*P[13][22] + PS201*P[2][22] - PS202*P[0][22] + PS203*P[3][22] - PS204*P[1][22] + PS75*P[14][22] + P[5][22];
    1715     3552705 :                     nextP[6][22] = -PS197*P[14][22] + PS199*P[13][22] - PS214*P[2][22] + PS215*P[3][22] + PS216*P[0][22] + PS217*P[1][22] + PS87*P[15][22] + P[6][22];
    1716     3552705 :                     nextP[7][22] = P[4][22]*dt + P[7][22];
    1717     3552705 :                     nextP[8][22] = P[5][22]*dt + P[8][22];
    1718     3552705 :                     nextP[9][22] = P[6][22]*dt + P[9][22];
    1719     3552705 :                     nextP[10][22] = P[10][22];
    1720     3552705 :                     nextP[11][22] = P[11][22];
    1721     3552705 :                     nextP[12][22] = P[12][22];
    1722     3552705 :                     nextP[13][22] = P[13][22];
    1723     3552705 :                     nextP[14][22] = P[14][22];
    1724     3552705 :                     nextP[15][22] = P[15][22];
    1725     3552705 :                     nextP[16][22] = P[16][22];
    1726     3552705 :                     nextP[17][22] = P[17][22];
    1727     3552705 :                     nextP[18][22] = P[18][22];
    1728     3552705 :                     nextP[19][22] = P[19][22];
    1729     3552705 :                     nextP[20][22] = P[20][22];
    1730     3552705 :                     nextP[21][22] = P[21][22];
    1731     3552705 :                     nextP[22][22] = P[22][22];
    1732     3552705 :                     nextP[0][23] = -PS11*P[1][23] - PS12*P[2][23] - PS13*P[3][23] + PS6*P[10][23] + PS7*P[11][23] + PS9*P[12][23] + P[0][23];
    1733     3552705 :                     nextP[1][23] = PS11*P[0][23] - PS12*P[3][23] + PS13*P[2][23] - PS34*P[10][23] - PS7*P[12][23] + PS9*P[11][23] + P[1][23];
    1734     3552705 :                     nextP[2][23] = PS11*P[3][23] + PS12*P[0][23] - PS13*P[1][23] - PS34*P[11][23] + PS6*P[12][23] - PS9*P[10][23] + P[2][23];
    1735     3552705 :                     nextP[3][23] = -PS11*P[2][23] + PS12*P[1][23] + PS13*P[0][23] - PS34*P[12][23] - PS6*P[11][23] + PS7*P[10][23] + P[3][23];
    1736     3552705 :                     nextP[4][23] = -PS171*P[15][23] + PS172*P[14][23] + PS173*P[1][23] + PS174*P[0][23] + PS175*P[2][23] - PS176*P[3][23] + PS43*P[13][23] + P[4][23];
    1737     3552705 :                     nextP[5][23] = PS190*P[15][23] - PS193*P[13][23] + PS201*P[2][23] - PS202*P[0][23] + PS203*P[3][23] - PS204*P[1][23] + PS75*P[14][23] + P[5][23];
    1738     3552705 :                     nextP[6][23] = -PS197*P[14][23] + PS199*P[13][23] - PS214*P[2][23] + PS215*P[3][23] + PS216*P[0][23] + PS217*P[1][23] + PS87*P[15][23] + P[6][23];
    1739     3552705 :                     nextP[7][23] = P[4][23]*dt + P[7][23];
    1740     3552705 :                     nextP[8][23] = P[5][23]*dt + P[8][23];
    1741     3552705 :                     nextP[9][23] = P[6][23]*dt + P[9][23];
    1742     3552705 :                     nextP[10][23] = P[10][23];
    1743     3552705 :                     nextP[11][23] = P[11][23];
    1744     3552705 :                     nextP[12][23] = P[12][23];
    1745     3552705 :                     nextP[13][23] = P[13][23];
    1746     3552705 :                     nextP[14][23] = P[14][23];
    1747     3552705 :                     nextP[15][23] = P[15][23];
    1748     3552705 :                     nextP[16][23] = P[16][23];
    1749     3552705 :                     nextP[17][23] = P[17][23];
    1750     3552705 :                     nextP[18][23] = P[18][23];
    1751     3552705 :                     nextP[19][23] = P[19][23];
    1752     3552705 :                     nextP[20][23] = P[20][23];
    1753     3552705 :                     nextP[21][23] = P[21][23];
    1754     3552705 :                     nextP[22][23] = P[22][23];
    1755     3552705 :                     nextP[23][23] = P[23][23];
    1756             :                 }
    1757             :             }
    1758             :         }
    1759             :     }
    1760             : 
    1761             :     // add the general state process noise variances
    1762    19965274 :     if (stateIndexLim > 9) {
    1763   217285680 :         for (uint8_t i=10; i<=stateIndexLim; i++) {
    1764   197627634 :             nextP[i][i] = nextP[i][i] + processNoiseVariance[i-10];
    1765             :         }
    1766             :     }
    1767             : 
    1768             :     // if the total position variance exceeds 1e4 (100m), then stop covariance
    1769             :     // growth by setting the predicted to the previous values
    1770             :     // This prevent an ill conditioned matrix from occurring for long periods
    1771             :     // without GPS
    1772    19965274 :     if ((P[7][7] + P[8][8]) > 1e4f) {
    1773       48912 :         for (uint8_t i=7; i<=8; i++)
    1774             :         {
    1775      706120 :             for (uint8_t j=0; j<=stateIndexLim; j++)
    1776             :             {
    1777      673512 :                 nextP[i][j] = P[i][j];
    1778      673512 :                 nextP[j][i] = P[j][i];
    1779             :             }
    1780             :         }
    1781             :     }
    1782             : 
    1783             :     // covariance matrix is symmetrical, so copy diagonals and copy lower half in nextP
    1784             :     // to lower and upper half in P
    1785   417245648 :     for (uint8_t row = 0; row <= stateIndexLim; row++) {
    1786             :         // copy diagonals
    1787   397280374 :         P[row][row] = nextP[row][row];
    1788             :         // copy off diagonals
    1789  4272560917 :         for (uint8_t column = 0 ; column < row; column++) {
    1790  3875280543 :             P[row][column] = P[column][row] = nextP[column][row];
    1791             :         }
    1792             :     }
    1793             : 
    1794             :     // inactive delta velocity bias states have all covariances zeroed to
    1795             :     // prevent interaction with other states
    1796    19965274 :     if (!inhibitDelVelBiasStates) {
    1797    78630296 :         for (uint8_t index=0; index<3; index++) {
    1798    58972722 :             const uint8_t stateIndex = index + 13;
    1799    58972722 :             if (dvelBiasAxisInhibit[index]) {
    1800    12076408 :                 zeroStatesVarCov(stateIndex, stateIndex);
    1801    12076408 :                 P[stateIndex][stateIndex] = dvelBiasAxisVarPrev[index];
    1802             :             }
    1803             :         }
    1804             :     }
    1805             : 
    1806             :     // constrain values to prevent ill-conditioning
    1807    19965274 :     ConstrainVariances();
    1808             : 
    1809    19965274 :     if (vertVelVarClipCounter > 0) {
    1810           0 :         vertVelVarClipCounter--;
    1811             :     }
    1812             : 
    1813    19965274 :     calcTiltErrorVariance();
    1814             : 
    1815             : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
    1816    19965274 :     verifyTiltErrorVariance();
    1817             : #endif
    1818             : }
    1819             : 
    1820             : // zero specified state variances and covariances in state covariance matrix
    1821    57611682 : void NavEKF3_core::zeroStatesVarCov(uint8_t first, uint8_t last)
    1822             : {
    1823             :     uint8_t row;
    1824   217817790 :     for (row=first; row<=last; row++)
    1825             :     {
    1826   160206108 :         zero_range(&P[row][0], 0, 23);
    1827             :     }
    1828             : 
    1829  1440292050 :     for (row=0; row<=23; row++)
    1830             :     {
    1831  1382680368 :         zero_range(&P[row][0], first, last);
    1832             :     }
    1833    57611682 : }
    1834             : 
    1835             : // reset the output data to the current EKF state
    1836        3144 : void NavEKF3_core::StoreOutputReset()
    1837             : {
    1838        3144 :     outputDataNew.quat = stateStruct.quat;
    1839        3144 :     outputDataNew.velocity = stateStruct.velocity;
    1840        3144 :     outputDataNew.position = stateStruct.position;
    1841             :     // write current measurement to entire table
    1842       31713 :     for (uint8_t i=0; i<imu_buffer_length; i++) {
    1843       28569 :         storedOutput[i] = outputDataNew;
    1844             :     }
    1845        3144 :     outputDataDelayed = outputDataNew;
    1846             :     // reset the states for the complementary filter used to provide a vertical position derivative output
    1847        3144 :     vertCompFiltState.pos = stateStruct.position.z;
    1848        3144 :     vertCompFiltState.vel = stateStruct.velocity.z;
    1849        3144 : }
    1850             : 
    1851             : // Reset the stored output quaternion history to current EKF state
    1852           0 : void NavEKF3_core::StoreQuatReset()
    1853             : {
    1854           0 :     outputDataNew.quat = stateStruct.quat;
    1855             :     // write current measurement to entire table
    1856           0 :     for (uint8_t i=0; i<imu_buffer_length; i++) {
    1857           0 :         storedOutput[i].quat = outputDataNew.quat;
    1858             :     }
    1859           0 :     outputDataDelayed.quat = outputDataNew.quat;
    1860           0 : }
    1861             : 
    1862             : // Rotate the stored output quaternion history through a quaternion rotation
    1863        4237 : void NavEKF3_core::StoreQuatRotate(const QuaternionF &deltaQuat)
    1864             : {
    1865        4237 :     outputDataNew.quat = outputDataNew.quat*deltaQuat;
    1866             :     // write current measurement to entire table
    1867       42722 :     for (uint8_t i=0; i<imu_buffer_length; i++) {
    1868       38485 :         storedOutput[i].quat = storedOutput[i].quat*deltaQuat;
    1869             :     }
    1870        4237 :     outputDataDelayed.quat = outputDataDelayed.quat*deltaQuat;
    1871        4237 : }
    1872             : 
    1873             : // constrain variances (diagonal terms) in the state covariance matrix to  prevent ill-conditioning
    1874             : // if states are inactive, zero the corresponding off-diagonals
    1875    39772176 : void NavEKF3_core::ConstrainVariances()
    1876             : {
    1877             :     // Covariance constraints as of March 2025
    1878             :     // This table assumes normal operations, there are additional constraints for specific failure modes like "badIMUdata" and "inhibitDelAngBiasStates".
    1879             :     // +----------------------------------------------------------------------------------------------+
    1880             :     // | State Index  |      State Name                 |  State Units  | Variance Constraint Range   |
    1881             :     // +----------------------------------------------------------------------------------------------+
    1882             :     // |  0 .. 3      | Attitude Quaternion             | unitless      | [0.0, 1.0]                  |
    1883             :     // |  4 .. 5      | Velocity (North, East)          | m/s           | [1e-4, 1e3]                 |
    1884             :     // |  6           | Velocity (Down)                 | m/s           | dynamic                     |
    1885             :     // |  7 .. 9      | Position (North, East, Down)    | m             | [1e-4, 1e6]                 |
    1886             :     // | 10 .. 12     | Gyro Bias (X, Y, Z)             | rad           | [0.0, (0.175 * dtEkfAvg)^2] |
    1887             :     // | 13 .. 15     | Accel Bias (X, Y, Z)            | m/s^2         | dynamic                     |
    1888             :     // | 16 .. 18     | Earth Magnetic Field (X, Y, Z)  | Gauss         | [0.0, 0.01]                 |
    1889             :     // | 19 .. 21     | Body Magnetic Field (X, Y, Z)   | Gauss         | [0.0, 0.01]                 |
    1890             :     // | 22 .. 23     | Wind Velocity (North, East)     | m/s           | [0.0, 400]                  |
    1891             :     // +----------------------------------------------------------------------------------------------+
    1892             : 
    1893   198860880 :     for (uint8_t i=0; i<=3; i++) P[i][i] = constrain_ftype(P[i][i],0.0,1.0); // attitude error
    1894   119316528 :     for (uint8_t i=4; i<=5; i++) P[i][i] = constrain_ftype(P[i][i], VEL_STATE_MIN_VARIANCE, 1.0e3); // NE velocity
    1895             : 
    1896             :     // if vibration affected use sensor observation variances to set a floor on the state variances
    1897    39772176 :     if (badIMUdata) {
    1898       32410 :         P[6][6] = fmaxF(P[6][6], sq(frontend->_gpsVertVelNoise));
    1899       32410 :         P[9][9] = fmaxF(P[9][9], sq(frontend->_baroAltNoise));
    1900    39739766 :     } else if (P[6][6] < VEL_STATE_MIN_VARIANCE) {
    1901             :         // handle collapse of the vertical velocity variance
    1902           0 :         P[6][6] = VEL_STATE_MIN_VARIANCE;
    1903             :         // this counter is decremented by 1 each prediction cycle in CovariancePrediction
    1904             :         // resulting in the count from each clip event fading to zero over 1 second which
    1905             :         // is sufficient to capture collapse from fusion of the lowest update rate sensor
    1906           0 :         vertVelVarClipCounter += EKF_TARGET_RATE_HZ;
    1907           0 :         if (vertVelVarClipCounter > VERT_VEL_VAR_CLIP_COUNT_LIM) {
    1908             :             // reset the corresponding covariances
    1909           0 :             zeroStatesVarCov(6, 6);
    1910             : 
    1911             :             // set the variances to the measurement variance
    1912             :         #if EK3_FEATURE_EXTERNAL_NAV
    1913           0 :             if (useExtNavVel) {
    1914           0 :                 P[6][6] = sq(extNavVelDelayed.err);
    1915             :             } else
    1916             :         #endif
    1917             :             {
    1918           0 :                 P[6][6] = sq(frontend->_gpsVertVelNoise);
    1919             :             }
    1920           0 :             vertVelVarClipCounter = 0;
    1921             :         }
    1922             :     }
    1923             : 
    1924   159088704 :     for (uint8_t i=7; i<=9; i++) P[i][i] = constrain_ftype(P[i][i], POS_STATE_MIN_VARIANCE, 1.0e6); // NED position
    1925             : 
    1926    39772176 :     if (!inhibitDelAngBiasStates) {
    1927   157324912 :         for (uint8_t i=10; i<=12; i++) P[i][i] = constrain_ftype(P[i][i],0.0f,sq(0.175 * dtEkfAvg));
    1928             :     } else {
    1929      440948 :         zeroStatesVarCov(10, 12);
    1930             :     }
    1931             : 
    1932             :     const ftype minSafeStateVar = 5E-9;
    1933    39772176 :     if (!inhibitDelVelBiasStates) {
    1934             : 
    1935             :         // Find the maximum delta velocity bias state variance and request a covariance reset if any variance is below the safe minimum
    1936             :         ftype maxStateVar = 0.0F;
    1937             :         bool resetRequired = false;
    1938   157324912 :         for (uint8_t stateIndex=13; stateIndex<=15; stateIndex++) {
    1939   117993684 :             if (P[stateIndex][stateIndex] > maxStateVar) {
    1940             :                 maxStateVar = P[stateIndex][stateIndex];
    1941    60168074 :             } else if (P[stateIndex][stateIndex] < minSafeStateVar) {
    1942             :                 resetRequired = true;
    1943             :             }
    1944             :         }
    1945             : 
    1946             :         // To ensure stability of the covariance matrix operations, the ratio of a max and min variance must
    1947             :         // not exceed 100 and the minimum variance must not fall below the target minimum
    1948    39331228 :         ftype minAllowedStateVar = fmaxF(0.01f * maxStateVar, minSafeStateVar);
    1949   157324912 :         for (uint8_t stateIndex=13; stateIndex<=15; stateIndex++) {
    1950   117993684 :             P[stateIndex][stateIndex] = constrain_ftype(P[stateIndex][stateIndex], minAllowedStateVar, sq(10.0f * dtEkfAvg));
    1951             :         }
    1952             : 
    1953             :         // If any one axis has fallen below the safe minimum, all delta velocity covariance terms must be reset to zero
    1954    39331228 :         if (resetRequired) {
    1955             :             // reset all delta velocity bias covariances
    1956           0 :             zeroStatesVarCov(13, 15);
    1957             :             // set all delta velocity bias variances to initial values and zero bias states
    1958           0 :             P[13][13] = sq(ACCEL_BIAS_LIM_SCALER * frontend->_accBiasLim * dtEkfAvg);
    1959           0 :             P[14][14] = P[13][13];
    1960           0 :             P[15][15] = P[13][13];
    1961           0 :             stateStruct.accel_bias.zero();
    1962             :         }
    1963             : 
    1964             :     } else {
    1965      440948 :         zeroStatesVarCov(13, 15);
    1966             :         // set all delta velocity bias variances to a margin above the minimum safe value
    1967     1763792 :         for (uint8_t i=0; i<=2; i++) {
    1968     1322844 :             const uint8_t stateIndex = i + 13;
    1969     1322844 :             P[stateIndex][stateIndex] = fmaxF(P[stateIndex][stateIndex], minSafeStateVar * 10.0F);
    1970             :         }
    1971             :     }
    1972             : 
    1973    39772176 :     if (!inhibitMagStates) {
    1974   102931204 :         for (uint8_t i=16; i<=18; i++) P[i][i] = constrain_ftype(P[i][i],0.0f,0.01f); // earth magnetic field
    1975   102931204 :         for (uint8_t i=19; i<=21; i++) P[i][i] = constrain_ftype(P[i][i],0.0f,0.01f); // body magnetic field
    1976             :     } else {
    1977    14039375 :         zeroStatesVarCov(16, 21);
    1978             :     }
    1979             : 
    1980    39772176 :     if (!inhibitWindStates) {
    1981     9313508 :         if (treatWindStatesAsTruth) {
    1982       85922 :             zeroStatesVarCov(22, 23);
    1983             :         } else {
    1984    27682758 :             for (uint8_t i=22; i<=23; i++) P[i][i] = constrain_ftype(P[i][i],0.0f,WIND_VEL_VARIANCE_MAX);
    1985             :         }
    1986             :     } else {
    1987    30458668 :         zeroStatesVarCov(22, 23);
    1988             :     }
    1989    39772176 : }
    1990             : 
    1991             : // actually do fusion to update statesArray from Kfusion and P from KHP.
    1992             : // returns true and skips fusion if variances would be driven negative.
    1993             : // force skips this negative check; passing true is probably a bug!
    1994    19806902 : bool NavEKF3_core::FinishFusion(ftype innov, bool force /*= false*/)
    1995             : {
    1996    19806902 :     if (!force) {
    1997             :         // Check that we are not going to drive any variances negative and skip the update if so
    1998   400613730 :         for (auto s=0; s<=stateIndexLim; s++) {
    1999   381966090 :             if (KHP[s][s] > P[s][s]) {
    2000             :                 return true;
    2001             :             }
    2002             :         }
    2003             :     }
    2004             : 
    2005             :     // correct the state vector using kalman gains filled in by caller
    2006   429595280 :     for (auto s=0; s<=stateIndexLim; s++) {
    2007   409788378 :         statesArray[s] -= Kfusion[s] * innov;
    2008             :     }
    2009    19806902 :     stateStruct.quat.normalize();
    2010             : 
    2011             :     // update the covariance matrix as P = P - KHP (KHP was filled by caller)
    2012   429595280 :     for (auto r=0; r<=stateIndexLim; r++) {
    2013  4965514005 :         for (auto c=0; c<=r; c++) {
    2014             :             // P must end up symmetric, so average the upper and lower
    2015             :             // differences, then store that result in both positions. it would
    2016             :             // be faster and more numerically stable to average the KHP entries
    2017             :             // instead, but we have no good proof P was symmetric before!
    2018  4555725627 :             const ftype lower = P[r][c] - KHP[r][c];
    2019  4555725627 :             const ftype upper = P[c][r] - KHP[c][r];
    2020  4555725627 :             const ftype res = 0.5f*(lower + upper);
    2021  4555725627 :             P[r][c] = res;
    2022  4555725627 :             P[c][r] = res;
    2023             :         }
    2024             :     }
    2025             : 
    2026             :     // limit the variances to prevent ill-conditioning
    2027    19806902 :     ConstrainVariances(); // can change statesArray!!
    2028             : 
    2029    19806902 :     return false;
    2030             : }
    2031             : 
    2032             : // constrain states using WMM tables and specified limit
    2033    22890049 : void NavEKF3_core::MagTableConstrain(void)
    2034             : {
    2035             :     // constrain to error from table earth field
    2036    22890049 :     ftype limit_ga = frontend->_mag_ef_limit * 0.001f;
    2037    22890049 :     stateStruct.earth_magfield.x = constrain_ftype(stateStruct.earth_magfield.x,
    2038             :                                                    table_earth_field_ga.x-limit_ga,
    2039             :                                                    table_earth_field_ga.x+limit_ga);
    2040    22890049 :     stateStruct.earth_magfield.y = constrain_ftype(stateStruct.earth_magfield.y,
    2041             :                                                    table_earth_field_ga.y-limit_ga,
    2042             :                                                    table_earth_field_ga.y+limit_ga);
    2043    22890049 :     stateStruct.earth_magfield.z = constrain_ftype(stateStruct.earth_magfield.z,
    2044             :                                                    table_earth_field_ga.z-limit_ga,
    2045             :                                                    table_earth_field_ga.z+limit_ga);
    2046    22890049 : }
    2047             : 
    2048             : // constrain states to prevent ill-conditioning
    2049    19965274 : void NavEKF3_core::ConstrainStates()
    2050             : {
    2051             :     // State constraints as of March 2025
    2052             :     // This table documents the limits applied to each EKF state.
    2053             :     // These are designed to keep state estimates within physically realistic bounds and prevent divergence.
    2054             :     // +---------------------------------------------------------------------------------------------------------+
    2055             :     // | State Index  |      State Name                 |  State Units  | State Constraint Range                 |
    2056             :     // +---------------------------------------------------------------------------------------------------------+
    2057             :     // |  0 .. 3      | Attitude Quaternion             | unitless      | [-1.0, 1.0]                            |
    2058             :     // |  4 .. 6      | Velocity (North, East, Down)    | m/s           | [-500, 500]                            |
    2059             :     // |  7 .. 8      | Position (North, East)          | m             | [-50e6,50e6]                           |
    2060             :     // |  9 (z)       | Position (Down / Altitude)      | m             | [-40000, 10000]                        |
    2061             :     // | 10 .. 12     | Gyro Bias (X, Y, Z)             | rad           | [-0.5, 0.5] * dtEkfAvg                 |
    2062             :     // | 13 .. 15     | Accel Bias (X, Y, Z)            | m/s²          | [-_accBiasLim, _accBiasLim] * dtEkfAvg |
    2063             :     // | 16 .. 18     | Earth Magnetic Field (X, Y, Z)  | Gauss         | [-1.0, 1.0]                            | or constrained by MagTableConstrain() if available
    2064             :     // | 19 .. 21     | Body Magnetic Field (X, Y, Z)   | Gauss         | [-0.5, 0.5]                            |
    2065             :     // | 22 .. 23     | Wind Velocity (North, East)     | m/s           | [-100, 100]                            |
    2066             :     // +---------------------------------------------------------------------------------------------------------+
    2067             : 
    2068             :     // quaternions are limited between +-1
    2069    99826370 :     for (uint8_t i=0; i<=3; i++) statesArray[i] = constrain_ftype(statesArray[i],-1.0f,1.0f);
    2070             :     // velocity limit 500 m/sec (could set this based on some multiple of max airspeed * EAS2TAS)
    2071    79861096 :     for (uint8_t i=4; i<=6; i++) statesArray[i] = constrain_ftype(statesArray[i],-5.0e2f,5.0e2f);
    2072             :     // position limit TODO apply circular limit
    2073    59895822 :     for (uint8_t i=7; i<=8; i++) statesArray[i] = constrain_ftype(statesArray[i],-EK3_POSXY_STATE_LIMIT,EK3_POSXY_STATE_LIMIT);
    2074             :     // height limit covers home alt on everest through to home alt at SL and balloon drop
    2075    19965274 :     stateStruct.position.z = constrain_ftype(stateStruct.position.z,-4.0e4f,1.0e4f);
    2076             :     // gyro bias limit (this needs to be set based on manufacturers specs)
    2077    19965274 :     const ftype gyro_bias_limit = getGyroBiasLimit();
    2078    79861096 :     for (uint8_t i=10; i<=12; i++) statesArray[i] = constrain_ftype(statesArray[i],-gyro_bias_limit*dtEkfAvg,gyro_bias_limit*dtEkfAvg);
    2079             :     // the accelerometer bias limit is controlled by a user adjustable parameter
    2080    79861096 :     for (uint8_t i=13; i<=15; i++) statesArray[i] = constrain_ftype(statesArray[i],-frontend->_accBiasLim*dtEkfAvg,frontend->_accBiasLim*dtEkfAvg);
    2081             :     // earth magnetic field limit
    2082    19965274 :     if (frontend->_mag_ef_limit <= 0 || !have_table_earth_field) {
    2083             :         // constrain to +/-1Ga
    2084     8270760 :         for (uint8_t i=16; i<=18; i++) statesArray[i] = constrain_ftype(statesArray[i],-1.0f,1.0f);
    2085             :     } else {
    2086             :         // use table constrain
    2087    17897584 :         MagTableConstrain();
    2088             :     }
    2089             :     // body magnetic field limit
    2090    79861096 :     for (uint8_t i=19; i<=21; i++) statesArray[i] = constrain_ftype(statesArray[i],-0.5f,0.5f);
    2091             :     // wind velocity limit 100 m/s (could be based on some multiple of max airspeed * EAS2TAS) - TODO apply circular limit
    2092    59895822 :     for (uint8_t i=22; i<=23; i++) statesArray[i] = constrain_ftype(statesArray[i],-100.0f,100.0f);
    2093             :     // constrain the terrain state to be below the vehicle height unless we are using terrain as the height datum
    2094    19965274 :     if (!inhibitGndState) {
    2095    19953064 :         terrainState = MAX(terrainState, stateStruct.position.z + rngOnGnd);
    2096             :     }
    2097    19965274 : }
    2098             : 
    2099             : // calculate the NED earth spin vector in rad/sec
    2100        4878 : void NavEKF3_core::calcEarthRateNED(Vector3F &omega, int32_t latitude) const
    2101             : {
    2102        4878 :     ftype lat_rad = radians(latitude*1.0e-7f);
    2103        4878 :     omega.x  = earthRate*cosF(lat_rad);
    2104        4878 :     omega.y  = 0;
    2105        4878 :     omega.z  = -earthRate*sinF(lat_rad);
    2106        4878 : }
    2107             : 
    2108             : // set yaw from a single magnetometer sample
    2109        4182 : void NavEKF3_core::setYawFromMag()
    2110             : {
    2111        4182 :     if (!use_compass()) {
    2112           0 :         return;
    2113             :     }
    2114             : 
    2115             :     // read the magnetometer data
    2116        4182 :     readMagData();
    2117             : 
    2118             :     // use best of either 312 or 321 rotation sequence when calculating yaw
    2119             :     rotationOrder order;
    2120        4182 :     bestRotationOrder(order);
    2121        4182 :     Vector3F eulerAngles;
    2122        4182 :     Matrix3F Tbn_zeroYaw;
    2123        4182 :     if (order == rotationOrder::TAIT_BRYAN_321) {
    2124             :         // rolled more than pitched so use 321 rotation order
    2125        2988 :         stateStruct.quat.to_euler(eulerAngles);
    2126        2988 :         Tbn_zeroYaw.from_euler(eulerAngles.x, eulerAngles.y, 0.0f);
    2127        1194 :     } else if (order == rotationOrder::TAIT_BRYAN_312) {
    2128             :         // pitched more than rolled so use 312 rotation order
    2129        1194 :         eulerAngles = stateStruct.quat.to_vector312();
    2130        1194 :         Tbn_zeroYaw.from_euler312(eulerAngles.x, eulerAngles.y, 0.0f);
    2131             :     } else {
    2132             :         // rotation order not supported
    2133             :         return;
    2134             :     }
    2135             : 
    2136        4182 :     Vector3F magMeasNED = Tbn_zeroYaw * magDataDelayed.mag;
    2137        4182 :     ftype yawAngMeasured = wrap_PI(-atan2F(magMeasNED.y, magMeasNED.x) + MagDeclination());
    2138             : 
    2139             :     // update quaternion states and covariances
    2140        4182 :     resetQuatStateYawOnly(yawAngMeasured, sq(MAX(frontend->_yawNoise, 1.0e-2f)), order);
    2141             : }
    2142             : 
    2143             : // update mag field states and associated variances using magnetomer and declination data
    2144        4205 : void NavEKF3_core::resetMagFieldStates()
    2145             : {
    2146             :     // Rotate Mag measurements into NED to set initial NED magnetic field states
    2147             : 
    2148             :     // update rotation matrix from body to NED frame
    2149        4205 :     stateStruct.quat.inverse().rotation_matrix(prevTnb);
    2150             : 
    2151        4205 :     if (have_table_earth_field && frontend->_mag_ef_limit > 0) {
    2152        1513 :         stateStruct.earth_magfield = table_earth_field_ga;
    2153             :     } else {
    2154        2692 :         stateStruct.earth_magfield = prevTnb.transposed() * magDataDelayed.mag;
    2155             :     }
    2156             : 
    2157             :     // set the NE earth magnetic field states using the published declination
    2158             :     // and set the corresponding variances and covariances
    2159        4205 :     alignMagStateDeclination();
    2160             : 
    2161             :     // set the remaining variances and covariances
    2162        4205 :     zeroStatesVarCov(18, 21);
    2163        4205 :     P[18][18] = sq(frontend->_magNoise);
    2164        4205 :     P[19][19] = P[18][18];
    2165        4205 :     P[20][20] = P[18][18];
    2166        4205 :     P[21][21] = P[18][18];
    2167             : 
    2168             :     // record the fact we have initialised the magnetic field states
    2169        4205 :     recordMagReset();
    2170        4205 : }
    2171             : 
    2172             : // calculate the tilt error variance
    2173    19972655 : void NavEKF3_core::calcTiltErrorVariance()
    2174             : {
    2175    19972655 :     const ftype &q0 = stateStruct.quat[0];
    2176    19972655 :     const ftype &q1 = stateStruct.quat[1];
    2177    19972655 :     const ftype &q2 = stateStruct.quat[2];
    2178    19972655 :     const ftype &q3 = stateStruct.quat[3];
    2179             : 
    2180             :     // equations generated by quaternion_error_propagation(): in derivation/generate_2.py
    2181             :     // only diagonals have been used
    2182    19972655 :     const ftype PS0 = q0*q1 + q2*q3;
    2183    19972655 :     const ftype PS1 = PS0*q1;
    2184    19972655 :     const ftype PS2 = sq(q0) - sq(q1) - sq(q2) + sq(q3);
    2185    19972655 :     const ftype PS3 = PS2*q0;
    2186    19972655 :     const ftype PS4 = PS0*q2;
    2187    19972655 :     const ftype PS5 = PS2*q3;
    2188    19972655 :     const ftype PS6 = PS0*q3;
    2189    19972655 :     const ftype PS7 = PS2*q2;
    2190    19972655 :     const ftype PS8 = PS0*q0;
    2191    19972655 :     const ftype PS9 = PS2*q1;
    2192    19972655 :     const ftype PS10 = q0*q2 - q1*q3;
    2193    19972655 :     const ftype PS11 = PS10*q2;
    2194    19972655 :     const ftype PS12 = PS10*q3;
    2195    19972655 :     const ftype PS13 = PS10*q0;
    2196    19972655 :     const ftype PS14 = PS10*q1;
    2197             : 
    2198    19972655 :     tiltErrorVariance  = 4*P[0][0]*sq(2*PS8 - PS9) + 4*P[1][1]*sq(2*PS1 + PS3) + 4*P[2][2]*sq(2*PS4 + PS5) + 4*P[3][3]*sq(-2*PS6 + PS7);
    2199    19972655 :     tiltErrorVariance += 4*P[0][0]*sq(2*PS13 - PS7) + 4*P[1][1]*sq(2*PS14 - PS5) + 4*P[2][2]*sq(2*PS11 + PS3) + 4*P[3][3]*sq(2*PS12 + PS9);
    2200    19972655 :     tiltErrorVariance += 16*P[0][0]*sq(PS14 - PS4) + 16*P[1][1]*sq(PS13 + PS6) + 16*P[2][2]*sq(-PS12 + PS8) + 16*P[3][3]*sq(PS1 + PS11);
    2201             : 
    2202    19972655 :     tiltErrorVariance = constrain_ftype(tiltErrorVariance, 0.0f, sq(radians(30.0f)));
    2203    19972655 : }
    2204             : 
    2205        4210 : void NavEKF3_core::bestRotationOrder(rotationOrder &order)
    2206             : {
    2207        4210 :     if (fabsF(prevTnb[2][0]) < fabsF(prevTnb[2][1])) {
    2208             :         // rolled more than pitched so use 321 sequence
    2209        2990 :         order = rotationOrder::TAIT_BRYAN_321;
    2210             :     } else {
    2211             :         // pitched more than rolled so use 312 sequence
    2212        1220 :         order = rotationOrder::TAIT_BRYAN_312;
    2213             :     }
    2214        4210 : }
    2215             : 
    2216             : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
    2217             : // calculate the tilt error variance using an alternative numerical difference technique
    2218             : // and log with value generated by NavEKF3_core::calcTiltErrorVariance()
    2219    19965274 : void NavEKF3_core::verifyTiltErrorVariance()
    2220             : {
    2221             : #if HAL_LOGGING_ENABLED
    2222    19965274 :     const Vector3f gravity_ef = Vector3f(0.0f,0.0f,1.0f);
    2223    19965274 :     Matrix3f Tnb;
    2224             :     const float quat_delta = 0.001f;
    2225    19965274 :     float tiltErrorVarianceAlt = 0.0f;
    2226    99826370 :     for (uint8_t index = 0; index<4; index++) {
    2227    79861096 :         QuaternionF quat = stateStruct.quat;
    2228             : 
    2229             :         // Add a positive increment to the quaternion element and calculate the tilt error vector
    2230    79861096 :         quat[index] = stateStruct.quat[index] + quat_delta;
    2231    79861096 :         quat.inverse().rotation_matrix(Tnb);
    2232    79861096 :         const Vector3f gravity_bf_plus = Tnb * gravity_ef;
    2233             : 
    2234             :         // Add a negative increment to the quaternion element and calculate the tilt error vector
    2235    79861096 :         quat[index] = stateStruct.quat[index] - quat_delta;
    2236    79861096 :         quat.inverse().rotation_matrix(Tnb);
    2237    79861096 :         const Vector3f gravity_bf_minus = Tnb * gravity_ef;
    2238             : 
    2239             :         // calculate the angular difference between the two vectors using a small angle assumption
    2240    79861096 :         const Vector3f tilt_diff_vec = gravity_bf_minus % gravity_bf_plus;
    2241             : 
    2242             :         // calculate the partial derivative of angle error wrt the quaternion element
    2243    79861096 :         const float tilt_error_derivative = tilt_diff_vec.length() / (2.0f * quat_delta);
    2244             : 
    2245             :         // sum the contribution of the quaternion elemnent variance to the tilt angle error variance
    2246    79861096 :         tiltErrorVarianceAlt += P[index][index] * sq(tilt_error_derivative);
    2247             :     }
    2248             : 
    2249    19965274 :     tiltErrorVarianceAlt = MIN(tiltErrorVarianceAlt, sq(radians(30.0f)));
    2250    19965274 :     if (imuSampleTime_ms - lastLogTime_ms > 500) {
    2251      551396 :         lastLogTime_ms = imuSampleTime_ms;
    2252      551396 :         const struct log_XKTV msg {
    2253             :             LOG_PACKET_HEADER_INIT(LOG_XKTV_MSG),
    2254      551396 :             time_us      : dal.micros64(),
    2255      551396 :             core         : core_index,
    2256      551396 :             tvs          : float(tiltErrorVariance),
    2257             :             tvd          : float(tiltErrorVarianceAlt),
    2258      551396 :         };
    2259      551396 :         AP::logger().WriteBlock(&msg, sizeof(msg));
    2260             :     }
    2261             : #endif  // HAL_LOGGING_ENABLED
    2262    19965274 : }
    2263             : #endif
    2264             : 
    2265             : /*
    2266             :   move the EKF origin to the current position at 1Hz. The public_origin doesn't move.
    2267             :   By moving the EKF origin we keep the distortion due to spherical
    2268             :   shape of the earth to a minimum.
    2269             :  */
    2270      280584 : void NavEKF3_core::moveEKFOrigin(void)
    2271             : {
    2272             :     // only move origin when we have a origin and we're using GPS
    2273      280584 :     if (!frontend->common_origin_valid || !filterStatus.flags.using_gps) {
    2274       64806 :         return;
    2275             :     }
    2276             : 
    2277             :     // move the origin to the current state location
    2278      215778 :     Location loc = EKF_origin;
    2279      215778 :     loc.offset(stateStruct.position.x, stateStruct.position.y);
    2280      215778 :     const Vector2F diffNE = loc.get_distance_NE_ftype(EKF_origin);
    2281      215778 :     EKF_origin = loc;
    2282             : 
    2283             :     // now fix all output states
    2284      215778 :     stateStruct.position.xy() += diffNE;
    2285      215778 :     outputDataNew.position.xy() += diffNE;
    2286      215778 :     outputDataDelayed.position.xy() += diffNE;
    2287             : 
    2288     2166343 :     for (unsigned index=0; index < imu_buffer_length; index++) {
    2289     1950565 :         storedOutput[index].position.xy() += diffNE;
    2290             :     }
    2291             : }

Generated by: LCOV version 1.14