LCOV - code coverage report
Current view: top level - AP_NavEKF2 - AP_NavEKF2_Measurements.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 303 444 68.2 %
Date: 2026-09-09 11:02:08 Functions: 13 19 68.4 %

          Line data    Source code
       1             : #include "AP_NavEKF2_core.h"
       2             : 
       3             : #include "AP_NavEKF2.h"
       4             : 
       5             : #include <GCS_MAVLink/GCS.h>
       6             : #include <AP_DAL/AP_DAL.h>
       7             : #include <AP_InternalError/AP_InternalError.h>
       8             : 
       9             : #if AP_RANGEFINDER_ENABLED
      10             : 
      11             : 
      12             : /********************************************************
      13             : *              OPT FLOW AND RANGE FINDER                *
      14             : ********************************************************/
      15             : 
      16             : // Read the range finder and take new measurements if available
      17             : // Apply a median filter
      18     3230372 : void NavEKF2_core::readRangeFinder(void)
      19             : {
      20             :     uint8_t midIndex;
      21             :     uint8_t maxIndex;
      22             :     uint8_t minIndex;
      23             : 
      24             :     // get theoretical correct range when the vehicle is on the ground
      25             :     // don't allow range to go below 5cm because this can cause problems with optical flow processing
      26     3230372 :     const auto *_rng = dal.rangefinder();
      27     3230372 :     if (_rng == nullptr) {
      28             :         return;
      29             :     }
      30             : 
      31       55546 :     rngOnGnd = MAX(_rng->ground_clearance_orient(ROTATION_PITCH_270), 0.05f);
      32             : 
      33             :     // read range finder at 20Hz
      34             :     // TODO better way of knowing if it has new data
      35       55546 :     if (_rng && (imuSampleTime_ms - lastRngMeasTime_ms) > 50) {
      36             : 
      37             :         // reset the timer used to control the measurement rate
      38       11182 :         lastRngMeasTime_ms =  imuSampleTime_ms;
      39             : 
      40             :         // store samples and sample time into a ring buffer if valid
      41             :         // use data from two range finders if available
      42             : 
      43       33546 :         for (uint8_t sensorIndex = 0; sensorIndex < ARRAY_SIZE(rngMeasIndex); sensorIndex++) {
      44       22364 :             auto *sensor = _rng->get_backend(sensorIndex);
      45       22364 :             if (sensor == nullptr) {
      46       17134 :                 continue;
      47             :             }
      48       11182 :             if ((sensor->orientation() == ROTATION_PITCH_270) && (sensor->status() == AP_DAL_RangeFinder::Status::Good)) {
      49        5230 :                 rngMeasIndex[sensorIndex] ++;
      50        5230 :                 if (rngMeasIndex[sensorIndex] > 2) {
      51        1742 :                     rngMeasIndex[sensorIndex] = 0;
      52             :                 }
      53        5230 :                 storedRngMeasTime_ms[sensorIndex][rngMeasIndex[sensorIndex]] = imuSampleTime_ms - 25;
      54        5230 :                 storedRngMeas[sensorIndex][rngMeasIndex[sensorIndex]] = sensor->distance();
      55             :             } else {
      56        5952 :                 continue;
      57             :             }
      58             : 
      59             :             // check for three fresh samples
      60        5230 :             bool sampleFresh[DOWNWARD_RANGEFINDER_MAX_INSTANCES][3] = {};
      61       20920 :             for (uint8_t index = 0; index <= 2; index++) {
      62       15690 :                 sampleFresh[sensorIndex][index] = (imuSampleTime_ms - storedRngMeasTime_ms[sensorIndex][index]) < 500;
      63             :             }
      64             : 
      65             :             // find the median value if we have three fresh samples
      66        5230 :             if (sampleFresh[sensorIndex][0] && sampleFresh[sensorIndex][1] && sampleFresh[sensorIndex][2]) {
      67        5206 :                 if (storedRngMeas[sensorIndex][0] > storedRngMeas[sensorIndex][1]) {
      68             :                     minIndex = 1;
      69             :                     maxIndex = 0;
      70             :                 } else {
      71             :                     minIndex = 0;
      72             :                     maxIndex = 1;
      73             :                 }
      74        5206 :                 if (storedRngMeas[sensorIndex][2] > storedRngMeas[sensorIndex][maxIndex]) {
      75             :                     midIndex = maxIndex;
      76        4514 :                 } else if (storedRngMeas[sensorIndex][2] < storedRngMeas[sensorIndex][minIndex]) {
      77             :                     midIndex = minIndex;
      78             :                 } else {
      79             :                     midIndex = 2;
      80             :                 }
      81             : 
      82             :                 // don't allow time to go backwards
      83        5206 :                 if (storedRngMeasTime_ms[sensorIndex][midIndex] > rangeDataNew.time_ms) {
      84        3092 :                     rangeDataNew.time_ms = storedRngMeasTime_ms[sensorIndex][midIndex];
      85             :                 }
      86             : 
      87             :                 // limit the measured range to be no less than the on-ground range
      88        5206 :                 rangeDataNew.rng = MAX(storedRngMeas[sensorIndex][midIndex],rngOnGnd);
      89             : 
      90             :                 // get position in body frame for the current sensor
      91        5206 :                 rangeDataNew.sensor_idx = sensorIndex;
      92             : 
      93             :                 // write data to buffer with time stamp to be fused when the fusion time horizon catches up with it
      94        5206 :                 storedRange.push(rangeDataNew);
      95             : 
      96             :                 // indicate we have updated the measurement
      97        5206 :                 rngValidMeaTime_ms = imuSampleTime_ms;
      98             : 
      99          24 :             } else if (!takeOffDetected && ((imuSampleTime_ms - rngValidMeaTime_ms) > 200)) {
     100             :                 // before takeoff we assume on-ground range value if there is no data
     101          12 :                 rangeDataNew.time_ms = imuSampleTime_ms;
     102          12 :                 rangeDataNew.rng = rngOnGnd;
     103             : 
     104             :                 // write data to buffer with time stamp to be fused when the fusion time horizon catches up with it
     105          12 :                 storedRange.push(rangeDataNew);
     106             : 
     107             :                 // indicate we have updated the measurement
     108          12 :                 rngValidMeaTime_ms = imuSampleTime_ms;
     109             : 
     110             :             }
     111             :         }
     112             :     }
     113             : }
     114             : #endif
     115             : 
     116             : // write the raw optical flow measurements
     117             : // this needs to be called externally.
     118           0 : void NavEKF2_core::writeOptFlowMeas(const uint8_t rawFlowQuality, const Vector2f &rawFlowRates, const Vector2f &rawGyroRates, const uint32_t msecFlowMeas, const Vector3f &posOffset, float heightOverride)
     119             : {
     120             :     // The raw measurements need to be optical flow rates in radians/second averaged across the time since the last update
     121             :     // The PX4Flow sensor outputs flow rates with the following axis and sign conventions:
     122             :     // A positive X rate is produced by a positive sensor rotation about the X axis
     123             :     // A positive Y rate is produced by a positive sensor rotation about the Y axis
     124             :     // This filter uses a different definition of optical flow rates to the sensor with a positive optical flow rate produced by a
     125             :     // negative rotation about that axis. For example a positive rotation of the flight vehicle about its X (roll) axis would produce a negative X flow rate
     126           0 :     flowMeaTime_ms = imuSampleTime_ms;
     127             :     // calculate bias errors on flow sensor gyro rates, but protect against spikes in data
     128             :     // reset the accumulated body delta angle and time
     129             :     // don't do the calculation if not enough time lapsed for a reliable body rate measurement
     130           0 :     if (delTimeOF > 0.01f) {
     131           0 :         flowGyroBias.x = 0.99f * flowGyroBias.x + 0.01f * constrain_ftype((rawGyroRates.x - delAngBodyOF.x/delTimeOF),-0.1f,0.1f);
     132           0 :         flowGyroBias.y = 0.99f * flowGyroBias.y + 0.01f * constrain_ftype((rawGyroRates.y - delAngBodyOF.y/delTimeOF),-0.1f,0.1f);
     133           0 :         delAngBodyOF.zero();
     134           0 :         delTimeOF = 0.0f;
     135             :     }
     136             :     // by definition if this function is called, then flow measurements have been provided so we
     137             :     // need to run the optical flow takeoff detection
     138           0 :     detectOptFlowTakeoff();
     139             : 
     140             :     // calculate rotation matrices at mid sample time for flow observations
     141           0 :     stateStruct.quat.rotation_matrix(Tbn_flow);
     142             :     // don't use data with a low quality indicator or extreme rates (helps catch corrupt sensor data)
     143           0 :     if ((rawFlowQuality > 0) && rawFlowRates.length() < 4.2f && rawGyroRates.length() < 4.2f) {
     144             :         // correct flow sensor body rates for bias and write
     145           0 :         ofDataNew.bodyRadXYZ.x = rawGyroRates.x - flowGyroBias.x;
     146           0 :         ofDataNew.bodyRadXYZ.y = rawGyroRates.y - flowGyroBias.y;
     147             :         // the sensor interface doesn't provide a z axis rate so use the rate from the nav sensor instead
     148           0 :         if (delTimeOF > 0.001f) {
     149             :             // first preference is to use the rate averaged over the same sampling period as the flow sensor
     150           0 :             ofDataNew.bodyRadXYZ.z = delAngBodyOF.z / delTimeOF;
     151           0 :         } else if (imuDataNew.delAngDT > 0.001f){
     152             :             // second preference is to use most recent IMU data
     153           0 :             ofDataNew.bodyRadXYZ.z = imuDataNew.delAng.z / imuDataNew.delAngDT;
     154             :         } else {
     155             :             // third preference is use zero
     156           0 :             ofDataNew.bodyRadXYZ.z =  0.0f;
     157             :         }
     158             :         // write uncorrected flow rate measurements
     159             :         // note correction for different axis and sign conventions used by the px4flow sensor
     160           0 :         ofDataNew.flowRadXY = (-rawFlowRates).toftype(); // raw (non motion compensated) optical flow angular rate about the X axis (rad/sec)
     161             :         // write the flow sensor position in body frame
     162           0 :         ofDataNew.body_offset = posOffset.toftype();
     163             :         // write the flow sensor height override
     164           0 :         ofDataNew.heightOverride = heightOverride;
     165             :         // write flow rate measurements corrected for body rates
     166           0 :         ofDataNew.flowRadXYcomp.x = ofDataNew.flowRadXY.x + ofDataNew.bodyRadXYZ.x;
     167           0 :         ofDataNew.flowRadXYcomp.y = ofDataNew.flowRadXY.y + ofDataNew.bodyRadXYZ.y;
     168             :         // record time last observation was received so we can detect loss of data elsewhere
     169           0 :         flowValidMeaTime_ms = imuSampleTime_ms;
     170             :         // estimate sample time of the measurement
     171           0 :         ofDataNew.time_ms = imuSampleTime_ms - frontend->_flowDelay_ms - frontend->flowTimeDeltaAvg_ms/2;
     172             :         // Correct for the average intersampling delay due to the filter updaterate
     173           0 :         ofDataNew.time_ms -= localFilterTimeStep_ms/2;
     174             :         // Prevent time delay exceeding age of oldest IMU data in the buffer
     175           0 :         ofDataNew.time_ms = MAX(ofDataNew.time_ms,imuDataDelayed.time_ms);
     176             :         // Save data to buffer
     177           0 :         storedOF.push(ofDataNew);
     178             :         // Check for data at the fusion time horizon
     179           0 :         flowDataToFuse = storedOF.recall(ofDataDelayed, imuDataDelayed.time_ms);
     180             :     }
     181           0 : }
     182             : 
     183             : 
     184             : /********************************************************
     185             : *                      MAGNETOMETER                     *
     186             : ********************************************************/
     187             : 
     188             : // try changing to another compass
     189          14 : void NavEKF2_core::tryChangeCompass(void)
     190             : {
     191          14 :     const auto &compass = dal.compass();
     192          14 :     const uint8_t maxCount = compass.get_count();
     193             : 
     194             :     // search through the list of magnetometers
     195          14 :     for (uint8_t i=1; i<maxCount; i++) {
     196          14 :         uint8_t tempIndex = magSelectIndex + i;
     197             :         // loop back to the start index if we have exceeded the bounds
     198          14 :         if (tempIndex >= maxCount) {
     199           2 :             tempIndex -= maxCount;
     200             :         }
     201             :         // if the magnetometer is allowed to be used for yaw and has a different index, we start using it
     202          14 :         if (compass.healthy(tempIndex) && compass.use_for_yaw(tempIndex) && tempIndex != magSelectIndex) {
     203          14 :             magSelectIndex = tempIndex;
     204          14 :             GCS_SEND_TEXT(MAV_SEVERITY_INFO, "EKF2 IMU%u switching to compass %u",(unsigned)imu_index,magSelectIndex);
     205             :             // reset the timeout flag and timer
     206          14 :             magTimeout = false;
     207          14 :             lastHealthyMagTime_ms = imuSampleTime_ms;
     208             :             // zero the learned magnetometer bias states
     209          14 :             stateStruct.body_magfield.zero();
     210             :             // clear the measurement buffer
     211          14 :             storedMag.reset();
     212             :             // clear the data waiting flag so that we do not use any data pending from the previous sensor
     213          14 :             magDataToFuse = false;
     214             :             // request a reset of the magnetic field states
     215          14 :             magStateResetRequest = true;
     216             :             // declare the field unlearned so that the reset request will be obeyed
     217          14 :             magFieldLearned = false;
     218             : 
     219             :             // reset body mag variances on next CovariancePrediction
     220          14 :             needMagBodyVarReset = true;
     221          14 :             return;
     222             :         }
     223             :     }
     224             : }
     225             : 
     226             : // check for new magnetometer data and update store measurements if available
     227     3228940 : void NavEKF2_core::readMagData()
     228             : {
     229     3228940 :     const auto &compass = dal.compass();
     230             : 
     231     3228940 :     if (!compass.available()) {
     232           0 :         allMagSensorsFailed = true;
     233           0 :         return;        
     234             :     }
     235             : 
     236             :     // If we are a vehicle with a sideslip constraint to aid yaw estimation and we have timed out on our last avialable
     237             :     // magnetometer, then declare the magnetometers as failed for this flight
     238     3228940 :     const uint8_t maxCount = compass.get_count();
     239     3228940 :     if (allMagSensorsFailed || (magTimeout && assume_zero_sideslip() && magSelectIndex >= maxCount-1 && inFlight)) {
     240           2 :         allMagSensorsFailed = true;
     241           2 :         return;
     242             :     }
     243             : 
     244     3228938 :     if (compass.learn_offsets_enabled()) {
     245             :         // while learning offsets keep all mag states reset
     246           0 :         InitialiseVariablesMag();
     247           0 :         wasLearningCompass_ms = imuSampleTime_ms;
     248     3228938 :     } else if (wasLearningCompass_ms != 0 && imuSampleTime_ms - wasLearningCompass_ms > 1000) {
     249           0 :         wasLearningCompass_ms = 0;
     250             :         // force a new yaw alignment 1s after learning completes. The
     251             :         // delay is to ensure any buffered mag samples are discarded
     252           0 :         yawAlignComplete = false;
     253           0 :         InitialiseVariablesMag();
     254             :     }
     255             : 
     256             :     // If the magnetometer has timed out (been rejected for too long), we find another magnetometer to use if available
     257             :     // Don't do this if we are on the ground because there can be magnetic interference and we need to know if there is a problem
     258             :     // before taking off. Don't do this within the first 30 seconds from startup because the yaw error could be due to large yaw gyro bias affsets
     259             :     // if the timeout is due to a sensor failure, then declare a timeout regardless of onground status
     260     3228938 :     if (maxCount > 1) {
     261     3228938 :         bool fusionTimeout = magTimeout && !onGround && imuSampleTime_ms - ekfStartTime_ms > 30000;
     262     3228938 :         bool sensorTimeout = !compass.healthy(magSelectIndex) && imuSampleTime_ms - lastMagRead_ms > frontend->magFailTimeLimit_ms;
     263     3228938 :         if (fusionTimeout || sensorTimeout) {
     264          14 :             tryChangeCompass();
     265             :         }
     266             :     }
     267             : 
     268             :     // do not accept new compass data faster than 14Hz (nominal rate is 10Hz) to prevent high processor loading
     269             :     // because magnetometer fusion is an expensive step and we could overflow the FIFO buffer
     270     3228938 :     if (use_compass() &&
     271     3228938 :         compass.healthy(magSelectIndex) &&
     272     3221382 :         compass.last_update_usec(magSelectIndex) - lastMagUpdate_us > 70000) {
     273             : 
     274             :         // detect changes to magnetometer offset parameters and reset states
     275      610242 :         Vector3F nowMagOffsets = compass.get_offsets(magSelectIndex).toftype();
     276      610242 :         bool changeDetected = lastMagOffsetsValid && (nowMagOffsets != lastMagOffsets);
     277             :         if (changeDetected) {
     278             :             // zero the learned magnetometer bias states
     279           4 :             stateStruct.body_magfield.zero();
     280             :             // clear the measurement buffer
     281           4 :             storedMag.reset();
     282             :             // reset body mag variances on next
     283             :             // CovariancePrediction. This copes with possible errors
     284             :             // in the new offsets
     285           4 :             needMagBodyVarReset = true;
     286             :         }
     287      610242 :         lastMagOffsets = nowMagOffsets;
     288      610242 :         lastMagOffsetsValid = true;
     289             : 
     290             :         // store time of last measurement update
     291      610242 :         lastMagUpdate_us = compass.last_update_usec(magSelectIndex);
     292             : 
     293             :         // Magnetometer data at the current time horizon
     294      610242 :         mag_elements magDataNew;
     295             : 
     296             :         // estimate of time magnetometer measurement was taken, allowing for delays
     297      610242 :         magDataNew.time_ms = imuSampleTime_ms - frontend->magDelay_ms;
     298             : 
     299             :         // Correct for the average intersampling delay due to the filter updaterate
     300      610242 :         magDataNew.time_ms -= localFilterTimeStep_ms/2;
     301             : 
     302             :         // read compass data and scale to improve numerical conditioning
     303      610242 :         magDataNew.mag = compass.get_field(magSelectIndex).toftype() * 0.001f;
     304             : 
     305             :         // check for consistent data between magnetometers
     306      610242 :         consistentMagData = compass.consistent();
     307             : 
     308             :         // save magnetometer measurement to buffer to be fused later
     309      610242 :         storedMag.push(magDataNew);
     310             : 
     311             :         // remember time we read compass, to detect compass sensor failure
     312      610242 :         lastMagRead_ms = imuSampleTime_ms;
     313             :     }
     314             : }
     315             : 
     316             : /********************************************************
     317             : *                Inertial Measurements                  *
     318             : ********************************************************/
     319             : 
     320             : /*
     321             :  *  Read IMU delta angle and delta velocity measurements and downsample to 100Hz
     322             :  *  for storage in the data buffers used by the EKF. If the IMU data arrives at
     323             :  *  lower rate than 100Hz, then no downsampling or upsampling will be performed.
     324             :  *  Downsampling is done using a method that does not introduce coning or sculling
     325             :  *  errors.
     326             :  */
     327     4231300 : void NavEKF2_core::readIMUData()
     328             : {
     329     4231300 :     const auto &ins = dal.ins();
     330             : 
     331             :     // average IMU sampling rate
     332     4231300 :     dtIMUavg = ins.get_loop_delta_t();
     333             : 
     334             :     // use the nominated imu or primary if not available
     335             :     uint8_t accel_active, gyro_active;
     336             : 
     337     4231300 :     if (ins.use_accel(imu_index)) {
     338             :         accel_active = imu_index;
     339             :     } else {
     340           0 :         accel_active = ins.get_first_usable_accel();
     341             :     }
     342             : 
     343     4231300 :     if (ins.use_gyro(imu_index)) {
     344             :         gyro_active = imu_index;
     345             :     } else {
     346           0 :         gyro_active = ins.get_first_usable_gyro();
     347             :     }
     348             : 
     349     4231300 :     if (gyro_active != gyro_index_active) {
     350             :         // we are switching active gyro at runtime. Copy over the
     351             :         // biases we have learned from the previously inactive
     352             :         // gyro. We don't re-init the bias uncertainty as it should
     353             :         // have the same uncertainty as the previously active gyro
     354           0 :         stateStruct.gyro_bias = inactiveBias[gyro_active].gyro_bias;
     355           0 :         gyro_index_active = gyro_active;
     356             : 
     357             :         // use the gyro scale factor we have previously used on this
     358             :         // IMU (if any). We don't reset the variances as we don't want
     359             :         // errors after switching to be mis-assigned to the gyro scale
     360             :         // factor
     361           0 :         stateStruct.gyro_scale = inactiveBias[gyro_active].gyro_scale;
     362             :     }
     363             : 
     364     4231300 :     if (accel_active != accel_index_active) {
     365             :         // switch to the learned accel bias for this IMU
     366           0 :         stateStruct.accel_zbias = inactiveBias[accel_active].accel_zbias;
     367           0 :         accel_index_active = accel_active;
     368             :     }
     369             : 
     370             :     // update the inactive bias states
     371     4231300 :     learnInactiveBiases();
     372             : 
     373     4231300 :     readDeltaVelocity(accel_index_active, imuDataNew.delVel, imuDataNew.delVelDT);
     374     4231300 :     accelPosOffset = ins.get_imu_pos_offset(accel_index_active).toftype();
     375     4231300 :     imuDataNew.accel_index = accel_index_active;
     376             : 
     377             :     // Get delta angle data from primary gyro or primary if not available
     378     4231300 :     readDeltaAngle(gyro_index_active, imuDataNew.delAng, imuDataNew.delAngDT);
     379     4231300 :     imuDataNew.gyro_index = gyro_index_active;
     380             : 
     381             :     // Get current time stamp
     382     4231300 :     imuDataNew.time_ms = imuSampleTime_ms;
     383             : 
     384             :     // use the most recent IMU index for the downsampled IMU
     385             :     // data. This isn't strictly correct if we switch IMUs between
     386             :     // samples
     387     4231300 :     imuDataDownSampledNew.gyro_index = imuDataNew.gyro_index;
     388     4231300 :     imuDataDownSampledNew.accel_index = imuDataNew.accel_index;
     389             : 
     390             :     // Accumulate the measurement time interval for the delta velocity and angle data
     391     4231300 :     imuDataDownSampledNew.delAngDT += imuDataNew.delAngDT;
     392     4231300 :     imuDataDownSampledNew.delVelDT += imuDataNew.delVelDT;
     393             : 
     394             :     // Rotate quaternon atitude from previous to new and normalise.
     395             :     // Accumulation using quaternions prevents introduction of coning errors due to downsampling
     396     4231300 :     imuQuatDownSampleNew.rotate(imuDataNew.delAng);
     397     4231300 :     imuQuatDownSampleNew.normalize();
     398             : 
     399             :     // Rotate the latest delta velocity into body frame at the start of accumulation
     400     4231300 :     Matrix3F deltaRotMat;
     401     4231300 :     imuQuatDownSampleNew.rotation_matrix(deltaRotMat);
     402             : 
     403             :     // Apply the delta velocity to the delta velocity accumulator
     404     4231300 :     imuDataDownSampledNew.delVel += deltaRotMat*imuDataNew.delVel;
     405             : 
     406             :     // Keep track of the number of IMU frames since the last state prediction
     407     4231300 :     framesSincePredict++;
     408             : 
     409             :     /*
     410             :      * If the target EKF time step has been accumulated, and the frontend has allowed start of a new predict cycle,
     411             :      * then store the accumulated IMU data to be used by the state prediction, ignoring the frontend permission if more
     412             :      * than twice the target time has lapsed. Adjust the target EKF step time threshold to allow for timing jitter in the
     413             :      * IMU data.
     414             :      */
     415     4231300 :     if ((dtIMUavg*(float)framesSincePredict >= (EKF_TARGET_DT-(dtIMUavg*0.5)) &&
     416     4231300 :          startPredictEnabled) || (dtIMUavg*(float)framesSincePredict >= 2.0f*EKF_TARGET_DT)) {
     417             : 
     418             :         // convert the accumulated quaternion to an equivalent delta angle
     419     3256540 :         imuQuatDownSampleNew.to_axis_angle(imuDataDownSampledNew.delAng);
     420             : 
     421             :         // Time stamp the data
     422     3256540 :         imuDataDownSampledNew.time_ms = imuSampleTime_ms;
     423             : 
     424             :         // Write data to the FIFO IMU buffer
     425     3256540 :         storedIMU.push_youngest_element(imuDataDownSampledNew);
     426             : 
     427             :         // calculate the achieved average time step rate for the EKF
     428     3256540 :         ftype dtNow = constrain_ftype(0.5f*(imuDataDownSampledNew.delAngDT+imuDataDownSampledNew.delVelDT),0.0f,10.0f*EKF_TARGET_DT);
     429     3256540 :         dtEkfAvg = 0.98f * dtEkfAvg + 0.02f * dtNow;
     430             : 
     431             :         // zero the accumulated IMU data and quaternion
     432     3256540 :         imuDataDownSampledNew.delAng.zero();
     433     3256540 :         imuDataDownSampledNew.delVel.zero();
     434     3256540 :         imuDataDownSampledNew.delAngDT = 0.0f;
     435     3256540 :         imuDataDownSampledNew.delVelDT = 0.0f;
     436     3256540 :         imuDataDownSampledNew.gyro_index = gyro_index_active;
     437     3256540 :         imuDataDownSampledNew.accel_index = accel_index_active;
     438     3256540 :         imuQuatDownSampleNew[0] = 1.0f;
     439     3256540 :         imuQuatDownSampleNew[3] = imuQuatDownSampleNew[2] = imuQuatDownSampleNew[1] = 0.0f;
     440             : 
     441             :         // reset the counter used to let the frontend know how many frames have elapsed since we started a new update cycle
     442     3256540 :         framesSincePredict = 0;
     443             : 
     444             :         // set the flag to let the filter know it has new IMU data and needs to run
     445     3256540 :         runUpdates = true;
     446             : 
     447             :         // extract the oldest available data from the FIFO buffer
     448     3256540 :         imuDataDelayed = storedIMU.get_oldest_element();
     449             : 
     450             :         // protect against delta time going to zero
     451             :         // TODO - check if calculations can tolerate 0
     452     3256540 :         ftype minDT = 0.1f*dtEkfAvg;
     453     3256540 :         imuDataDelayed.delAngDT = MAX(imuDataDelayed.delAngDT,minDT);
     454     3256540 :         imuDataDelayed.delVelDT = MAX(imuDataDelayed.delVelDT,minDT);
     455             : 
     456     3256540 :         updateTimingStatistics();
     457             :             
     458             :         // correct the extracted IMU data for sensor errors
     459     3256540 :         delAngCorrected = imuDataDelayed.delAng;
     460     3256540 :         delVelCorrected = imuDataDelayed.delVel;
     461     3256540 :         correctDeltaAngle(delAngCorrected, imuDataDelayed.delAngDT, imuDataDelayed.gyro_index);
     462     3256540 :         correctDeltaVelocity(delVelCorrected, imuDataDelayed.delVelDT, imuDataDelayed.accel_index);
     463             : 
     464     3256540 :     } else {
     465             :         // we don't have new IMU data in the buffer so don't run filter updates on this time step
     466      974760 :         runUpdates = false;
     467             :     }
     468     4231300 : }
     469             : 
     470             : // read the delta velocity and corresponding time interval from the IMU
     471             : // return false if data is not available
     472     4231300 : bool NavEKF2_core::readDeltaVelocity(uint8_t ins_index, Vector3F &dVel, ftype &dVel_dt) {
     473     4231300 :     const auto &ins = dal.ins();
     474             : 
     475     4231300 :     if (ins_index < ins.get_accel_count()) {
     476     4231300 :         Vector3f dVelF;
     477             :         float dVel_dtF;
     478     4231300 :         ins.get_delta_velocity(ins_index, dVelF, dVel_dtF);
     479     4231300 :         dVel = dVelF.toftype();
     480     4231300 :         dVel_dt = dVel_dtF;
     481     4231300 :         dVel_dt = MAX(dVel_dt,1.0e-4f);
     482     4231300 :         dVel_dt = MIN(dVel_dt,1.0e-1f);
     483             :         return true;
     484             :     }
     485             :     return false;
     486             : }
     487             : 
     488             : /********************************************************
     489             : *             Global Position Measurement               *
     490             : ********************************************************/
     491             : 
     492             : // check for new valid GPS data and update stored measurement if available
     493     3247888 : void NavEKF2_core::readGpsData()
     494             : {
     495     3247888 :     if (frontend->_fusionModeGPS == 3) {
     496             :         // don't read GPS data if GPS usage disabled
     497             :         return;
     498             :     }
     499             : 
     500             :     // check for new GPS data
     501             :     // do not accept data at a faster rate than 14Hz to avoid overflowing the FIFO buffer
     502     3247888 :     const auto &gps = dal.gps();
     503     3247888 :     if (gps.last_message_time_ms(gps.primary_sensor()) - lastTimeGpsReceived_ms > 70) {
     504      336830 :         if (gps.status() >= AP_GPS_FixType::FIX_3D) {
     505             :             // report GPS fix status
     506      312142 :             gpsCheckStatus.bad_fix = false;
     507             : 
     508             :             // store fix time from previous read
     509             :             const uint32_t secondLastGpsTime_ms = lastTimeGpsReceived_ms;
     510             : 
     511             :             // get current fix time
     512      312142 :             lastTimeGpsReceived_ms = gps.last_message_time_ms(gps.primary_sensor());
     513             : 
     514             : 
     515             :             // estimate when the GPS fix was valid, allowing for GPS processing and other delays
     516             :             // ideally we should be using a timing signal from the GPS receiver to set this time
     517      312142 :             float gps_delay = 0.0;
     518      312142 :             gps.get_lag(gps_delay); // ignore the return value
     519      312142 :             gpsDataNew.time_ms = lastTimeGpsReceived_ms - (uint32_t)(1e3f * gps_delay);
     520             : 
     521             :             // Correct for the average intersampling delay due to the filter updaterate
     522      312142 :             gpsDataNew.time_ms -= localFilterTimeStep_ms/2;
     523             : 
     524             :             // Prevent time delay exceeding age of oldest IMU data in the buffer
     525      312142 :             gpsDataNew.time_ms = MAX(gpsDataNew.time_ms,imuDataDelayed.time_ms);
     526             : 
     527             :             // Get which GPS we are using for position information
     528      312142 :             gpsDataNew.sensor_idx = gps.primary_sensor();
     529             : 
     530             :             // read the NED velocity from the GPS
     531      312142 :             gpsDataNew.vel = gps.velocity().toftype();
     532             : 
     533             :             // Use the speed and position accuracy from the GPS if available, otherwise set it to zero.
     534             :             // Apply a decaying envelope filter with a 5 second time constant to the raw accuracy data
     535      312142 :             ftype alpha = constrain_ftype(0.0002f * (lastTimeGpsReceived_ms - secondLastGpsTime_ms),0.0f,1.0f);
     536      312142 :             gpsSpdAccuracy *= (1.0f - alpha);
     537             :             float gpsSpdAccRaw;
     538      312142 :             if (!gps.speed_accuracy(gpsSpdAccRaw)) {
     539           0 :                 gpsSpdAccuracy = 0.0f;
     540             :             } else {
     541      312142 :                 gpsSpdAccuracy = MAX(gpsSpdAccuracy,gpsSpdAccRaw);
     542      312142 :                 gpsSpdAccuracy = MIN(gpsSpdAccuracy,50.0f);
     543      312142 :                 gpsSpdAccuracy = MAX(gpsSpdAccuracy,frontend->_gpsHorizVelNoise);
     544             :             }
     545      312142 :             gpsPosAccuracy *= (1.0f - alpha);
     546             :             float gpsPosAccRaw;
     547      312142 :             if (!gps.horizontal_accuracy(gpsPosAccRaw)) {
     548           0 :                 gpsPosAccuracy = 0.0f;
     549             :             } else {
     550      312142 :                 gpsPosAccuracy = MAX(gpsPosAccuracy,gpsPosAccRaw);
     551      312142 :                 gpsPosAccuracy = MIN(gpsPosAccuracy,100.0f);
     552      312142 :                 gpsPosAccuracy = MAX(gpsPosAccuracy, frontend->_gpsHorizPosNoise);
     553             :             }
     554      312142 :             gpsHgtAccuracy *= (1.0f - alpha);
     555             :             float gpsHgtAccRaw;
     556      312142 :             if (!gps.vertical_accuracy(gpsHgtAccRaw)) {
     557           0 :                 gpsHgtAccuracy = 0.0f;
     558             :             } else {
     559      312142 :                 gpsHgtAccuracy = MAX(gpsHgtAccuracy,gpsHgtAccRaw);
     560      312142 :                 gpsHgtAccuracy = MIN(gpsHgtAccuracy,100.0f);
     561      312142 :                 gpsHgtAccuracy = MAX(gpsHgtAccuracy, 1.5f * frontend->_gpsHorizPosNoise);
     562             :             }
     563             : 
     564             :             // check if we have enough GPS satellites and increase the gps noise scaler if we don't
     565      312142 :             if (gps.num_sats() >= 6 && (PV_AidingMode == AID_ABSOLUTE)) {
     566      254938 :                 gpsNoiseScaler = 1.0f;
     567       57204 :             } else if (gps.num_sats() == 5 && (PV_AidingMode == AID_ABSOLUTE)) {
     568           0 :                 gpsNoiseScaler = 1.4f;
     569             :             } else { // <= 4 satellites or in constant position mode
     570       57204 :                 gpsNoiseScaler = 2.0f;
     571             :             }
     572             : 
     573             :             // Check if GPS can output vertical velocity, if it is allowed to be used, and set GPS fusion mode accordingly
     574      312142 :             if (gps.have_vertical_velocity() && frontend->_fusionModeGPS == 0) {
     575      312142 :                 useGpsVertVel = true;
     576             :             } else {
     577           0 :                 useGpsVertVel = false;
     578             :             }
     579             : 
     580             :             // Monitor quality of the GPS velocity data both before and after alignment. This updates
     581             :             // GpsGoodToAlign class variable
     582      312142 :             calcGpsGoodToAlign();
     583             : 
     584             :             // Post-alignment checks
     585      312142 :             calcGpsGoodForFlight();
     586             : 
     587             :             // see if we can get origin from frontend
     588      312142 :             if (!validOrigin && frontend->common_origin_valid) {
     589             : 
     590         180 :                 if (!setOrigin(frontend->common_EKF_origin)) {
     591             :                     // set an error as an attempt was made to set the origin more than once
     592           0 :                     INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
     593           0 :                     return;
     594             :                 }
     595             : 
     596             :             }
     597             : 
     598             :             // Read the GPS location in WGS-84 lat,long,height coordinates
     599      312142 :             const Location &gpsloc = gps.location();
     600             : 
     601             :             // Set the EKF origin and magnetic field declination if not previously set  and GPS checks have passed
     602      312142 :             if (gpsGoodToAlign && !validOrigin) {
     603         180 :                 Location gpsloc_fieldelevation = gpsloc;
     604             :                 // if flying, correct for height change from takeoff so that the origin is at field elevation
     605         180 :                 if (inFlight) {
     606           0 :                     gpsloc_fieldelevation.alt += (int32_t)(100.0f * stateStruct.position.z);
     607             :                 }
     608             : 
     609         180 :                 if (!setOrigin(gpsloc_fieldelevation)) {
     610             :                     // set an error as an attempt was made to set the origin more than once
     611           0 :                     INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
     612           0 :                     return;
     613             :                 }
     614             : 
     615             :                 // set the NE earth magnetic field states using the published declination
     616             :                 // and set the corresponding variances and covariances
     617         180 :                 alignMagStateDeclination();
     618             : 
     619             :                 // Set the height of the NED origin
     620         180 :                 ekfGpsRefHgt = (double)0.01 * (double)gpsloc.alt + (double)outputDataNew.position.z;
     621             : 
     622             :                 // Set the uncertainty of the GPS origin height
     623         180 :                 ekfOriginHgtVar = sq(gpsHgtAccuracy);
     624             : 
     625             :             }
     626             : 
     627      312142 :             if (gpsGoodToAlign && !have_table_earth_field) {
     628         394 :                 const auto &compass = dal.compass();
     629         394 :                 if (compass.have_scale_factor(magSelectIndex) &&
     630         394 :                     compass.auto_declination_enabled()) {
     631         394 :                     table_earth_field_ga = AP_Declination::get_earth_field_ga(gpsloc).toftype();
     632         394 :                     table_declination = radians(AP_Declination::get_declination(gpsloc.lat*1.0e-7,
     633         394 :                                                                                 gpsloc.lng*1.0e-7));
     634         394 :                     have_table_earth_field = true;
     635         394 :                     if (frontend->_mag_ef_limit > 0) {
     636             :                         // initialise earth field from tables
     637         394 :                         stateStruct.earth_magfield = table_earth_field_ga;
     638             :                     }
     639             :                 }
     640             :             }
     641             :             
     642             :             // convert GPS measurements to local NED and save to buffer to be fused later if we have a valid origin
     643      312142 :             if (validOrigin) {
     644      287738 :                 gpsDataNew.pos = EKF_origin.get_distance_NE_ftype(gpsloc);
     645      287738 :                 if ((frontend->_originHgtMode & (1<<2)) == 0) {
     646      287738 :                     gpsDataNew.hgt = (float)((double)0.01 * (double)gpsloc.alt - ekfGpsRefHgt);
     647             :                 } else {
     648           0 :                     gpsDataNew.hgt = 0.01 * (gpsloc.alt - EKF_origin.alt);
     649             :                 }
     650      287738 :                 storedGPS.push(gpsDataNew);
     651             :                 // declare GPS available for use
     652      287738 :                 gpsNotAvailable = false;
     653             :             }
     654             : 
     655             :         } else {
     656             :             // report GPS fix status
     657       24688 :             gpsCheckStatus.bad_fix = true;
     658       24688 :             dal.snprintf(prearm_fail_string, sizeof(prearm_fail_string), "Waiting for 3D fix");
     659             :         }
     660             :     }
     661             : }
     662             : 
     663             : // read the delta angle and corresponding time interval from the IMU
     664             : // return false if data is not available
     665     4231300 : bool NavEKF2_core::readDeltaAngle(uint8_t ins_index, Vector3F &dAng, ftype &dAng_dt) {
     666     4231300 :     const auto &ins = dal.ins();
     667             : 
     668     4231300 :     if (ins_index < ins.get_gyro_count()) {
     669     4231300 :         Vector3f dAngF;
     670             :         float dAng_dtF;
     671     4231300 :         ins.get_delta_angle(ins_index, dAngF, dAng_dtF);
     672     4231300 :         dAng = dAngF.toftype();
     673     4231300 :         dAng_dt = dAng_dtF;
     674     4231300 :         dAng_dt = MAX(dAng_dt,1.0e-4f);
     675     4231300 :         dAng_dt = MIN(dAng_dt,1.0e-1f);
     676             :         return true;
     677             :     }
     678             :     return false;
     679             : }
     680             : 
     681             : 
     682             : /********************************************************
     683             : *                  Height Measurements                  *
     684             : ********************************************************/
     685             : 
     686             : // check for new pressure altitude measurement data and update stored measurement if available
     687     3247888 : void NavEKF2_core::readBaroData()
     688             : {
     689             :     // check to see if baro measurement has changed so we know if a new measurement has arrived
     690             :     // do not accept data at a faster rate than 14Hz to avoid overflowing the FIFO buffer
     691     3247888 :     const auto &baro = dal.baro();
     692     3247888 :     if (baro.get_last_update() - lastBaroReceived_ms > 70) {
     693             : 
     694      616346 :         baroDataNew.hgt = baro.get_altitude();
     695             : 
     696             :         // If we are in takeoff mode, the height measurement is limited to be no less than the measurement at start of takeoff
     697             :         // This prevents negative baro disturbances due to copter downwash corrupting the EKF altitude during initial ascent
     698      616346 :         if (dal.get_takeoff_expected() && !assume_zero_sideslip()) {
     699        1226 :             baroDataNew.hgt = MAX(baroDataNew.hgt, meaHgtAtTakeOff);
     700             :         }
     701             : 
     702             :         // time stamp used to check for new measurement
     703      616346 :         lastBaroReceived_ms = baro.get_last_update();
     704             : 
     705             :         // estimate of time height measurement was taken, allowing for delays
     706      616346 :         baroDataNew.time_ms = lastBaroReceived_ms - frontend->_hgtDelay_ms;
     707             : 
     708             :         // Correct for the average intersampling delay due to the filter updaterate
     709      616346 :         baroDataNew.time_ms -= localFilterTimeStep_ms/2;
     710             : 
     711             :         // Prevent time delay exceeding age of oldest IMU data in the buffer
     712      616346 :         baroDataNew.time_ms = MAX(baroDataNew.time_ms,imuDataDelayed.time_ms);
     713             : 
     714             :         // save baro measurement to buffer to be fused later
     715      616346 :         storedBaro.push(baroDataNew);
     716             :     }
     717     3247888 : }
     718             : 
     719             : // calculate filtered offset between baro height measurement and EKF height estimate
     720             : // offset should be subtracted from baro measurement to match filter estimate
     721             : // offset is used to enable reversion to baro from alternate height data source
     722           0 : void NavEKF2_core::calcFiltBaroOffset()
     723             : {
     724             :     // Apply a first order LPF with spike protection
     725           0 :     baroHgtOffset += 0.1f * constrain_ftype(baroDataDelayed.hgt + stateStruct.position.z - baroHgtOffset, -5.0f, 5.0f);
     726           0 : }
     727             : 
     728             : // correct the height of the EKF origin to be consistent with GPS Data using a Bayes filter.
     729           0 : void NavEKF2_core::correctEkfOriginHeight()
     730             : {
     731             :     // Estimate the WGS-84 height of the EKF's origin using a Bayes filter
     732             : 
     733             :     // calculate the variance of our a-priori estimate of the ekf origin height
     734           0 :     ftype deltaTime = constrain_ftype(0.001f * (imuDataDelayed.time_ms - lastOriginHgtTime_ms), 0.0f, 1.0f);
     735           0 :     if (activeHgtSource == HGT_SOURCE_BARO) {
     736             :         // Use the baro drift rate
     737             :         const ftype baroDriftRate = 0.05f;
     738           0 :         ekfOriginHgtVar += sq(baroDriftRate * deltaTime);
     739           0 :     } else if (activeHgtSource == HGT_SOURCE_RNG) {
     740             :         // use the worse case expected terrain gradient and vehicle horizontal speed
     741             :         const ftype maxTerrGrad = 0.25f;
     742           0 :         ekfOriginHgtVar += sq(maxTerrGrad * stateStruct.velocity.xy().length() * deltaTime);
     743             :     } else {
     744             :         // by definition our height source is absolute so cannot run this filter
     745             :         return;
     746             :     }
     747           0 :     lastOriginHgtTime_ms = imuDataDelayed.time_ms;
     748             : 
     749             :     // calculate the observation variance assuming EKF error relative to datum is independent of GPS observation error
     750             :     // when not using GPS as height source
     751           0 :     ftype originHgtObsVar = sq(gpsHgtAccuracy) + P[8][8];
     752             : 
     753             :     // calculate the correction gain
     754           0 :     ftype gain = ekfOriginHgtVar / (ekfOriginHgtVar + originHgtObsVar);
     755             : 
     756             :     // calculate the innovation
     757           0 :     ftype innovation = - stateStruct.position.z - gpsDataDelayed.hgt;
     758             : 
     759             :     // check the innovation variance ratio
     760           0 :     ftype ratio = sq(innovation) / (ekfOriginHgtVar + originHgtObsVar);
     761             : 
     762             :     // correct the EKF origin and variance estimate if the innovation is less than 5-sigma
     763           0 :     if (ratio < 25.0f && gpsAccuracyGood) {
     764           0 :         ekfGpsRefHgt -= (double)(gain * innovation);
     765           0 :         ekfOriginHgtVar -= MAX(gain * ekfOriginHgtVar , 0.0f);
     766             :     }
     767             : }
     768             : 
     769             : /********************************************************
     770             : *                Air Speed Measurements                 *
     771             : ********************************************************/
     772             : 
     773             : // check for new airspeed data and update stored measurements if available
     774     3230372 : void NavEKF2_core::readAirSpdData()
     775             : {
     776             :     // if airspeed reading is valid and is set by the user to be used and has been updated then
     777             :     // we take a new reading, convert from EAS to TAS and set the flag letting other functions
     778             :     // know a new measurement is available
     779     3230372 :     const auto *aspeed = dal.airspeed();
     780     2968482 :     if (aspeed &&
     781     2968482 :         aspeed->use() &&
     782     6046138 :         aspeed->healthy() &&
     783     2815694 :         aspeed->last_update_ms() != timeTasReceived_ms) {
     784      556412 :         tasDataNew.tas = aspeed->get_airspeed() * dal.get_EAS2TAS();
     785      556412 :         timeTasReceived_ms = aspeed->last_update_ms();
     786      556412 :         tasDataNew.time_ms = timeTasReceived_ms - frontend->tasDelay_ms;
     787             : 
     788             :         // Correct for the average intersampling delay due to the filter update rate
     789      556412 :         tasDataNew.time_ms -= localFilterTimeStep_ms/2;
     790             : 
     791             :         // Save data into the buffer to be fused when the fusion time horizon catches up with it
     792      556412 :         storedTAS.push(tasDataNew);
     793             :     }
     794             :     // Check the buffer for measurements that have been overtaken by the fusion time horizon and need to be fused
     795     3230372 :     tasDataToFuse = storedTAS.recall(tasDataDelayed,imuDataDelayed.time_ms);
     796     3230372 : }
     797             : 
     798             : /********************************************************
     799             : *              Range Beacon Measurements                *
     800             : ********************************************************/
     801             : 
     802             : #if AP_BEACON_ENABLED
     803             : // check for new range beacon data and push to data buffer if available
     804     3248540 : void NavEKF2_core::readRngBcnData()
     805             : {
     806             :     // get the location of the beacon data
     807     3248540 :     const auto *beacon = dal.beacon();
     808             : 
     809             :     // exit immediately if no beacon object
     810     3248540 :     if (beacon == nullptr) {
     811     3248540 :         return;
     812             :     }
     813             : 
     814             :     // get the number of beacons in use
     815           0 :     N_beacons = beacon->count();
     816             : 
     817             :     // search through all the beacons for new data and if we find it stop searching and push the data into the observation buffer
     818             :     bool newDataToPush = false;
     819             :     uint8_t numRngBcnsChecked = 0;
     820             :     // start the search one index up from where we left it last time
     821           0 :     uint8_t index = lastRngBcnChecked;
     822           0 :     while (!newDataToPush && numRngBcnsChecked < N_beacons) {
     823             :         // track the number of beacons checked
     824           0 :         numRngBcnsChecked++;
     825             : 
     826             :         // move to next beacon, wrap index if necessary
     827           0 :         index++;
     828           0 :         if (index >= N_beacons) {
     829             :             index = 0;
     830             :         }
     831             : 
     832             :         // check that the beacon is healthy and has new data
     833           0 :         if (beacon->beacon_healthy(index) &&
     834           0 :                 beacon->beacon_last_update_ms(index) != lastTimeRngBcn_ms[index])
     835             :         {
     836             :             // set the timestamp, correcting for measurement delay and average intersampling delay due to the filter update rate
     837           0 :             lastTimeRngBcn_ms[index] = beacon->beacon_last_update_ms(index);
     838           0 :             rngBcnDataNew.time_ms = lastTimeRngBcn_ms[index] - frontend->_rngBcnDelay_ms - localFilterTimeStep_ms/2;
     839             : 
     840             :             // set the range noise
     841             :             // TODO the range library should provide the noise/accuracy estimate for each beacon
     842           0 :             rngBcnDataNew.rngErr = frontend->_rngBcnNoise.get();
     843             : 
     844             :             // set the range measurement
     845           0 :             rngBcnDataNew.rng = beacon->beacon_distance(index);
     846             : 
     847             :             // set the beacon position
     848           0 :             rngBcnDataNew.beacon_posNED = beacon->beacon_position(index).toftype();
     849             : 
     850             :             // identify the beacon identifier
     851           0 :             rngBcnDataNew.beacon_ID = index;
     852             : 
     853             :             // indicate we have new data to push to the buffer
     854             :             newDataToPush = true;
     855             : 
     856             :             // update the last checked index
     857           0 :             lastRngBcnChecked = index;
     858             :         }
     859             :     }
     860             : 
     861             :     // Check if the beacon system has returned a 3D fix
     862           0 :     Vector3f beaconVehiclePosNEDF;
     863             :     float beaconVehiclePosErrF;
     864           0 :     if (beacon->get_vehicle_position_ned(beaconVehiclePosNEDF, beaconVehiclePosErrF)) {
     865           0 :         rngBcnLast3DmeasTime_ms = imuSampleTime_ms;
     866             :     }
     867           0 :     beaconVehiclePosNED = beaconVehiclePosNEDF.toftype();
     868           0 :     beaconVehiclePosErr = beaconVehiclePosErrF;
     869             : 
     870             : 
     871             :     // Check if the range beacon data can be used to align the vehicle position
     872           0 :     if (imuSampleTime_ms - rngBcnLast3DmeasTime_ms < 250 && beaconVehiclePosErr < 1.0f && rngBcnAlignmentCompleted) {
     873             :         // check for consistency between the position reported by the beacon and the position from the 3-State alignment filter
     874           0 :         ftype posDiffSq = sq(receiverPos.x - beaconVehiclePosNED.x) + sq(receiverPos.y - beaconVehiclePosNED.y);
     875           0 :         ftype posDiffVar = sq(beaconVehiclePosErr) + receiverPosCov[0][0] + receiverPosCov[1][1];
     876           0 :         if (posDiffSq < 9.0f*posDiffVar) {
     877           0 :             rngBcnGoodToAlign = true;
     878             :             // Set the EKF origin and magnetic field declination if not previously set
     879           0 :             if (!validOrigin && PV_AidingMode != AID_ABSOLUTE) {
     880             :                 // get origin from beacon system
     881           0 :                 Location origin_loc;
     882           0 :                 if (beacon->get_origin(origin_loc)) {
     883           0 :                     setOriginLLH(origin_loc);
     884             : 
     885             :                     // set the NE earth magnetic field states using the published declination
     886             :                     // and set the corresponding variances and covariances
     887           0 :                     alignMagStateDeclination();
     888             : 
     889             :                     // Set the uncertainty of the origin height
     890           0 :                     ekfOriginHgtVar = sq(beaconVehiclePosErr);
     891             :                 }
     892             :             }
     893             :         } else {
     894           0 :             rngBcnGoodToAlign = false;
     895             :         }
     896             :     } else {
     897           0 :         rngBcnGoodToAlign = false;
     898             :     }
     899             : 
     900             :     // Save data into the buffer to be fused when the fusion time horizon catches up with it
     901           0 :     if (newDataToPush) {
     902           0 :         storedRangeBeacon.push(rngBcnDataNew);
     903             :     }
     904             : 
     905             :     // Check the buffer for measurements that have been overtaken by the fusion time horizon and need to be fused
     906           0 :     rngBcnDataToFuse = storedRangeBeacon.recall(rngBcnDataDelayed,imuDataDelayed.time_ms);
     907             : 
     908             : }
     909             : #endif  // AP_BEACON_ENABLED
     910             : 
     911             : /*
     912             :   update timing statistics structure
     913             :  */
     914     3256540 : void NavEKF2_core::updateTimingStatistics(void)
     915             : {
     916     3256540 :     if (timing.count == 0) {
     917       12886 :         timing.dtIMUavg_max = dtIMUavg;
     918       12886 :         timing.dtIMUavg_min = dtIMUavg;
     919       12886 :         timing.dtEKFavg_max = dtEkfAvg;
     920       12886 :         timing.dtEKFavg_min = dtEkfAvg;
     921       12886 :         timing.delAngDT_max = imuDataDelayed.delAngDT;
     922       12886 :         timing.delAngDT_min = imuDataDelayed.delAngDT;
     923       12886 :         timing.delVelDT_max = imuDataDelayed.delVelDT;
     924       12886 :         timing.delVelDT_min = imuDataDelayed.delVelDT;
     925             :     } else {
     926     3243654 :         timing.dtIMUavg_max = MAX(timing.dtIMUavg_max, dtIMUavg);
     927     3243654 :         timing.dtIMUavg_min = MIN(timing.dtIMUavg_min, dtIMUavg);
     928     3243654 :         timing.dtEKFavg_max = MAX(timing.dtEKFavg_max, dtEkfAvg);
     929     3243654 :         timing.dtEKFavg_min = MIN(timing.dtEKFavg_min, dtEkfAvg);
     930     3243654 :         timing.delAngDT_max = MAX(timing.delAngDT_max, imuDataDelayed.delAngDT);
     931     3243654 :         timing.delAngDT_min = MIN(timing.delAngDT_min, imuDataDelayed.delAngDT);
     932     3243654 :         timing.delVelDT_max = MAX(timing.delVelDT_max, imuDataDelayed.delVelDT);
     933     3243654 :         timing.delVelDT_min = MIN(timing.delVelDT_min, imuDataDelayed.delVelDT);
     934             :     }
     935     3256540 :     timing.count++;
     936     3256540 : }
     937             : 
     938           0 : void NavEKF2_core::writeExtNavData(const Vector3f &pos, const Quaternion &quat, float posErr, float angErr, uint32_t timeStamp_ms, uint16_t delay_ms, uint32_t resetTime_ms)
     939             : {
     940             :     // protect against NaN
     941           0 :     if (pos.is_nan() || isnan(posErr) || quat.is_nan() || isnan(angErr)) {
     942           0 :         return;
     943             :     }
     944             : 
     945             :     // limit update rate to maximum allowed by sensor buffers and fusion process
     946             :     // don't try to write to buffer until the filter has been initialised
     947           0 :     if ((timeStamp_ms - extNavMeasTime_ms) < 20) {
     948             :         return;
     949             :     } else {
     950           0 :         extNavMeasTime_ms = timeStamp_ms;
     951             :     }
     952             : 
     953           0 :     if (resetTime_ms != extNavLastPosResetTime_ms) {
     954           0 :         extNavDataNew.posReset = true;
     955           0 :         extNavLastPosResetTime_ms = resetTime_ms;
     956             :     } else {
     957           0 :         extNavDataNew.posReset = false;
     958             :     }
     959             : 
     960           0 :     extNavDataNew.pos = pos.toftype();
     961           0 :     extNavDataNew.quat = quat.toftype();
     962           0 :     extNavDataNew.posErr = posErr;
     963           0 :     extNavDataNew.angErr = angErr;
     964           0 :     timeStamp_ms = timeStamp_ms - delay_ms;
     965             :     // Correct for the average intersampling delay due to the filter updaterate
     966           0 :     timeStamp_ms -= localFilterTimeStep_ms/2;
     967             :     // Prevent time delay exceeding age of oldest IMU data in the buffer
     968           0 :     timeStamp_ms = MAX(timeStamp_ms,imuDataDelayed.time_ms);
     969           0 :     extNavDataNew.time_ms = timeStamp_ms;
     970             : 
     971           0 :     storedExtNav.push(extNavDataNew);
     972             : }
     973             : 
     974             : /*
     975             :   return declination in radians
     976             : */
     977      609763 : ftype NavEKF2_core::MagDeclination(void) const
     978             : {
     979             :     // if we are using the WMM tables then use the table declination
     980             :     // to ensure consistency with the table mag field. Otherwise use
     981             :     // the declination from the compass library
     982      609763 :     if (have_table_earth_field && frontend->_mag_ef_limit > 0) {
     983      563294 :         return table_declination;
     984             :     }
     985       46469 :     if (!use_compass()) {
     986             :         return 0;
     987             :     }
     988       46468 :     return dal.compass().get_declination();
     989             : }
     990             : 
     991             : /*
     992             :   update estimates of inactive bias states. This keeps inactive IMUs
     993             :   as hot-spares so we can switch to them without causing a jump in the
     994             :   error
     995             :  */
     996     4231300 : void NavEKF2_core::learnInactiveBiases(void)
     997             : {
     998             : #if INS_MAX_INSTANCES == 1
     999             :     inactiveBias[0].gyro_bias = stateStruct.gyro_bias;
    1000             :     inactiveBias[0].gyro_scale = stateStruct.gyro_scale;
    1001             :     inactiveBias[0].accel_zbias = stateStruct.accel_zbias;
    1002             : #else
    1003     4231300 :     const auto &ins = dal.ins();
    1004             : 
    1005             :     // learn gyro biases
    1006    16925200 :     for (uint8_t i=0; i<INS_MAX_INSTANCES; i++) {
    1007    12693900 :         if (!ins.use_gyro(i)) {
    1008             :             // can't use this gyro
    1009     4179824 :             continue;
    1010             :         }
    1011     8514076 :         if (gyro_index_active == i) {
    1012             :             // use current estimates from main filter of gyro bias and scale
    1013     4231300 :             inactiveBias[i].gyro_bias = stateStruct.gyro_bias;
    1014     4231300 :             inactiveBias[i].gyro_scale = stateStruct.gyro_scale;
    1015             :         } else {
    1016             :             // get filtered gyro and use the difference between the
    1017             :             // corrected gyro on the active IMU and the inactive IMU
    1018             :             // to move the inactive bias towards the right value
    1019     4282776 :             Vector3F filtered_gyro_active = ins.get_gyro(gyro_index_active).toftype() - (stateStruct.gyro_bias/dtEkfAvg);
    1020     4282776 :             Vector3F filtered_gyro_inactive = ins.get_gyro(i).toftype() - (inactiveBias[i].gyro_bias/dtEkfAvg);
    1021     4282776 :             Vector3F error = filtered_gyro_active - filtered_gyro_inactive;
    1022             : 
    1023             :             // prevent a single large error from contaminating bias estimate
    1024             :             const ftype bias_limit = radians(5);
    1025     4282776 :             error.x = constrain_ftype(error.x, -bias_limit, bias_limit);
    1026     4282776 :             error.y = constrain_ftype(error.y, -bias_limit, bias_limit);
    1027     4282776 :             error.z = constrain_ftype(error.z, -bias_limit, bias_limit);
    1028             : 
    1029             :             // slowly bring the inactive gyro in line with the active gyro. This corrects a 5 deg/sec
    1030             :             // gyro bias error in around 1 minute
    1031     4282776 :             inactiveBias[i].gyro_bias -= error * (1.0e-4f * dtEkfAvg);
    1032             :         }
    1033             :     }
    1034             : 
    1035             :     // learn accel biases
    1036    16925200 :     for (uint8_t i=0; i<INS_MAX_INSTANCES; i++) {
    1037    12693900 :         if (!ins.use_accel(i)) {
    1038             :             // can't use this accel
    1039     4179824 :             continue;
    1040             :         }
    1041     8514076 :         if (accel_index_active == i) {
    1042             :             // use current estimate from main filter
    1043     4231300 :             inactiveBias[i].accel_zbias = stateStruct.accel_zbias;
    1044             :         } else {
    1045             :             // get filtered accel and use the difference between the
    1046             :             // corrected accel on the active IMU and the inactive IMU
    1047             :             // to move the inactive bias towards the right value
    1048     4282776 :             ftype filtered_accel_active = ins.get_accel(accel_index_active).z - (stateStruct.accel_zbias/dtEkfAvg);
    1049     4282776 :             ftype filtered_accel_inactive = ins.get_accel(i).z - (inactiveBias[i].accel_zbias/dtEkfAvg);
    1050     4282776 :             ftype error = filtered_accel_active - filtered_accel_inactive;
    1051             : 
    1052             :             // prevent a single large error from contaminating bias estimate
    1053             :             const ftype bias_limit = 1; // m/s/s
    1054     4282776 :             error = constrain_ftype(error, -bias_limit, bias_limit);
    1055             : 
    1056             :             // slowly bring the inactive accel in line with the active accel
    1057             :             // this learns 0.5m/s/s bias in about 1 minute
    1058     4282776 :             inactiveBias[i].accel_zbias -= error * (1.0e-4f * dtEkfAvg);
    1059             :         }
    1060             :     }
    1061             : #endif
    1062     4231300 : }
    1063             : 
    1064             : // Writes the default equivalent airspeed in m/s to be used in forward flight if a measured airspeed is required and not available.
    1065           0 : void NavEKF2_core::writeDefaultAirSpeed(float airspeed)
    1066             : {
    1067           0 :     defaultAirSpeed = airspeed;
    1068           0 : }
    1069             : 
    1070           0 : void NavEKF2_core::writeExtNavVelData(const Vector3f &vel, float err, uint32_t timeStamp_ms, uint16_t delay_ms)
    1071             : {
    1072             :     // protect against NaN
    1073           0 :     if (vel.is_nan() || isnan(err)) {
    1074           0 :         return;
    1075             :     }
    1076             : 
    1077           0 :     if ((timeStamp_ms - extNavVelMeasTime_ms) < 20) {
    1078             :         return;
    1079             :     }
    1080             : 
    1081           0 :     extNavVelMeasTime_ms = timeStamp_ms;
    1082           0 :     useExtNavVel = true;
    1083           0 :     extNavVelNew.vel = vel.toftype();
    1084           0 :     extNavVelNew.err = err;
    1085           0 :     timeStamp_ms = timeStamp_ms - delay_ms;
    1086             :     // Correct for the average intersampling delay due to the filter updaterate
    1087           0 :     timeStamp_ms -= localFilterTimeStep_ms/2;
    1088             :     // Prevent time delay exceeding age of oldest IMU data in the buffer
    1089           0 :     timeStamp_ms = MAX(timeStamp_ms,imuDataDelayed.time_ms);
    1090           0 :     extNavVelNew.time_ms = timeStamp_ms;
    1091           0 :     storedExtNavVel.push(extNavVelNew);
    1092             : }

Generated by: LCOV version 1.14