LCOV - code coverage report
Current view: top level - AP_NavEKF2 - AP_NavEKF2_Control.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 245 279 87.8 %
Date: 2026-09-09 11:02:08 Functions: 20 21 95.2 %

          Line data    Source code
       1             : #include "AP_NavEKF2_core.h"
       2             : 
       3             : #include <GCS_MAVLink/GCS.h>
       4             : #include <AP_DAL/AP_DAL.h>
       5             : 
       6             : #include "AP_NavEKF2.h"
       7             : 
       8             : // Control filter mode transitions
       9     4213784 : void NavEKF2_core::controlFilterModes()
      10             : {
      11             :     // Determine motor arm status
      12     4213784 :     prevMotorsArmed = motorsArmed;
      13     4213784 :     motorsArmed = dal.get_armed();
      14     4213784 :     if (motorsArmed && !prevMotorsArmed) {
      15             :         // set the time at which we arm to assist with checks
      16         388 :         timeAtArming_ms =  imuSampleTime_ms;
      17             :     }
      18             : 
      19             :     // Detect if we are in flight on or ground
      20     4213784 :     detectFlight();
      21             : 
      22             :     // Determine if learning of wind and magnetic field will be enabled and set corresponding indexing limits to
      23             :     // avoid unnecessary operations
      24     4213784 :     setWindMagStateLearningMode();
      25             : 
      26             :     // Check the alignmnent status of the tilt and yaw attitude
      27             :     // Used during initial bootstrap alignment of the filter
      28     4213784 :     checkAttitudeAlignmentStatus();
      29             : 
      30             :     // Set the type of inertial navigation aiding used
      31     4213784 :     setAidingMode();
      32             : 
      33     4213784 : }
      34             : 
      35             : /*
      36             :   return effective value for _magCal for this core
      37             :  */
      38     4213784 : uint8_t NavEKF2_core::effective_magCal(void) const
      39             : {
      40             :     // force use of simple magnetic heading fusion for specified cores
      41     4213784 :     if (frontend->_magMask & core_index) {
      42             :         return 2;
      43             :     } else {
      44     4213784 :         return frontend->_magCal;
      45             :     }
      46             : }
      47             : 
      48             : // Determine if learning of wind and magnetic field will be enabled and set corresponding indexing limits to
      49             : // avoid unnecessary operations
      50     4213784 : void NavEKF2_core::setWindMagStateLearningMode()
      51             : {
      52             :     // If we are on ground, or in constant position mode, or don't have the right vehicle and sensing to estimate wind, inhibit wind states
      53     4213784 :     bool setWindInhibit = (!useAirspeed() && !assume_zero_sideslip()) || onGround || (PV_AidingMode == AID_NONE);
      54     4213784 :     if (!inhibitWindStates && setWindInhibit) {
      55         262 :         inhibitWindStates = true;
      56     4213522 :     } else if (inhibitWindStates && !setWindInhibit) {
      57         284 :         inhibitWindStates = false;
      58             :         // set states and variances
      59         284 :         if (yawAlignComplete && useAirspeed()) {
      60             :             // if we have airspeed and a valid heading, set the wind states to the reciprocal of the vehicle heading
      61             :             // which assumes the vehicle has launched into the wind
      62         266 :              Vector3F tempEuler;
      63         266 :             stateStruct.quat.to_euler(tempEuler.x, tempEuler.y, tempEuler.z);
      64         266 :             ftype windSpeed =  sqrtF(sq(stateStruct.velocity.x) + sq(stateStruct.velocity.y)) - tasDataDelayed.tas;
      65         266 :             stateStruct.wind_vel.x = windSpeed * cosF(tempEuler.z);
      66         266 :             stateStruct.wind_vel.y = windSpeed * sinF(tempEuler.z);
      67             : 
      68             :             // set the wind sate variances to the measurement uncertainty
      69         798 :             for (uint8_t index=22; index<=23; index++) {
      70         532 :                 P[index][index] = sq(constrain_ftype(frontend->_easNoise, 0.5f, 5.0f) * constrain_ftype(dal.get_EAS2TAS(), 0.9f, 10.0f));
      71             :             }
      72             :         } else {
      73             :             // set the variances using a typical wind speed
      74          54 :             for (uint8_t index=22; index<=23; index++) {
      75          36 :                 P[index][index] = sq(5.0f);
      76             :             }
      77             :         }
      78             :     }
      79             : 
      80             :     // determine if the vehicle is manoeuvring
      81     4213784 :     if (accNavMagHoriz > 0.5f) {
      82     1727854 :         manoeuvring = true;
      83             :     } else {
      84     2485930 :         manoeuvring = false;
      85             :     }
      86             : 
      87             :     // Determine if learning of magnetic field states has been requested by the user
      88     4213784 :     uint8_t magCal = effective_magCal();
      89             :     bool magCalRequested =
      90     4213784 :             ((magCal == 0) && inFlight) || // when flying
      91     1893630 :             ((magCal == 1) && manoeuvring)  || // when manoeuvring
      92     6107414 :             ((magCal == 3) && finalInflightYawInit && finalInflightMagInit) || // when initial in-air yaw and mag field reset is complete
      93             :             (magCal == 4); // all the time
      94             : 
      95             :     // Deny mag calibration request if we aren't using the compass, it has been inhibited by the user,
      96             :     // we do not have an absolute position reference or are on the ground (unless explicitly requested by the user)
      97     4213784 :     bool magCalDenied = !use_compass() || (magCal == 2) || (onGround && magCal != 4);
      98             : 
      99             :     // Inhibit the magnetic field calibration if not requested or denied
     100     4213784 :     bool setMagInhibit = !magCalRequested || magCalDenied;
     101     4213784 :     if (!inhibitMagStates && setMagInhibit) {
     102         282 :         inhibitMagStates = true;
     103             :         // variances will be reset in CovariancePrediction
     104     4213502 :     } else if (inhibitMagStates && !setMagInhibit) {
     105         308 :         inhibitMagStates = false;
     106         308 :         if (magFieldLearned) {
     107             :             // if we have already learned the field states, then retain the learned variances
     108           2 :             P[16][16] = earthMagFieldVar.x;
     109           2 :             P[17][17] = earthMagFieldVar.y;
     110           2 :             P[18][18] = earthMagFieldVar.z;
     111           2 :             P[19][19] = bodyMagFieldVar.x;
     112           2 :             P[20][20] = bodyMagFieldVar.y;
     113           2 :             P[21][21] = bodyMagFieldVar.z;
     114             :         } else {
     115             :             // set the variances equal to the observation variances
     116        2142 :             for (uint8_t index=16; index<=21; index++) {
     117        1836 :                 P[index][index] = sq(frontend->_magNoise);
     118             :             }
     119             : 
     120             :             // set the NE earth magnetic field states using the published declination
     121             :             // and set the corresponding variances and covariances
     122         306 :             alignMagStateDeclination();
     123             : 
     124             :         }
     125             :         // request a reset of the yaw and magnetic field states if not done before
     126         308 :         if (!magStateInitComplete || (!finalInflightMagInit && inFlight)) {
     127         290 :             magYawResetRequest = true;
     128             :         }
     129             :     }
     130             : 
     131             :     // If on ground we clear the flag indicating that the magnetic field in-flight initialisation has been completed
     132             :     // because we want it re-done for each takeoff
     133     4213784 :     if (onGround) {
     134     1220526 :         finalInflightYawInit = false;
     135     1220526 :         finalInflightMagInit = false;
     136             :     }
     137             : 
     138             :     // Adjust the indexing limits used to address the covariance, states and other EKF arrays to avoid unnecessary operations
     139             :     // if we are not using those states
     140     4213784 :     if (inhibitMagStates && inhibitWindStates) {
     141     1391332 :         stateIndexLim = 15;
     142     2822452 :     } else if (inhibitWindStates) {
     143      638319 :         stateIndexLim = 21;
     144             :     } else {
     145     2184133 :         stateIndexLim = 23;
     146             :     }
     147     4213784 : }
     148             : 
     149             : // Set inertial navigation aiding mode
     150     4213784 : void NavEKF2_core::setAidingMode()
     151             : {
     152             :     // Save the previous status so we can detect when it has changed
     153     4213784 :     PV_AidingModePrev = PV_AidingMode;
     154             : 
     155             :     // Determine if we should change aiding mode
     156     4213784 :     switch (PV_AidingMode) {
     157      888463 :     case AID_NONE: {
     158             :         // Don't allow filter to start position or velocity aiding until the tilt and yaw alignment is complete
     159             :         // and IMU gyro bias estimates have stabilised
     160      888463 :         bool filterIsStable = tiltAlignComplete && yawAlignComplete && checkGyroCalStatus();
     161             :         // If GPS usage has been prohiited then we use flow aiding provided optical flow data is present
     162             :         // GPS aiding is the preferred option unless excluded by the user
     163      888463 :         bool canUseGPS = ((frontend->_fusionModeGPS) != 3 && readyToUseGPS() && filterIsStable);
     164      888463 :         bool canUseRangeBeacon = readyToUseRangeBeacon() && filterIsStable;
     165      888463 :         bool canUseExtNav = readyToUseExtNav();
     166      888463 :         if(canUseGPS || canUseRangeBeacon || canUseExtNav) {
     167         250 :             PV_AidingMode = AID_ABSOLUTE;
     168      888213 :         } else if (optFlowDataPresent() && (frontend->_flowUse == FLOW_USE_NAV) && filterIsStable) {
     169           0 :             PV_AidingMode = AID_RELATIVE;
     170             :         }
     171             :         }
     172             :         break;
     173             : 
     174           0 :     case AID_RELATIVE: {
     175             :         // Check if the optical flow sensor has timed out
     176           0 :         bool flowSensorTimeout = ((imuSampleTime_ms - flowValidMeaTime_ms) > 5000);
     177             :         // Check if the fusion has timed out (flow measurements have been rejected for too long)
     178           0 :         bool flowFusionTimeout = ((imuSampleTime_ms - prevFlowFuseTime_ms) > 5000);
     179             :         // Enable switch to absolute position mode if GPS is available
     180             :         // If GPS is not available and flow fusion has timed out, then fall-back to no-aiding
     181           0 :         if((frontend->_fusionModeGPS) != 3 && readyToUseGPS()) {
     182           0 :             PV_AidingMode = AID_ABSOLUTE;
     183           0 :         } else if (flowSensorTimeout || flowFusionTimeout) {
     184           0 :             PV_AidingMode = AID_NONE;
     185             :         }
     186             :         }
     187             :         break;
     188             : 
     189     3325321 :     case AID_ABSOLUTE: {
     190             :         // Find the minimum time without data required to trigger any check
     191     3325321 :         uint16_t minTestTime_ms = MIN(frontend->tiltDriftTimeMax_ms, MIN(frontend->posRetryTimeNoVel_ms,frontend->posRetryTimeUseVel_ms));
     192             : 
     193             :         // Check if optical flow data is being used
     194     3325321 :         bool optFlowUsed = (imuSampleTime_ms - prevFlowFuseTime_ms <= minTestTime_ms);
     195             : 
     196             :         // Check if airspeed data is being used
     197     3325321 :         bool airSpdUsed = (imuSampleTime_ms - lastTasPassTime_ms <= minTestTime_ms);
     198             : 
     199             :         // Check if range beacon data is being used
     200     3325321 :         bool rngBcnUsed = (imuSampleTime_ms - lastRngBcnPassTime_ms <= minTestTime_ms);
     201             : 
     202             :         // Check if GPS is being used
     203     3325321 :         bool posUsed = (imuSampleTime_ms - lastPosPassTime_ms <= minTestTime_ms);
     204     3325321 :         bool gpsVelUsed = (imuSampleTime_ms - lastVelPassTime_ms <= minTestTime_ms);
     205             : 
     206             :         // Check if external nav is being used
     207     3325321 :         bool extNavUsed = (imuSampleTime_ms - lastExtNavPassTime_ms <= minTestTime_ms);
     208             : 
     209             :         // Check if attitude drift has been constrained by a measurement source
     210     3325321 :         bool attAiding = posUsed || gpsVelUsed || optFlowUsed || airSpdUsed || rngBcnUsed || extNavUsed;
     211             : 
     212             :         // check if velocity drift has been constrained by a measurement source
     213     3325321 :         bool velAiding = gpsVelUsed || airSpdUsed || optFlowUsed;
     214             : 
     215             :         // check if position drift has been constrained by a measurement source
     216     3325321 :         bool posAiding = posUsed || rngBcnUsed || extNavUsed;
     217             : 
     218             :         // Check if the loss of attitude aiding has become critical
     219             :         bool attAidLossCritical = false;
     220     3325321 :         if (!attAiding) {
     221        1848 :             attAidLossCritical = (imuSampleTime_ms - prevFlowFuseTime_ms > frontend->tiltDriftTimeMax_ms) &&
     222        1846 :                    (imuSampleTime_ms - lastTasPassTime_ms > frontend->tiltDriftTimeMax_ms) &&
     223        1846 :                    (imuSampleTime_ms - lastRngBcnPassTime_ms > frontend->tiltDriftTimeMax_ms) &&
     224           2 :                    (imuSampleTime_ms - lastPosPassTime_ms > frontend->tiltDriftTimeMax_ms) &&
     225        1850 :                    (imuSampleTime_ms - lastExtNavPassTime_ms > frontend->tiltDriftTimeMax_ms) &&
     226             :                    (imuSampleTime_ms - lastVelPassTime_ms > frontend->tiltDriftTimeMax_ms);
     227             :         }
     228             : 
     229             :         // Check if the loss of position accuracy has become critical
     230             :         bool posAidLossCritical = false;
     231     3325321 :         if (!posAiding ) {
     232             :             uint16_t maxLossTime_ms;
     233       24032 :             if (!velAiding) {
     234        1848 :                 maxLossTime_ms = frontend->posRetryTimeNoVel_ms;
     235             :             } else {
     236       22184 :                 maxLossTime_ms = frontend->posRetryTimeUseVel_ms;
     237             :             }
     238       22108 :             posAidLossCritical = (imuSampleTime_ms - lastRngBcnPassTime_ms > maxLossTime_ms) &&
     239       46140 :                                  (imuSampleTime_ms - lastExtNavPassTime_ms > maxLossTime_ms) &&
     240             :                                  (imuSampleTime_ms - lastPosPassTime_ms > maxLossTime_ms);
     241             :         }
     242             : 
     243     3325321 :         if (attAidLossCritical) {
     244             :             // if the loss of attitude data is critical, then put the filter into a constant position mode
     245           2 :             PV_AidingMode = AID_NONE;
     246           2 :             posTimeout = true;
     247           2 :             velTimeout = true;
     248           2 :             rngBcnTimeout = true;
     249           2 :             tasTimeout = true;
     250           2 :             gpsNotAvailable = true;
     251     3325319 :         } else if (posAidLossCritical) {
     252       14534 :             if ((frontend->_flowUse == FLOW_USE_NAV) && optFlowDataPresent() && (imuSampleTime_ms - rngValidMeaTime_ms < 500)) {
     253           0 :                 PV_AidingMode = AID_NONE;
     254             :             }
     255             :             // if the loss of position is critical, declare all sources of position aiding as being timed out
     256       14534 :             posTimeout = true;
     257       14534 :             velTimeout = true;
     258       14534 :             rngBcnTimeout = true;
     259       14534 :             gpsNotAvailable = true;
     260             :         }
     261             :         break;
     262             :     }
     263             :     }
     264             : 
     265             :     // check to see if we are starting or stopping aiding and set states and modes as required
     266     4213784 :     if (PV_AidingMode != PV_AidingModePrev) {
     267             :         // set various usage modes based on the condition when we start aiding. These are then held until aiding is stopped.
     268         252 :         switch (PV_AidingMode) {
     269           2 :         case AID_NONE:
     270             :             // We have ceased aiding
     271           2 :             GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "EKF2 IMU%u has stopped aiding",(unsigned)imu_index);
     272             :             // When not aiding, estimate orientation & height fusing synthetic constant position and zero velocity measurement to constrain tilt errors
     273           2 :             posTimeout = true;
     274           2 :             velTimeout = true;            
     275             :             // Reset the normalised innovation to avoid false failing bad fusion tests
     276           2 :             velTestRatio = 0.0f;
     277           2 :             posTestRatio = 0.0f;
     278             :              // store the current position to be used to keep reporting the last known position
     279           2 :             lastKnownPositionNE.x = stateStruct.position.x;
     280           2 :             lastKnownPositionNE.y = stateStruct.position.y;
     281             :             // initialise filtered altitude used to provide a takeoff reference to current baro on disarm
     282             :             // this reduces the time required for the baro noise filter to settle before the filtered baro data can be used
     283           2 :             meaHgtAtTakeOff = baroDataDelayed.hgt;
     284             :             // reset the vertical position state to faster recover from baro errors experienced during touchdown
     285           2 :             stateStruct.position.z = -meaHgtAtTakeOff;
     286           2 :             break;
     287             : 
     288           0 :         case AID_RELATIVE:
     289             :             // We have commenced aiding, but GPS usage has been prohibited so use optical flow only
     290           0 :             GCS_SEND_TEXT(MAV_SEVERITY_INFO, "EKF2 IMU%u is using optical flow",(unsigned)imu_index);
     291           0 :             posTimeout = true;
     292           0 :             velTimeout = true;
     293             :             // Reset the last valid flow measurement time
     294           0 :             flowValidMeaTime_ms = imuSampleTime_ms;
     295             :             // Reset the last valid flow fusion time
     296           0 :             prevFlowFuseTime_ms = imuSampleTime_ms;
     297           0 :             break;
     298             : 
     299         250 :         case AID_ABSOLUTE: {
     300         250 :             bool canUseGPS = ((frontend->_fusionModeGPS) != 3 && readyToUseGPS());
     301         250 :             bool canUseRangeBeacon = readyToUseRangeBeacon();
     302         250 :             bool canUseExtNav = readyToUseExtNav();
     303             :             // We have commenced aiding and GPS usage is allowed
     304         250 :             if (canUseGPS) {
     305         250 :                 GCS_SEND_TEXT(MAV_SEVERITY_INFO, "EKF2 IMU%u is using GPS",(unsigned)imu_index);
     306             :             }
     307         250 :             posTimeout = false;
     308         250 :             velTimeout = false;
     309             :             // We have commenced aiding and range beacon usage is allowed
     310         250 :             if (canUseRangeBeacon) {
     311           0 :                 GCS_SEND_TEXT(MAV_SEVERITY_INFO, "EKF2 IMU%u is using range beacons",(unsigned)imu_index);
     312           0 :                 GCS_SEND_TEXT(MAV_SEVERITY_INFO, "EKF2 IMU%u initial pos NE = %3.1f,%3.1f (m)",(unsigned)imu_index,(double)receiverPos.x,(double)receiverPos.y);
     313           0 :                 GCS_SEND_TEXT(MAV_SEVERITY_INFO, "EKF2 IMU%u initial beacon pos D offset = %3.1f (m)",(unsigned)imu_index,(double)bcnPosOffset);
     314             :             }
     315             :             // We have commenced aiding and external nav usage is allowed
     316         250 :             if (canUseExtNav) {
     317           0 :                 GCS_SEND_TEXT(MAV_SEVERITY_INFO, "EKF2 IMU%u is using external nav data",(unsigned)imu_index);
     318           0 :                 GCS_SEND_TEXT(MAV_SEVERITY_INFO, "EKF2 IMU%u initial pos NED = %3.1f,%3.1f,%3.1f (m)",(unsigned)imu_index,(double)extNavDataDelayed.pos.x,(double)extNavDataDelayed.pos.y,(double)extNavDataDelayed.pos.z);
     319             :                 //handle yaw reset as special case only if compass is disabled
     320           0 :                 if (!use_compass()) {
     321           0 :                     extNavYawResetRequest = true;
     322           0 :                     controlMagYawReset();
     323             :                 }
     324             :                 // handle height reset as special case
     325           0 :                 hgtMea = -extNavDataDelayed.pos.z;
     326           0 :                 posDownObsNoise = sq(constrain_ftype(extNavDataDelayed.posErr, 0.1f, 10.0f));
     327           0 :                 ResetHeight();
     328             :             }
     329             :             // reset the last fusion accepted times to prevent unwanted activation of timeout logic
     330         250 :             lastPosPassTime_ms = imuSampleTime_ms;
     331         250 :             lastVelPassTime_ms = imuSampleTime_ms;
     332         250 :             lastRngBcnPassTime_ms = imuSampleTime_ms;
     333         250 :             lastExtNavPassTime_ms = imuSampleTime_ms;
     334             :             }
     335         250 :             break;
     336             :         }
     337             : 
     338             :         // Always reset the position and velocity when changing mode
     339         252 :         ResetVelocity();
     340         252 :         ResetPosition();
     341             :     }
     342     4213784 : }
     343             : 
     344             : // Check the tilt and yaw alignmnent status
     345             : // Used during initial bootstrap alignment of the filter
     346     4213784 : void NavEKF2_core::checkAttitudeAlignmentStatus()
     347             : {
     348             :     // Check for tilt convergence - used during initial alignment
     349     4213784 :     ftype alpha = 1.0f*imuDataDelayed.delAngDT;
     350     4213784 :     ftype temp=tiltErrVec.length();
     351     4213784 :     tiltErrFilt = alpha*temp + (1.0f-alpha)*tiltErrFilt;
     352     4213784 :     if (tiltErrFilt < 0.005f && !tiltAlignComplete) {
     353         418 :         tiltAlignComplete = true;
     354         418 :         GCS_SEND_TEXT(MAV_SEVERITY_INFO, "EKF2 IMU%u tilt alignment complete",(unsigned)imu_index);
     355             :     }
     356             : 
     357             :     // submit yaw and magnetic field reset requests depending on whether we have compass data
     358     4213784 :     if (tiltAlignComplete && !yawAlignComplete) {
     359         760 :         if (use_compass()) {
     360           0 :             magYawResetRequest = true;
     361           0 :             gpsYawResetRequest = false;
     362             :         } else {
     363         760 :             magYawResetRequest = false;
     364         760 :             gpsYawResetRequest = true;
     365             :         }
     366             :     }
     367     4213784 : }
     368             : 
     369             : // return true if we should use the airspeed sensor
     370     7405944 : bool NavEKF2_core::useAirspeed(void) const
     371             : {
     372     7405944 :     return dal.airspeed_sensor_enabled();
     373             : }
     374             : 
     375             : // return true if we should use the range finder sensor
     376           0 : bool NavEKF2_core::useRngFinder(void) const
     377             : {
     378             :     // TO-DO add code to set this based in setting of optical flow use parameter and presence of sensor
     379           0 :     return true;
     380             : }
     381             : 
     382             : // return true if optical flow data is available
     383      888853 : bool NavEKF2_core::optFlowDataPresent(void) const
     384             : {
     385      888853 :     return (imuSampleTime_ms - flowMeaTime_ms < 200);
     386             : }
     387             : 
     388             : // return true if the filter to be ready to use gps
     389      888713 : bool NavEKF2_core::readyToUseGPS(void) const
     390             : {
     391      888713 :     return validOrigin && tiltAlignComplete && yawAlignComplete && gpsGoodToAlign && (frontend->_fusionModeGPS != 3) && gpsDataToFuse;
     392             : }
     393             : 
     394             : // return true if the filter to be ready to use the beacon range measurements
     395      888713 : bool NavEKF2_core::readyToUseRangeBeacon(void) const
     396             : {
     397      888713 :     return tiltAlignComplete && yawAlignComplete && rngBcnGoodToAlign && rngBcnDataToFuse;
     398             : }
     399             : 
     400             : // return true if the filter to be ready to use external nav data
     401      888713 : bool NavEKF2_core::readyToUseExtNav(void) const
     402             : {
     403      888713 :     return tiltAlignComplete && extNavDataToFuse;
     404             : }
     405             : 
     406             : // return true if we should use the compass
     407    16320620 : bool NavEKF2_core::use_compass(void) const
     408             : {
     409    16320620 :     return dal.compass().use_for_yaw(magSelectIndex) && !allMagSensorsFailed;
     410             : }
     411             : 
     412             : /*
     413             :   should we assume zero sideslip?
     414             :  */
     415    11869049 : bool NavEKF2_core::assume_zero_sideslip(void) const
     416             : {
     417             :     // we don't assume zero sideslip for ground vehicles as EKF could
     418             :     // be quite sensitive to a rapid spin of the ground vehicle if
     419             :     // traction is lost
     420    11869049 :     return dal.get_fly_forward() && dal.get_vehicle_class() != AP_DAL::VehicleClass::GROUND;
     421             : }
     422             : 
     423             : // sets the local NED origin using a LLH location (latitude, longitude, height)
     424             : // returns false if absolute aiding and GPS is being used or if the origin is already set
     425          34 : bool NavEKF2_core::setOriginLLH(const Location &loc)
     426             : {
     427          34 :     if (PV_AidingMode == AID_ABSOLUTE && !extNavUsedForPos) {
     428             :         // reject attempts to set the origin if GPS is being used
     429             :         return false;
     430             :     }
     431             : 
     432          34 :     return setOrigin(loc);
     433             : }
     434             : 
     435             : // sets the local NED origin using a LLH location (latitude, longitude, height)
     436             : // returns false if the origin has already been set
     437         394 : bool NavEKF2_core::setOrigin(const Location &loc)
     438             : {
     439             :     // if the origin is valid reject setting a new origin
     440         394 :     if (validOrigin) {
     441             :         return false;
     442             :     }
     443             : 
     444         394 :     EKF_origin = loc;
     445         394 :     ekfGpsRefHgt = (double)0.01 * (double)EKF_origin.alt;
     446             :     // define Earth rotation vector in the NED navigation frame at the origin
     447         394 :     calcEarthRateNED(earthRateNED, EKF_origin.lat);
     448         394 :     validOrigin = true;
     449         394 :     GCS_SEND_TEXT(MAV_SEVERITY_INFO, "EKF2 IMU%u origin set",(unsigned)imu_index);
     450             : 
     451             :     // put origin in frontend as well to ensure it stays in sync between lanes
     452         394 :     frontend->common_EKF_origin = EKF_origin;
     453         394 :     frontend->common_origin_valid = true;
     454             : 
     455         394 :     return true;
     456             : }
     457             : 
     458             : // record a yaw reset event
     459         846 : void NavEKF2_core::recordYawReset()
     460             : {
     461         846 :     yawAlignComplete = true;
     462         846 :     if (inFlight) {
     463          38 :         finalInflightYawInit = true;
     464             :     }
     465         846 : }
     466             : 
     467             : // return true and set the class variable true if the delta angle bias has been learned
     468      747519 : bool NavEKF2_core::checkGyroCalStatus(void)
     469             : {
     470             :     // check delta angle bias variances
     471      747519 :     const ftype delAngBiasVarMax = sq(radians(0.15f * dtEkfAvg));
     472      747519 :     if (!use_compass()) {
     473             :         // rotate the variances into earth frame and evaluate horizontal terms only as yaw component is poorly observable without a compass
     474             :         // which can make this check fail
     475           8 :         Vector3F delAngBiasVarVec = Vector3F(P[9][9],P[10][10],P[11][11]);
     476           8 :         Vector3F temp = prevTnb * delAngBiasVarVec;
     477          16 :         delAngBiasLearned = (fabsF(temp.x) < delAngBiasVarMax) &&
     478           8 :                             (fabsF(temp.y) < delAngBiasVarMax);
     479             :     } else {
     480      747511 :         delAngBiasLearned =  (P[9][9] <= delAngBiasVarMax) &&
     481      747511 :                              (P[10][10] <= delAngBiasVarMax) &&
     482      537294 :                              (P[11][11] <= delAngBiasVarMax);
     483             :     }
     484      747519 :     return delAngBiasLearned;
     485             : }
     486             : 
     487             : // Update the filter status
     488     3248540 : void  NavEKF2_core::updateFilterStatus(void)
     489             : {
     490             :     // init return value
     491     3248540 :     filterStatus.value = 0;
     492     3248540 :     bool doingFlowNav = (PV_AidingMode == AID_RELATIVE) && flowDataValid;
     493     3248540 :     bool doingWindRelNav = !tasTimeout && assume_zero_sideslip();
     494     3248540 :     bool doingNormalGpsNav = !posTimeout && (PV_AidingMode == AID_ABSOLUTE);
     495     3248540 :     bool someVertRefData = (!velTimeout && (useGpsVertVel || useExtNavVel)) || !hgtTimeout;
     496     3248540 :     bool someHorizRefData = !(velTimeout && posTimeout && tasTimeout) || doingFlowNav;
     497     3248540 :     bool optFlowNavPossible = flowDataValid && delAngBiasLearned;
     498     3248540 :     bool gpsNavPossible = !gpsNotAvailable && gpsGoodToAlign && delAngBiasLearned;
     499     3248540 :     bool filterHealthy = healthy() && tiltAlignComplete && (yawAlignComplete || (!use_compass() && (PV_AidingMode == AID_NONE)));
     500             :     // If GPS height usage is specified, height is considered to be inaccurate until the GPS passes all checks
     501     3248540 :     bool hgtNotAccurate = (frontend->_altSource == 2) && !validOrigin;
     502             : 
     503             :     // set individual flags
     504     3248540 :     filterStatus.flags.attitude = !stateStruct.quat.is_nan() && filterHealthy;   // attitude valid (we need a better check)
     505     3248540 :     filterStatus.flags.horiz_vel = someHorizRefData && filterHealthy;      // horizontal velocity estimate valid
     506     3248540 :     filterStatus.flags.vert_vel = someVertRefData && filterHealthy;        // vertical velocity estimate valid
     507     3248540 :     filterStatus.flags.horiz_pos_rel = ((doingFlowNav && gndOffsetValid) || doingWindRelNav || doingNormalGpsNav) && filterHealthy;   // relative horizontal position estimate valid
     508     3248540 :     filterStatus.flags.horiz_pos_abs = doingNormalGpsNav && filterHealthy; // absolute horizontal position estimate valid
     509     3248540 :     filterStatus.flags.vert_pos = !hgtTimeout && filterHealthy && !hgtNotAccurate; // vertical position estimate valid
     510     3248540 :     filterStatus.flags.terrain_alt = gndOffsetValid && filterHealthy;           // terrain height estimate valid
     511     3248540 :     filterStatus.flags.const_pos_mode = (PV_AidingMode == AID_NONE) && filterHealthy;     // constant position mode
     512     3248540 :     filterStatus.flags.pred_horiz_pos_rel = ((optFlowNavPossible || gpsNavPossible) && filterHealthy) || filterStatus.flags.horiz_pos_rel; // we should be able to estimate a relative position when we enter flight mode
     513     3248540 :     filterStatus.flags.pred_horiz_pos_abs = (gpsNavPossible && filterHealthy) || filterStatus.flags.horiz_pos_abs; // we should be able to estimate an absolute position when we enter flight mode
     514     3248540 :     filterStatus.flags.takeoff_detected = takeOffDetected; // takeoff for optical flow navigation has been detected
     515     3248540 :     filterStatus.flags.takeoff = dal.get_takeoff_expected(); // The EKF has been told to expect takeoff and is in a ground effect mitigation mode
     516     3248540 :     filterStatus.flags.touchdown = dal.get_touchdown_expected(); // The EKF has been told to detect touchdown and is in a ground effect mitigation mode
     517     3248540 :     filterStatus.flags.using_gps = ((imuSampleTime_ms - lastPosPassTime_ms) < 4000) && (PV_AidingMode == AID_ABSOLUTE);
     518     3248540 :     filterStatus.flags.gps_glitching = !gpsAccuracyGood && (PV_AidingMode == AID_ABSOLUTE) && !extNavUsedForPos; // GPS glitching is affecting navigation accuracy
     519     3248540 :     filterStatus.flags.gps_quality_good = gpsGoodToAlign;
     520             :     // for reporting purposes we report rejecting airspeed after 3s of not fusing when we want to fuse the data
     521     6508474 :     filterStatus.flags.rejecting_airspeed = lastTasFailTime_ms != 0 &&
     522     3248540 :                                             (imuSampleTime_ms - lastTasFailTime_ms) < 1000 &&
     523        6110 :                                             (imuSampleTime_ms - lastTasPassTime_ms) > 3000;
     524     3248540 :     filterStatus.flags.initalized = filterStatus.flags.initalized || healthy();
     525     3248540 : }
     526             : 
     527     3248540 : void NavEKF2_core::runYawEstimatorPrediction()
     528             : {
     529     3248540 :     if (yawEstimator != nullptr && frontend->_fusionModeGPS <= 1) {
     530             :         ftype trueAirspeed;
     531     3248540 :         if (is_positive(defaultAirSpeed) && assume_zero_sideslip()) {
     532           0 :             if (imuDataDelayed.time_ms - tasDataDelayed.time_ms < 5000) {
     533           0 :                 trueAirspeed = tasDataDelayed.tas;
     534             :             } else {
     535           0 :                 trueAirspeed = defaultAirSpeed * dal.get_EAS2TAS();
     536             :             }
     537             :         } else {
     538             :             trueAirspeed = 0.0f;
     539             :         }
     540             : 
     541     3248540 :         yawEstimator->update(imuDataDelayed.delAng, imuDataDelayed.delVel, imuDataDelayed.delAngDT, imuDataDelayed.delVelDT, EKFGSF_run_filterbank, trueAirspeed);
     542             :     }
     543     3248540 : }
     544             : 
     545     3248540 : void NavEKF2_core::runYawEstimatorCorrection()
     546             : {
     547     3248540 :     if (yawEstimator != nullptr && frontend->_fusionModeGPS <= 1 && EKFGSF_run_filterbank) {
     548     2340576 :         if (gpsDataToFuse) {
     549      224936 :             Vector2F gpsVelNE = Vector2F(gpsDataDelayed.vel.x, gpsDataDelayed.vel.y);
     550      224936 :             ftype gpsVelAcc = fmaxF(gpsSpdAccuracy, ftype(frontend->_gpsHorizVelNoise));
     551      224936 :             yawEstimator->fuseVelData(gpsVelNE, gpsVelAcc);
     552             :         }
     553             : 
     554             :         // action an external reset request
     555     2340576 :         if (EKFGSF_yaw_reset_request_ms > 0 && imuSampleTime_ms - EKFGSF_yaw_reset_request_ms < YAW_RESET_TO_GSF_TIMEOUT_MS) {
     556           4 :             EKFGSF_resetMainFilterYaw();
     557             :         }
     558             :     }
     559     3248540 : }
     560             : 
     561             : // request a reset the yaw to the GSF estimate
     562             : // request times out after YAW_RESET_TO_GSF_TIMEOUT_MS if it cannot be actioned
     563           4 : void NavEKF2_core::EKFGSF_requestYawReset()
     564             : {
     565           4 :     EKFGSF_yaw_reset_request_ms = imuSampleTime_ms;
     566           4 : }

Generated by: LCOV version 1.14