LCOV - code coverage report
Current view: top level - AP_NavEKF3 - AP_NavEKF3_VehicleStatus.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 178 191 93.2 %
Date: 2026-09-09 11:02:08 Functions: 5 5 100.0 %

          Line data    Source code
       1             : #include "AP_NavEKF3_core.h"
       2             : 
       3             : #include "AP_NavEKF3.h"
       4             : 
       5             : #include <AP_DAL/AP_DAL.h>
       6             : 
       7             : /* Monitor GPS data to see if quality is good enough to initialise the EKF
       8             :    Monitor magnetometer innovations to see if the heading is good enough to use GPS
       9             :    Return true if all criteria pass for 10 seconds
      10             : 
      11             :    We also record the failure reason so that pre_arm_check()
      12             :    can give a good report to the user on why arming is failing
      13             : */
      14     1346446 : void NavEKF3_core::calcGpsGoodToAlign(void)
      15             : {
      16     1346446 :     if (inFlight && !finalInflightYawInit && assume_zero_sideslip() && !use_compass()) {
      17             :         // this is a special case where a plane has launched without magnetometer
      18             :         // is now in the air and needs to align yaw to the GPS and start navigating as soon as possible
      19           0 :         gpsGoodToAlign = true;
      20           0 :         return;
      21             :     }
      22             : 
      23             :     // User defined multiplier to be applied to check thresholds
      24     1346446 :     ftype checkScaler = 0.01f*(ftype)frontend->_gpsCheckScaler;
      25             : 
      26     1346446 :     if (gpsGoodToAlign) {
      27             :         /*
      28             :           if we have already passed GPS alignment checks then raise
      29             :           the check threshold so that we have some hysterisis and
      30             :           don't continuously change from able to arm to not able to
      31             :           arm
      32             :          */
      33     1227730 :         checkScaler *= 1.3f;
      34             :     }
      35             :     
      36             :     // If we have good magnetometer consistency and bad innovations for longer than 5 seconds then we reset heading and field states
      37             :     // This enables us to handle large changes to the external magnetic field environment that occur before arming
      38     1346446 :     if ((magTestRatio.x <= 1.0f && magTestRatio.y <= 1.0f && yawTestRatio <= 1.0f) || !consistentMagData) {
      39     1345349 :         magYawResetTimer_ms = imuSampleTime_ms;
      40             :     }
      41     1346446 :     if ((imuSampleTime_ms - magYawResetTimer_ms > 5000) && !motorsArmed) {
      42             :         // request reset of heading and magnetic field states
      43           8 :         magYawResetRequest = true;
      44             :         // reset timer to ensure that bad magnetometer data cannot cause a heading reset more often than every 5 seconds
      45           8 :         magYawResetTimer_ms = imuSampleTime_ms;
      46             :     }
      47             : 
      48             :     // Check for significant change in GPS position if disarmed which indicates bad GPS
      49             :     // This check can only be used when the vehicle is stationary
      50     1346446 :     const auto &gps = dal.gps();
      51             : 
      52     1346446 :     const Location &gpsloc = gps.location(preferred_gps); // Current location
      53             :     const ftype posFiltTimeConst = 10.0; // time constant used to decay position drift
      54             :     // calculate time lapsed since last update and limit to prevent numerical errors
      55     1346446 :     ftype deltaTime = constrain_ftype(ftype(imuDataDelayed.time_ms - lastPreAlignGpsCheckTime_ms)*0.001f,0.01f,posFiltTimeConst);
      56     1346446 :     lastPreAlignGpsCheckTime_ms = imuDataDelayed.time_ms;
      57             :     // Sum distance moved
      58     1346446 :     gpsDriftNE += gpsloc_prev.get_distance(gpsloc);
      59     1346446 :     gpsloc_prev = gpsloc;
      60             :     // Decay distance moved exponentially to zero
      61     1346446 :     gpsDriftNE *= (1.0f - deltaTime/posFiltTimeConst);
      62             :     // Clamp the filter state to prevent excessive persistence of large transients
      63     1346446 :     gpsDriftNE = MIN(gpsDriftNE,10.0f);
      64             :     // Fail if more than 3 metres drift after filtering whilst on-ground
      65             :     // This corresponds to a maximum acceptable average drift rate of 0.3 m/s or single glitch event of 3m
      66     1346446 :     bool gpsDriftFail = (gpsDriftNE > 3.0f*checkScaler) && onGround && (frontend->_gpsCheck & MASK_GPS_POS_DRIFT);
      67             : 
      68             :     // Report check result as a text string and bitmask
      69             :     if (gpsDriftFail) {
      70           0 :         dal.snprintf(prearm_fail_string,
      71             :                            sizeof(prearm_fail_string),
      72           0 :                            "GPS drift %.1fm (needs %.1f)", (double)gpsDriftNE, (double)(3.0f*checkScaler));
      73           0 :         gpsCheckStatus.bad_horiz_drift = true;
      74             :     } else {
      75     1346446 :         gpsCheckStatus.bad_horiz_drift = false;
      76             :     }
      77             : 
      78             :     // Check that the vertical GPS vertical velocity is reasonable after noise filtering
      79             :     bool gpsVertVelFail;
      80     1346446 :     if (gpsDataNew.have_vz && onGround) {
      81             :         // check that the average vertical GPS velocity is close to zero
      82      377907 :         gpsVertVelFilt = 0.1f * gpsDataNew.vel.z + 0.9f * gpsVertVelFilt;
      83      377907 :         gpsVertVelFilt = constrain_ftype(gpsVertVelFilt,-10.0f,10.0f);
      84      377907 :         gpsVertVelFail = (fabsF(gpsVertVelFilt) > 0.3f*checkScaler) && (frontend->_gpsCheck & MASK_GPS_VERT_SPD);
      85             :     } else {
      86             :         gpsVertVelFail = false;
      87             :     }
      88             : 
      89             :     // Report check result as a text string and bitmask
      90             :     if (gpsVertVelFail) {
      91           0 :         dal.snprintf(prearm_fail_string,
      92             :                            sizeof(prearm_fail_string),
      93           0 :                            "GPS vertical speed %.2fm/s (needs %.2f)", (double)fabsF(gpsVertVelFilt), (double)(0.3f*checkScaler));
      94           0 :         gpsCheckStatus.bad_vert_vel = true;
      95             :     } else {
      96     1346446 :         gpsCheckStatus.bad_vert_vel = false;
      97             :     }
      98             : 
      99             :     // Check that the horizontal GPS vertical velocity is reasonable after noise filtering
     100             :     // This check can only be used if the vehicle is stationary
     101             :     bool gpsHorizVelFail;
     102     1346446 :     if (onGround) {
     103      378553 :         gpsHorizVelFilt = 0.1f * gpsDataDelayed.vel.xy().length() + 0.9f * gpsHorizVelFilt;
     104      378553 :         gpsHorizVelFilt = constrain_ftype(gpsHorizVelFilt,-10.0f,10.0f);
     105      378553 :         gpsHorizVelFail = (fabsF(gpsHorizVelFilt) > 0.3f*checkScaler) && (frontend->_gpsCheck & MASK_GPS_HORIZ_SPD);
     106             :     } else {
     107             :         gpsHorizVelFail = false;
     108             :     }
     109             : 
     110             :     // Report check result as a text string and bitmask
     111             :     if (gpsHorizVelFail) {
     112           0 :         dal.snprintf(prearm_fail_string,
     113             :                            sizeof(prearm_fail_string),
     114           0 :                            "GPS horizontal speed %.2fm/s (needs %.2f)", (double)gpsDriftNE, (double)(0.3f*checkScaler));
     115           0 :         gpsCheckStatus.bad_horiz_vel = true;
     116             :     } else {
     117     1346446 :         gpsCheckStatus.bad_horiz_vel = false;
     118             :     }
     119             : 
     120             :     // fail if horiziontal position accuracy not sufficient
     121     1346446 :     float hAcc = 0.0f;
     122             :     bool hAccFail;
     123     1346446 :     if (gps.horizontal_accuracy(preferred_gps, hAcc)) {
     124     1345660 :         hAccFail = (hAcc > 5.0f*checkScaler)  && (frontend->_gpsCheck & MASK_GPS_POS_ERR);
     125             :     } else {
     126             :         hAccFail =  false;
     127             :     }
     128             : 
     129             :     // Report check result as a text string and bitmask
     130             :     if (hAccFail) {
     131         636 :         dal.snprintf(prearm_fail_string,
     132             :                            sizeof(prearm_fail_string),
     133             :                            "GPS horiz error %.1fm (needs %.1f)", (double)hAcc, (double)(5.0f*checkScaler));
     134         636 :         gpsCheckStatus.bad_hAcc = true;
     135             :     } else {
     136     1345810 :         gpsCheckStatus.bad_hAcc = false;
     137             :     }
     138             : 
     139             : 
     140             :     // Check for vertical GPS accuracy
     141     1346446 :     float vAcc = 0.0f;
     142             :     bool vAccFail = false;
     143     1346446 :     if (gps.vertical_accuracy(preferred_gps, vAcc)) {
     144     1345660 :         vAccFail = (vAcc > 7.5f * checkScaler)  && (frontend->_gpsCheck & MASK_GPS_POS_ERR);
     145             :     }
     146             :     // Report check result as a text string and bitmask
     147             :     if (vAccFail) {
     148         634 :         dal.snprintf(prearm_fail_string,
     149             :                            sizeof(prearm_fail_string),
     150             :                            "GPS vert error %.1fm (needs < %.1f)", (double)vAcc, (double)(7.5f * checkScaler));
     151         634 :         gpsCheckStatus.bad_vAcc = true;
     152             :     } else {
     153     1345812 :         gpsCheckStatus.bad_vAcc = false;
     154             :     }
     155             : 
     156             :     // fail if reported speed accuracy greater than threshold
     157     1346446 :     bool gpsSpdAccFail = (gpsSpdAccuracy > 1.0f*checkScaler) && (frontend->_gpsCheck & MASK_GPS_SPD_ERR);
     158             : 
     159             :     // Report check result as a text string and bitmask
     160             :     if (gpsSpdAccFail) {
     161         646 :         dal.snprintf(prearm_fail_string,
     162             :                            sizeof(prearm_fail_string),
     163         646 :                            "GPS speed error %.1f (needs < %.1f)", (double)gpsSpdAccuracy, (double)(1.0f*checkScaler));
     164         646 :         gpsCheckStatus.bad_sAcc = true;
     165             :     } else {
     166     1345800 :         gpsCheckStatus.bad_sAcc = false;
     167             :     }
     168             : 
     169             :     // fail if satellite geometry is poor
     170     1346446 :     bool hdopFail = (gps.get_hdop(preferred_gps) > 250)  && (frontend->_gpsCheck & MASK_GPS_HDOP);
     171             : 
     172             :     // Report check result as a text string and bitmask
     173             :     if (hdopFail) {
     174           1 :         dal.snprintf(prearm_fail_string, sizeof(prearm_fail_string),
     175           1 :                            "GPS HDOP %.1f (needs 2.5)", (double)(0.01f * gps.get_hdop(preferred_gps)));
     176           1 :         gpsCheckStatus.bad_hdop = true;
     177             :     } else {
     178     1346445 :         gpsCheckStatus.bad_hdop = false;
     179             :     }
     180             : 
     181             :     // fail if not enough sats
     182     1346446 :     bool numSatsFail = (gps.num_sats(preferred_gps) < 6) && (frontend->_gpsCheck & MASK_GPS_NSATS);
     183             : 
     184             :     // Report check result as a text string and bitmask
     185             :     if (numSatsFail) {
     186         992 :         dal.snprintf(prearm_fail_string, sizeof(prearm_fail_string),
     187         992 :                            "GPS numsats %u (needs 6)", gps.num_sats(preferred_gps));
     188         992 :         gpsCheckStatus.bad_sats = true;
     189             :     } else {
     190     1345454 :         gpsCheckStatus.bad_sats = false;
     191             :     }
     192             : 
     193             :     // fail if magnetometer innovations are outside limits indicating bad yaw
     194             :     // with bad yaw we are unable to use GPS
     195             :     bool yawFail;
     196     1346446 :     if ((magTestRatio.x > 1.0f || magTestRatio.y > 1.0f || yawTestRatio > 1.0f) && (frontend->_gpsCheck & MASK_GPS_YAW_ERR)) {
     197             :         yawFail = true;
     198             :     } else {
     199             :         yawFail = false;
     200             :     }
     201             : 
     202             :     // Report check result as a text string and bitmask
     203             :     if (yawFail) {
     204       14664 :         dal.snprintf(prearm_fail_string,
     205             :                            sizeof(prearm_fail_string),
     206             :                            "Mag yaw error x=%.1f y=%.1f",
     207       14664 :                            (double)magTestRatio.x,
     208       14664 :                            (double)magTestRatio.y);
     209       14664 :         gpsCheckStatus.bad_yaw = true;
     210             :     } else {
     211     1331782 :         gpsCheckStatus.bad_yaw = false;
     212             :     }
     213             : 
     214             :     // assume failed first time through and notify user checks have started
     215     1346446 :     if (lastGpsVelFail_ms == 0) {
     216        2786 :         dal.snprintf(prearm_fail_string, sizeof(prearm_fail_string), "EKF starting GPS checks");
     217        2786 :         lastGpsVelFail_ms = imuSampleTime_ms;
     218             :     }
     219             : 
     220             :     // record time of pass or fail
     221     1346446 :     if (gpsSpdAccFail || numSatsFail || hdopFail || hAccFail || vAccFail || yawFail || gpsDriftFail || gpsVertVelFail || gpsHorizVelFail) {
     222       16451 :         lastGpsVelFail_ms = imuSampleTime_ms;
     223             :     } else {
     224     1329995 :         lastGpsVelPass_ms = imuSampleTime_ms;
     225             :     }
     226             : 
     227             :     // continuous period of 10s without fail required to set healthy
     228             :     // continuous period of 5s without pass required to set unhealthy
     229     1346446 :     if (!gpsGoodToAlign && imuSampleTime_ms - lastGpsVelFail_ms > 10000) {
     230        1726 :         gpsGoodToAlign = true;
     231     1344720 :     } else if (gpsGoodToAlign && imuSampleTime_ms - lastGpsVelPass_ms > 5000) {
     232          27 :         gpsGoodToAlign = false;
     233             :     }
     234             : 
     235     1346446 :     if (gpsGoodToAlign && waitingForGpsChecks) {
     236          12 :         waitingForGpsChecks = false;
     237             :     }
     238             : }
     239             : 
     240             : // update inflight calculaton that determines if GPS data is good enough for reliable navigation
     241     1346446 : void NavEKF3_core::calcGpsGoodForFlight(void)
     242             : {
     243             :     // use simple criteria based on the GPS receiver's claimed vertical
     244             :     // position accuracy and speed accuracy and the EKF innovation consistency
     245             :     // checks
     246             : 
     247             :     // set up variables and constants used by filter that is applied to GPS speed accuracy
     248             :     const ftype alpha1 = 0.2f; // coefficient for first stage LPF applied to raw speed accuracy data
     249             :     const ftype tau = 10.0f; // time constant (sec) of peak hold decay
     250     1346446 :     if (lastGpsCheckTime_ms == 0) {
     251        2794 :         lastGpsCheckTime_ms =  imuSampleTime_ms;
     252             :     }
     253     1346446 :     ftype dtLPF = (imuSampleTime_ms - lastGpsCheckTime_ms) * 1e-3f;
     254     1346446 :     lastGpsCheckTime_ms = imuSampleTime_ms;
     255     1346446 :     ftype alpha2 = constrain_ftype(dtLPF/tau,0.0f,1.0f);
     256             : 
     257             :     // get the receivers reported speed accuracy
     258             :     float gpsSpdAccRaw;
     259     1346446 :     if (!dal.gps().speed_accuracy(preferred_gps, gpsSpdAccRaw)) {
     260        2943 :         gpsSpdAccRaw = 0.0f;
     261             :     }
     262             : 
     263             :     // filter the raw speed accuracy using a LPF
     264     1346446 :     sAccFilterState1 = constrain_ftype((alpha1 * gpsSpdAccRaw + (1.0f - alpha1) * sAccFilterState1),0.0f,10.0f);
     265             : 
     266             :     // apply a peak hold filter to the LPF output
     267     1346446 :     sAccFilterState2 = MAX(sAccFilterState1,((1.0f - alpha2) * sAccFilterState2));
     268             : 
     269             :     // Apply a threshold test with hysteresis to the filtered GPS speed accuracy data
     270     1346446 :     if (sAccFilterState2 > 1.5f ) {
     271         138 :         gpsSpdAccPass = false;
     272     1346308 :     } else if(sAccFilterState2 < 1.0f) {
     273     1346216 :         gpsSpdAccPass = true;
     274             :     }
     275             : 
     276             :     // Apply a threshold test with hysteresis to the GPS vertical position accuracy data
     277             :     // Require a fail for one second and a pass for 3 seconds to transition
     278             :     float gpsVAccRaw;
     279     1346446 :     ftype gpsVAccThreshold = (ftype)frontend->_gpsVAccThreshold;
     280     1346446 :     if (lastGpsVertAccFailTime_ms == 0) {
     281        2794 :         lastGpsVertAccFailTime_ms = imuSampleTime_ms;
     282        2794 :         lastGpsVertAccPassTime_ms = imuSampleTime_ms;
     283             :     }
     284     1346446 :     if (!dal.gps().vertical_accuracy(preferred_gps, gpsVAccRaw)) {
     285             :         // No vertical accuracy data yet, let's treat it as a value above the threshold
     286         786 :         gpsVAccRaw = gpsVAccThreshold + 1.0f;
     287             :     }
     288     1346446 :     if (gpsVAccThreshold <= 0 || gpsVAccRaw < gpsVAccThreshold) {
     289     1346446 :         lastGpsVertAccPassTime_ms = imuSampleTime_ms;
     290             :     } else {
     291           0 :         lastGpsVertAccFailTime_ms = imuSampleTime_ms;
     292             :     }
     293     1346446 :     if ((imuSampleTime_ms - lastGpsVertAccPassTime_ms) > 1000) {
     294           0 :         gpsVertAccPass = false;
     295     1346446 :     } else if ((imuSampleTime_ms - lastGpsVertAccFailTime_ms) > 3000) {
     296     1310811 :         gpsVertAccPass = true;
     297             :     }
     298             : 
     299             :     // Apply a threshold test with hysteresis to the normalised position and velocity innovations
     300             :     // Require a fail for one second and a pass for 10 seconds to transition
     301     1346446 :     if (lastGpsInnovFailTime_ms == 0) {
     302        2794 :         lastGpsInnovFailTime_ms = imuSampleTime_ms;
     303        2794 :         lastGpsInnovPassTime_ms = imuSampleTime_ms;
     304             :     }
     305     1346446 :     if (velTestRatio < 1.0f && posTestRatio < 1.0f) {
     306     1344145 :         lastGpsInnovPassTime_ms = imuSampleTime_ms;
     307        2301 :     } else if (velTestRatio > 0.7f || posTestRatio > 0.7f) {
     308        2301 :         lastGpsInnovFailTime_ms = imuSampleTime_ms;
     309             :     }
     310     1346446 :     if ((imuSampleTime_ms - lastGpsInnovPassTime_ms) > 1000) {
     311        1378 :         ekfInnovationsPass = false;
     312     1345068 :     } else if ((imuSampleTime_ms - lastGpsInnovFailTime_ms) > 10000) {
     313     1241673 :         ekfInnovationsPass = true;
     314             :     }
     315             : 
     316             :     // both GPS speed accuracy and EKF innovations must pass
     317     1346446 :     gpsAccuracyGood = gpsSpdAccPass && ekfInnovationsPass;
     318             : 
     319             :     // also update whether the GPS fix is good enough for altitude
     320     1346446 :     gpsAccuracyGoodForAltitude = gpsAccuracyGood && gpsVertAccPass;
     321     1346446 : }
     322             : 
     323             : // Detect if we are in flight or on ground
     324    78317950 : void NavEKF3_core::detectFlight()
     325             : {
     326             :     /*
     327             :         If we are a fly forward type vehicle (eg plane), then in-air status can be determined through a combination of speed and height criteria.
     328             :         Because of the differing certainty requirements of algorithms that need the in-flight / on-ground status we use two booleans where
     329             :         onGround indicates a high certainty we are not flying and inFlight indicates a high certainty we are flying. It is possible for
     330             :         both onGround and inFlight to be false if the status is uncertain, but they cannot both be true.
     331             : 
     332             :         If we are a plane as indicated by the assume_zero_sideslip() status, then different logic is used
     333             : 
     334             :         TODO - this logic should be moved out of the EKF and into the flight vehicle code.
     335             :     */
     336             : 
     337    78317950 :     if (assume_zero_sideslip()) {
     338             :         // To be confident we are in the air we use a criteria which combines arm status, ground speed, airspeed and height change
     339     8963266 :         ftype gndSpdSq = sq(gpsDataNew.vel.x) + sq(gpsDataNew.vel.y);
     340             :         bool highGndSpd = false;
     341             :         bool highAirSpd = false;
     342             :         bool largeHgtChange = false;
     343             : 
     344             :         // trigger at 8 m/s airspeed
     345     8963266 :         const auto *arsp = dal.airspeed();
     346     8963266 :         if (arsp && arsp->healthy(selected_airspeed) && arsp->use(selected_airspeed)) {
     347     8802548 :             if (arsp->get_airspeed(selected_airspeed) * dal.get_EAS2TAS() > 10.0f) {
     348             :                 highAirSpd = true;
     349             :             }
     350             :         }
     351             : 
     352             :         // trigger on ground speed
     353             :         const ftype gndSpdThresholdSq = sq(5.0f);
     354     8963266 :         if (gndSpdSq > gndSpdThresholdSq + sq(gpsSpdAccuracy)) {
     355             :             highGndSpd = true;
     356             :         }
     357             : 
     358             :         // trigger if more than 10m away from initial height
     359     8963266 :         if (fabsF(hgtMea) > 10.0f) {
     360             :             largeHgtChange = true;
     361             :         }
     362             : 
     363     8963266 :         if (motorsArmed) {
     364     7065683 :             onGround = false;
     365     7065683 :             if (highGndSpd && (dal.get_takeoff_expected() || highAirSpd || largeHgtChange)) {
     366             :                 // to a high certainty we are flying
     367     6676427 :                 inFlight = true;
     368             :             }
     369             :         } else {
     370             :             // to a high certainty we are not flying
     371     1897583 :             onGround = true;
     372     1897583 :             inFlight = false;
     373             :         }
     374             : 
     375             :     } else {
     376             :         // Non fly forward vehicle, so can only use height and motor arm status
     377             : 
     378             :         // If the motors are armed then we could be flying and if they are not armed then we are definitely not flying
     379    69354684 :         if (motorsArmed) {
     380    46438793 :             onGround = false;
     381             :         } else {
     382    22915891 :             inFlight = false;
     383    22915891 :             onGround = true;
     384             :         }
     385             : 
     386    69354684 :         if (!onGround) {
     387             : #if APM_BUILD_TYPE(APM_BUILD_ArduSub)
     388             :             // If depth has increased since arming, then we definitely are diving
     389     2472976 :             if ((stateStruct.position.z - posDownAtTakeoff) > 1.5f) {
     390     1788233 :                 inFlight = true;
     391             :             }
     392             : 
     393             :             // If rangefinder has decreased since arming, then we definitely are diving
     394     2472976 :             if ((rangeDataNew.rng - rngAtStartOfFlight) < -0.5f) {
     395      661050 :                 inFlight = true;
     396             :             }
     397             : #else
     398             :             // If height has increased since exiting on-ground, then we definitely are flying
     399    43965817 :             if ((stateStruct.position.z - posDownAtTakeoff) < -1.5f) {
     400    37629089 :                 inFlight = true;
     401             :             }
     402             : 
     403             :             // If rangefinder has increased since exiting on-ground, then we definitely are flying
     404    43965817 :             if ((rangeDataNew.rng - rngAtStartOfFlight) > 0.5f) {
     405     2406501 :                 inFlight = true;
     406             :             }
     407             : #endif
     408             : 
     409             :             // If more than 5 seconds since likely_flying was set
     410             :             // true, then set inFlight true
     411    46438793 :             if (dal.get_time_flying_ms() > 5000) {
     412    41235526 :                 inFlight = true;
     413             :             }
     414             :         }
     415             : 
     416             :     }
     417             : 
     418             :     // Store vehicle height and range prior to takeoff for use in post takeoff checks
     419    78317950 :     if (onGround) {
     420             :         // store vertical position at start of flight to use as a reference for ground relative checks
     421    24813474 :         posDownAtTakeoff = stateStruct.position.z;
     422             :         // store the range finder measurement which will be used as a reference to detect when we have taken off
     423    24813474 :         rngAtStartOfFlight = rangeDataNew.rng;
     424             :         // if the magnetic field states have been set, then continue to update the vertical position
     425             :         // quaternion and yaw innovation snapshots to use as a reference when we start to fly.
     426    24813474 :         if (magStateInitComplete) {
     427    23389908 :             posDownAtLastMagReset = stateStruct.position.z;
     428    23389908 :             quatAtLastMagReset = stateStruct.quat;
     429    23389908 :             yawInnovAtLastMagReset = innovYaw;
     430             :         }
     431             :     }
     432             : 
     433             :     // handle reset of counters used to control how many times we will try to reset the yaw to the EKF-GSF value per flight
     434    78317950 :     if ((!prevOnGround && onGround) || !gpsSpdAccPass) {
     435             :         // disable filter bank
     436     3849087 :         EKFGSF_run_filterbank = false;
     437    74468863 :     } else if (yawEstimator != nullptr && !EKFGSF_run_filterbank && (inFlight || dal.get_takeoff_expected()) && gpsSpdAccPass) {
     438             :         // flying or about to fly so reset counters and enable filter bank when GPS is good
     439        2229 :         EKFGSF_yaw_reset_ms = 0;
     440        2229 :         EKFGSF_yaw_reset_request_ms = 0;
     441        2229 :         EKFGSF_yaw_reset_count = 0;
     442        2229 :         EKFGSF_yaw_valid_count = 0;
     443        2229 :         EKFGSF_run_filterbank = true;
     444        2229 :         Vector3f gyroBias;
     445        2229 :         getGyroBias(gyroBias);
     446        2229 :         yawEstimator->setGyroBias(gyroBias);
     447             :     }
     448             : 
     449             :     // store current on-ground  and in-air status for next time
     450    78317950 :     prevOnGround = onGround;
     451    78317950 :     prevInFlight = inFlight;
     452             : 
     453    78317950 : }
     454             : 
     455             : // Set to true if the terrain underneath is stable enough to be used as a height reference
     456             : // in combination with a range finder. Set to false if the terrain underneath the vehicle
     457             : // cannot be used as a height reference. Use to prevent range finder operation otherwise
     458             : // enabled by the combination of EK3_RNG_USE_HGT and EK3_RNG_USE_SPD parameters.
     459        2559 : void NavEKF3_core::setTerrainHgtStable(bool val)
     460             : {
     461        2559 :     terrainHgtStable = val;
     462        2559 : }
     463             : 
     464             : #if EK3_FEATURE_OPTFLOW_FUSION
     465             : // Detect takeoff for optical flow navigation
     466       19976 : void NavEKF3_core::detectOptFlowTakeoff(void)
     467             : {
     468       19976 :     if (!onGround && !takeOffDetected && (imuSampleTime_ms - timeAtArming_ms) > 1000) {
     469             :         // we are no longer confidently on the ground so check the range finder and gyro for signs of takeoff
     470        1128 :         const auto &ins = dal.ins();
     471        1128 :         Vector3f angRateVec;
     472        1128 :         Vector3f gyroBias;
     473        1128 :         getGyroBias(gyroBias);
     474        1128 :         angRateVec = ins.get_gyro(gyro_index_active) - gyroBias;
     475             : 
     476        1128 :         takeOffDetected = (takeOffDetected || (angRateVec.length() > 0.1f) || (rangeDataNew.rng > (rngAtStartOfFlight + 0.1f)));
     477       19976 :     } else if (onGround) {
     478             :         // we are confidently on the ground so set the takeoff detected status to false
     479        5786 :         takeOffDetected = false;
     480             :     }
     481       19976 : }
     482             : #endif  // EK3_FEATURE_OPTFLOW_FUSION

Generated by: LCOV version 1.14