2016-07-14 02:08:43 -03:00
# include <AP_HAL/AP_HAL.h>
2020-11-05 19:30:16 -04:00
# include "AP_NavEKF3.h"
2016-07-14 02:08:43 -03:00
# include "AP_NavEKF3_core.h"
2020-11-05 19:30:16 -04:00
# include <AP_DAL/AP_DAL.h>
2016-07-14 02:08:43 -03:00
// Check basic filter health metrics and return a consolidated health status
bool NavEKF3_core : : healthy ( void ) const
{
uint16_t faultInt ;
getFilterFaults ( faultInt ) ;
if ( faultInt > 0 ) {
return false ;
}
if ( velTestRatio > 1 & & posTestRatio > 1 & & hgtTestRatio > 1 ) {
// all three metrics being above 1 means the filter is
// extremely unhealthy.
return false ;
}
// Give the filter a second to settle before use
if ( ( imuSampleTime_ms - ekfStartTime_ms ) < 1000 ) {
return false ;
}
// position and height innovations must be within limits when on-ground and in a static mode of operation
float horizErrSq = sq ( innovVelPos [ 3 ] ) + sq ( innovVelPos [ 4 ] ) ;
if ( onGround & & ( PV_AidingMode = = AID_NONE ) & & ( ( horizErrSq > 1.0f ) | | ( fabsf ( hgtInnovFiltState ) > 1.0f ) ) ) {
return false ;
}
// all OK
return true ;
}
// Return a consolidated error score where higher numbers represent larger errors
// Intended to be used by the front-end to determine which is the primary EKF
float NavEKF3_core : : errorScore ( ) const
{
float score = 0.0f ;
if ( tiltAlignComplete & & yawAlignComplete ) {
// Check GPS fusion performance
score = MAX ( score , 0.5f * ( velTestRatio + posTestRatio ) ) ;
// Check altimeter fusion performance
score = MAX ( score , hgtTestRatio ) ;
2020-06-19 03:21:46 -03:00
// Check airspeed fusion performance - only when we are using at least 2 airspeed sensors so we can switch lanes with
// a better one. This only comes into effect for a forward flight vehicle. A sensitivity factor of 0.3 is added to keep the
// EKF less sensitive to innovations arising due events like strong gusts of wind, thus, prevent reporting high error scores
if ( assume_zero_sideslip ( ) ) {
2020-11-07 02:24:13 -04:00
const auto * arsp = dal . airspeed ( ) ;
2020-06-19 03:21:46 -03:00
if ( arsp - > get_num_sensors ( ) > = 2 & & ( frontend - > _affinity & EKF_AFFINITY_ARSP ) ) {
score = MAX ( score , 0.3f * tasTestRatio ) ;
}
}
// Check magnetometer fusion performance - need this when magnetometer affinity is enabled to override the inherent compass
// switching mechanism, and instead be able to move to a better lane
if ( frontend - > _affinity & EKF_AFFINITY_MAG ) {
score = MAX ( score , 0.3f * ( magTestRatio . x + magTestRatio . y + magTestRatio . z ) ) ;
}
2016-07-14 02:08:43 -03:00
}
return score ;
}
2017-03-16 02:59:19 -03:00
// return data for debugging body frame odometry fusion
uint32_t NavEKF3_core : : getBodyFrameOdomDebug ( Vector3f & velInnov , Vector3f & velInnovVar )
{
velInnov . x = innovBodyVel [ 0 ] ;
velInnov . y = innovBodyVel [ 1 ] ;
velInnov . z = innovBodyVel [ 2 ] ;
velInnovVar . x = varInnovBodyVel [ 0 ] ;
velInnovVar . y = varInnovBodyVel [ 1 ] ;
velInnovVar . z = varInnovBodyVel [ 2 ] ;
2017-07-27 02:01:48 -03:00
return MAX ( bodyOdmDataDelayed . time_ms , wheelOdmDataDelayed . time_ms ) ;
2017-03-16 02:59:19 -03:00
}
2016-07-14 02:08:43 -03:00
// provides the height limit to be observed by the control loops
// returns false if no height limiting is required
// this is needed to ensure the vehicle does not fly too high when using optical flow navigation
bool NavEKF3_core : : getHeightControlLimit ( float & height ) const
{
// only ask for limiting if we are doing optical flow navigation
2020-11-16 08:40:57 -04:00
if ( frontend - > sources . useVelXYSource ( AP_NavEKF_Source : : SourceXY : : OPTFLOW ) & & ( PV_AidingMode = = AID_RELATIVE ) & & flowDataValid ) {
2016-07-14 02:08:43 -03:00
// If are doing optical flow nav, ensure the height above ground is within range finder limits after accounting for vehicle tilt and control errors
2020-11-07 02:24:13 -04:00
const auto * _rng = dal . rangefinder ( ) ;
2019-11-26 02:45:14 -04:00
if ( _rng = = nullptr ) {
// we really, really shouldn't be here.
return false ;
}
height = MAX ( float ( _rng - > max_distance_cm_orient ( ROTATION_PITCH_270 ) ) * 0.007f - 1.0f , 1.0f ) ;
2016-07-14 02:08:43 -03:00
// If we are are not using the range finder as the height reference, then compensate for the difference between terrain and EKF origin
2020-11-16 08:40:57 -04:00
if ( frontend - > sources . getPosZSource ( ) ! = AP_NavEKF_Source : : SourceZ : : RANGEFINDER ) {
2016-07-14 02:08:43 -03:00
height - = terrainState ;
}
return true ;
} else {
return false ;
}
}
// return the Euler roll, pitch and yaw angle in radians
void NavEKF3_core : : getEulerAngles ( Vector3f & euler ) const
{
outputDataNew . quat . to_euler ( euler . x , euler . y , euler . z ) ;
2020-11-07 02:24:13 -04:00
euler = euler - dal . get_trim ( ) ;
2016-07-14 02:08:43 -03:00
}
// return body axis gyro bias estimates in rad/sec
void NavEKF3_core : : getGyroBias ( Vector3f & gyroBias ) const
{
if ( dtEkfAvg < 1e-6 f ) {
gyroBias . zero ( ) ;
return ;
}
gyroBias = stateStruct . gyro_bias / dtEkfAvg ;
}
// return accelerometer bias in m/s/s
void NavEKF3_core : : getAccelBias ( Vector3f & accelBias ) const
{
if ( ! statesInitialised ) {
accelBias . zero ( ) ;
return ;
}
accelBias = stateStruct . accel_bias / dtEkfAvg ;
}
2020-09-16 19:10:10 -03:00
// return estimated 1-sigma tilt error in radians
2016-07-14 02:08:43 -03:00
void NavEKF3_core : : getTiltError ( float & ang ) const
{
2020-09-16 19:10:10 -03:00
ang = sqrtf ( MAX ( tiltErrorVariance , 0.0f ) ) ;
2016-07-14 02:08:43 -03:00
}
// return the transformation matrix from XYZ (body) to NED axes
void NavEKF3_core : : getRotationBodyToNED ( Matrix3f & mat ) const
{
outputDataNew . quat . rotation_matrix ( mat ) ;
2020-11-07 02:24:13 -04:00
mat = mat * dal . get_rotation_vehicle_body_to_autopilot_body ( ) ;
2016-07-14 02:08:43 -03:00
}
// return the quaternions defining the rotation from NED to XYZ (body) axes
void NavEKF3_core : : getQuaternion ( Quaternion & ret ) const
{
ret = outputDataNew . quat ;
}
// return the amount of yaw angle change due to the last yaw angle reset in radians
// returns the time of the last yaw angle reset or 0 if no reset has ever occurred
uint32_t NavEKF3_core : : getLastYawResetAngle ( float & yawAng ) const
{
yawAng = yawResetAngle ;
return lastYawReset_ms ;
}
// return the amount of NE position change due to the last position reset in metres
// returns the time of the last reset or 0 if no reset has ever occurred
uint32_t NavEKF3_core : : getLastPosNorthEastReset ( Vector2f & pos ) const
{
pos = posResetNE ;
return lastPosReset_ms ;
}
// return the amount of vertical position change due to the last vertical position reset in metres
// returns the time of the last reset or 0 if no reset has ever occurred
uint32_t NavEKF3_core : : getLastPosDownReset ( float & posD ) const
{
posD = posResetD ;
return lastPosResetD_ms ;
}
// return the amount of NE velocity change due to the last velocity reset in metres/sec
// returns the time of the last reset or 0 if no reset has ever occurred
uint32_t NavEKF3_core : : getLastVelNorthEastReset ( Vector2f & vel ) const
{
vel = velResetNE ;
return lastVelReset_ms ;
}
// return the NED wind speed estimates in m/s (positive is air moving in the direction of the axis)
void NavEKF3_core : : getWind ( Vector3f & wind ) const
{
wind . x = stateStruct . wind_vel . x ;
wind . y = stateStruct . wind_vel . y ;
wind . z = 0.0f ; // currently don't estimate this
}
// return the NED velocity of the body frame origin in m/s
//
void NavEKF3_core : : getVelNED ( Vector3f & vel ) const
{
// correct for the IMU position offset (EKF calculations are at the IMU)
vel = outputDataNew . velocity + velOffsetNED ;
}
2019-02-22 19:35:24 -04:00
// Return the rate of change of vertical position in the down direction (dPosD/dt) of the body frame origin in m/s
2016-07-14 02:08:43 -03:00
float NavEKF3_core : : getPosDownDerivative ( void ) const
{
// return the value calculated from a complementary filter applied to the EKF height and vertical acceleration
// correct for the IMU offset (EKF calculations are at the IMU)
2019-10-12 19:26:47 -03:00
return vertCompFiltState . vel + velOffsetNED . z ;
2016-07-14 02:08:43 -03:00
}
// This returns the specific forces in the NED frame
void NavEKF3_core : : getAccelNED ( Vector3f & accelNED ) const {
accelNED = velDotNED ;
accelNED . z - = GRAVITY_MSS ;
}
// Write the last estimated NE position of the body frame origin relative to the reference point (m).
// Return true if the estimate is valid
bool NavEKF3_core : : getPosNE ( Vector2f & posNE ) const
{
// There are three modes of operation, absolute position (GPS fusion), relative position (optical flow fusion) and constant position (no position estimate available)
if ( PV_AidingMode ! = AID_NONE ) {
// This is the normal mode of operation where we can use the EKF position states
// correct for the IMU offset (EKF calculations are at the IMU)
posNE . x = outputDataNew . position . x + posOffsetNED . x ;
posNE . y = outputDataNew . position . y + posOffsetNED . y ;
return true ;
} else {
// In constant position mode the EKF position states are at the origin, so we cannot use them as a position estimate
if ( validOrigin ) {
2020-11-07 02:24:13 -04:00
auto & gps = dal . gps ( ) ;
2020-11-05 19:30:16 -04:00
if ( ( gps . status ( selected_gps ) > = AP_DAL_GPS : : GPS_OK_FIX_2D ) ) {
2016-07-14 02:08:43 -03:00
// If the origin has been set and we have GPS, then return the GPS position relative to the origin
2020-06-23 13:35:34 -03:00
const struct Location & gpsloc = gps . location ( selected_gps ) ;
2019-04-08 10:16:20 -03:00
const Vector2f tempPosNE = EKF_origin . get_distance_NE ( gpsloc ) ;
2016-07-14 02:08:43 -03:00
posNE . x = tempPosNE . x ;
posNE . y = tempPosNE . y ;
return false ;
} else if ( rngBcnAlignmentStarted ) {
// If we are attempting alignment using range beacon data, then report the position
posNE . x = receiverPos . x ;
posNE . y = receiverPos . y ;
return false ;
} else {
// If no GPS fix is available, all we can do is provide the last known position
posNE . x = outputDataNew . position . x ;
posNE . y = outputDataNew . position . y ;
return false ;
}
} else {
// If the origin has not been set, then we have no means of providing a relative position
posNE . x = 0.0f ;
posNE . y = 0.0f ;
return false ;
}
}
return false ;
}
2017-05-30 22:22:37 -03:00
// Write the last calculated D position of the body frame origin relative to the EKF origin (m).
2016-07-14 02:08:43 -03:00
// Return true if the estimate is valid
bool NavEKF3_core : : getPosD ( float & posD ) const
{
// The EKF always has a height estimate regardless of mode of operation
2017-05-30 22:22:37 -03:00
// Correct for the IMU offset (EKF calculations are at the IMU)
2017-07-15 21:37:46 -03:00
// Also correct for changes to the origin height
if ( ( frontend - > _originHgtMode & ( 1 < < 2 ) ) = = 0 ) {
2017-05-30 22:22:37 -03:00
// Any sensor height drift corrections relative to the WGS-84 reference are applied to the origin.
posD = outputDataNew . position . z + posOffsetNED . z ;
} else {
// The origin height is static and corrections are applied to the local vertical position
// so that height returned by getLLH() = height returned by getOriginLLH - posD
posD = outputDataNew . position . z + posOffsetNED . z + 0.01f * ( float ) EKF_origin . alt - ( float ) ekfGpsRefHgt ;
}
2016-07-14 02:08:43 -03:00
// Return the current height solution status
return filterStatus . flags . vert_pos ;
}
// return the estimated height of body frame origin above ground level
bool NavEKF3_core : : getHAGL ( float & HAGL ) const
{
HAGL = terrainState - outputDataNew . position . z - posOffsetNED . z ;
// If we know the terrain offset and altitude, then we have a valid height above ground estimate
return ! hgtTimeout & & gndOffsetValid & & healthy ( ) ;
}
// Return the last calculated latitude, longitude and height in WGS-84
// If a calculated location isn't available, return a raw GPS measurement
// The status will return true if a calculation or raw measurement is available
// The getFilterStatus() function provides a more detailed description of data health and must be checked if data is to be used for flight control
bool NavEKF3_core : : getLLH ( struct Location & loc ) const
{
2020-11-07 02:24:13 -04:00
const auto & gps = dal . gps ( ) ;
2020-11-05 19:30:16 -04:00
2018-09-13 17:39:43 -03:00
Location origin ;
float posD ;
2017-12-01 21:02:55 -04:00
2018-09-13 17:39:43 -03:00
if ( getPosD ( posD ) & & getOriginLLH ( origin ) ) {
2016-07-14 02:08:43 -03:00
// Altitude returned is an absolute altitude relative to the WGS-84 spherioid
2018-09-13 17:39:43 -03:00
loc . alt = origin . alt - posD * 100 ;
2019-01-01 20:08:23 -04:00
loc . relative_alt = 0 ;
loc . terrain_alt = 0 ;
2016-07-14 02:08:43 -03:00
// there are three modes of operation, absolute position (GPS fusion), relative position (optical flow fusion) and constant position (no aiding)
if ( filterStatus . flags . horiz_pos_abs | | filterStatus . flags . horiz_pos_rel ) {
loc . lat = EKF_origin . lat ;
loc . lng = EKF_origin . lng ;
2019-02-24 20:16:24 -04:00
loc . offset ( outputDataNew . position . x , outputDataNew . position . y ) ;
2016-07-14 02:08:43 -03:00
return true ;
} else {
2019-02-22 19:35:24 -04:00
// we could be in constant position mode because the vehicle has taken off without GPS, or has lost GPS
2016-07-14 02:08:43 -03:00
// in this mode we cannot use the EKF states to estimate position so will return the best available data
2020-11-05 19:30:16 -04:00
if ( ( gps . status ( selected_gps ) > = AP_DAL_GPS : : GPS_OK_FIX_2D ) ) {
2016-07-14 02:08:43 -03:00
// we have a GPS position fix to return
2020-06-23 13:35:34 -03:00
const struct Location & gpsloc = gps . location ( selected_gps ) ;
2016-07-14 02:08:43 -03:00
loc . lat = gpsloc . lat ;
loc . lng = gpsloc . lng ;
return true ;
} else {
// if no GPS fix, provide last known position before entering the mode
2020-04-24 03:04:39 -03:00
loc . lat = EKF_origin . lat ;
loc . lng = EKF_origin . lng ;
2020-05-01 23:51:54 -03:00
if ( PV_AidingMode = = AID_NONE ) {
loc . offset ( lastKnownPositionNE . x , lastKnownPositionNE . y ) ;
} else {
loc . offset ( outputDataNew . position . x , outputDataNew . position . y ) ;
}
2016-07-14 02:08:43 -03:00
return false ;
}
}
} else {
// If no origin has been defined for the EKF, then we cannot use its position states so return a raw
// GPS reading if available and return false
2020-11-05 19:30:16 -04:00
if ( ( gps . status ( selected_gps ) > = AP_DAL_GPS : : GPS_OK_FIX_3D ) ) {
2020-06-23 13:35:34 -03:00
const struct Location & gpsloc = gps . location ( selected_gps ) ;
2016-07-14 02:08:43 -03:00
loc = gpsloc ;
2019-01-01 20:08:23 -04:00
loc . relative_alt = 0 ;
loc . terrain_alt = 0 ;
2016-07-14 02:08:43 -03:00
}
return false ;
}
}
// return the horizontal speed limit in m/s set by optical flow sensor limits
// return the scale factor to be applied to navigation velocity gains to compensate for increase in velocity noise with height when using optical flow
void NavEKF3_core : : getEkfControlLimits ( float & ekfGndSpdLimit , float & ekfNavVelGainScaler ) const
{
2017-03-16 02:59:19 -03:00
// If in the last 10 seconds we have received flow data and no odometry data, then we are relying on optical flow
bool relyingOnFlowData = ( imuSampleTime_ms - prevBodyVelFuseTime_ms > 1000 )
& & ( imuSampleTime_ms - flowValidMeaTime_ms < = 10000 ) ;
// If relying on optical flow, limit speed to prevent sensor limit being exceeded and adjust
// nav gains to prevent body rate feedback into flow rates destabilising the control loop
if ( PV_AidingMode = = AID_RELATIVE & & relyingOnFlowData ) {
2016-07-14 02:08:43 -03:00
// allow 1.0 rad/sec margin for angular motion
ekfGndSpdLimit = MAX ( ( frontend - > _maxFlowRate - 1.0f ) , 0.0f ) * MAX ( ( terrainState - stateStruct . position [ 2 ] ) , rngOnGnd ) ;
// use standard gains up to 5.0 metres height and reduce above that
ekfNavVelGainScaler = 4.0f / MAX ( ( terrainState - stateStruct . position [ 2 ] ) , 4.0f ) ;
} else {
ekfGndSpdLimit = 400.0f ; //return 80% of max filter speed
ekfNavVelGainScaler = 1.0f ;
}
}
// return the LLH location of the filters NED origin
bool NavEKF3_core : : getOriginLLH ( struct Location & loc ) const
{
if ( validOrigin ) {
loc = EKF_origin ;
2017-05-09 03:23:58 -03:00
// report internally corrected reference height if enabled
2017-07-15 21:37:46 -03:00
if ( ( frontend - > _originHgtMode & ( 1 < < 2 ) ) = = 0 ) {
2017-05-09 03:23:58 -03:00
loc . alt = ( int32_t ) ( 100.0f * ( float ) ekfGpsRefHgt ) ;
}
2016-07-14 02:08:43 -03:00
}
return validOrigin ;
}
// return earth magnetic field estimates in measurement units / 1000
void NavEKF3_core : : getMagNED ( Vector3f & magNED ) const
{
magNED = stateStruct . earth_magfield * 1000.0f ;
}
// return body magnetic field estimates in measurement units / 1000
void NavEKF3_core : : getMagXYZ ( Vector3f & magXYZ ) const
{
magXYZ = stateStruct . body_magfield * 1000.0f ;
}
// return magnetometer offsets
// return true if offsets are valid
bool NavEKF3_core : : getMagOffsets ( uint8_t mag_idx , Vector3f & magOffsets ) const
{
2020-11-07 02:24:13 -04:00
if ( ! dal . get_compass ( ) ) {
2017-07-31 16:22:51 -03:00
return false ;
}
2016-07-14 02:08:43 -03:00
// compass offsets are valid if we have finalised magnetic field initialisation, magnetic field learning is not prohibited,
// primary compass is valid and state variances have converged
const float maxMagVar = 5E-6 f ;
bool variancesConverged = ( P [ 19 ] [ 19 ] < maxMagVar ) & & ( P [ 20 ] [ 20 ] < maxMagVar ) & & ( P [ 21 ] [ 21 ] < maxMagVar ) ;
if ( ( mag_idx = = magSelectIndex ) & &
finalInflightMagInit & &
! inhibitMagStates & &
2020-11-07 02:24:13 -04:00
dal . get_compass ( ) - > healthy ( magSelectIndex ) & &
2016-07-14 02:08:43 -03:00
variancesConverged ) {
2020-11-07 02:24:13 -04:00
magOffsets = dal . get_compass ( ) - > get_offsets ( magSelectIndex ) - stateStruct . body_magfield * 1000.0f ;
2016-07-14 02:08:43 -03:00
return true ;
} else {
2020-11-07 02:24:13 -04:00
magOffsets = dal . get_compass ( ) - > get_offsets ( magSelectIndex ) ;
2016-07-14 02:08:43 -03:00
return false ;
}
}
// return the index for the active magnetometer
2020-06-23 13:35:34 -03:00
// return the index for the active airspeed
uint8_t NavEKF3_core : : getActiveAirspeed ( ) const
{
return ( uint8_t ) selected_airspeed ;
}
2016-07-14 02:08:43 -03:00
// return the innovations for the NED Pos, NED Vel, XYZ Mag and Vtas measurements
void NavEKF3_core : : getInnovations ( Vector3f & velInnov , Vector3f & posInnov , Vector3f & magInnov , float & tasInnov , float & yawInnov ) const
{
velInnov . x = innovVelPos [ 0 ] ;
velInnov . y = innovVelPos [ 1 ] ;
velInnov . z = innovVelPos [ 2 ] ;
posInnov . x = innovVelPos [ 3 ] ;
posInnov . y = innovVelPos [ 4 ] ;
posInnov . z = innovVelPos [ 5 ] ;
magInnov . x = 1e3 f * innovMag [ 0 ] ; // Convert back to sensor units
magInnov . y = 1e3 f * innovMag [ 1 ] ; // Convert back to sensor units
magInnov . z = 1e3 f * innovMag [ 2 ] ; // Convert back to sensor units
tasInnov = innovVtas ;
yawInnov = innovYaw ;
}
// return the innovation consistency test ratios for the velocity, position, magnetometer and true airspeed measurements
// this indicates the amount of margin available when tuning the various error traps
// also return the delta in position due to the last position reset
void NavEKF3_core : : getVariances ( float & velVar , float & posVar , float & hgtVar , Vector3f & magVar , float & tasVar , Vector2f & offset ) const
{
velVar = sqrtf ( velTestRatio ) ;
posVar = sqrtf ( posTestRatio ) ;
hgtVar = sqrtf ( hgtTestRatio ) ;
// If we are using simple compass yaw fusion, populate all three components with the yaw test ratio to provide an equivalent output
magVar . x = sqrtf ( MAX ( magTestRatio . x , yawTestRatio ) ) ;
magVar . y = sqrtf ( MAX ( magTestRatio . y , yawTestRatio ) ) ;
magVar . z = sqrtf ( MAX ( magTestRatio . z , yawTestRatio ) ) ;
tasVar = sqrtf ( tasTestRatio ) ;
offset = posResetNE ;
}
2020-10-13 02:20:48 -03:00
// get a particular source's velocity innovations
// returns true on success and results are placed in innovations and variances arguments
bool NavEKF3_core : : getVelInnovationsAndVariancesForSource ( AP_NavEKF_Source : : SourceXY source , Vector3f & innovations , Vector3f & variances ) const
{
switch ( source ) {
case AP_NavEKF_Source : : SourceXY : : GPS :
// check for timeouts
if ( AP_HAL : : millis ( ) - gpsVelInnovTime_ms > 500 ) {
return false ;
}
innovations = gpsVelInnov ;
variances = gpsVelVarInnov ;
return true ;
case AP_NavEKF_Source : : SourceXY : : EXTNAV :
// check for timeouts
if ( AP_HAL : : millis ( ) - extNavVelInnovTime_ms > 500 ) {
return false ;
}
innovations = extNavVelInnov ;
variances = extNavVelVarInnov ;
return true ;
default :
// variances are not available for this source
return false ;
}
// should never get here but just in case
return false ;
}
2016-07-14 02:08:43 -03:00
/*
return the filter fault status as a bitmasked integer
0 = quaternions are NaN
1 = velocities are NaN
2 = badly conditioned X magnetometer fusion
3 = badly conditioned Y magnetometer fusion
2020-06-29 08:10:41 -03:00
4 = badly conditioned Z magnetometer fusion
5 = badly conditioned airspeed fusion
6 = badly conditioned synthetic sideslip fusion
2016-07-14 02:08:43 -03:00
7 = filter is not initialised
*/
void NavEKF3_core : : getFilterFaults ( uint16_t & faults ) const
{
faults = ( stateStruct . quat . is_nan ( ) < < 0 |
stateStruct . velocity . is_nan ( ) < < 1 |
faultStatus . bad_xmag < < 2 |
faultStatus . bad_ymag < < 3 |
faultStatus . bad_zmag < < 4 |
faultStatus . bad_airspeed < < 5 |
faultStatus . bad_sideslip < < 6 |
! statesInitialised < < 7 ) ;
}
/*
return filter timeout status as a bitmasked integer
0 = position measurement timeout
1 = velocity measurement timeout
2 = height measurement timeout
3 = magnetometer measurement timeout
4 = true airspeed measurement timeout
5 = unassigned
6 = unassigned
7 = unassigned
*/
void NavEKF3_core : : getFilterTimeouts ( uint8_t & timeouts ) const
{
timeouts = ( posTimeout < < 0 |
velTimeout < < 1 |
hgtTimeout < < 2 |
magTimeout < < 3 |
tasTimeout < < 4 ) ;
}
// Return the navigation filter status message
void NavEKF3_core : : getFilterStatus ( nav_filter_status & status ) const
{
status = filterStatus ;
}
/*
return filter gps quality check status
*/
void NavEKF3_core : : getFilterGpsStatus ( nav_gps_status & faults ) const
{
// init return value
faults . value = 0 ;
// set individual flags
faults . flags . bad_sAcc = gpsCheckStatus . bad_sAcc ; // reported speed accuracy is insufficient
faults . flags . bad_hAcc = gpsCheckStatus . bad_hAcc ; // reported horizontal position accuracy is insufficient
2017-02-22 14:53:31 -04:00
faults . flags . bad_vAcc = gpsCheckStatus . bad_vAcc ; // reported vertical position accuracy is insufficient
2016-07-14 02:08:43 -03:00
faults . flags . bad_yaw = gpsCheckStatus . bad_yaw ; // EKF heading accuracy is too large for GPS use
faults . flags . bad_sats = gpsCheckStatus . bad_sats ; // reported number of satellites is insufficient
faults . flags . bad_horiz_drift = gpsCheckStatus . bad_horiz_drift ; // GPS horizontal drift is too large to start using GPS (check assumes vehicle is static)
faults . flags . bad_hdop = gpsCheckStatus . bad_hdop ; // reported HDoP is too large to start using GPS
faults . flags . bad_vert_vel = gpsCheckStatus . bad_vert_vel ; // GPS vertical speed is too large to start using GPS (check assumes vehicle is static)
faults . flags . bad_fix = gpsCheckStatus . bad_fix ; // The GPS cannot provide the 3D fix required
faults . flags . bad_horiz_vel = gpsCheckStatus . bad_horiz_vel ; // The GPS horizontal speed is excessive (check assumes the vehicle is static)
}
// send an EKF_STATUS message to GCS
2019-12-10 03:04:21 -04:00
void NavEKF3_core : : send_status_report ( mavlink_channel_t chan ) const
2016-07-14 02:08:43 -03:00
{
// prepare flags
uint16_t flags = 0 ;
if ( filterStatus . flags . attitude ) {
flags | = EKF_ATTITUDE ;
}
if ( filterStatus . flags . horiz_vel ) {
flags | = EKF_VELOCITY_HORIZ ;
}
if ( filterStatus . flags . vert_vel ) {
flags | = EKF_VELOCITY_VERT ;
}
if ( filterStatus . flags . horiz_pos_rel ) {
flags | = EKF_POS_HORIZ_REL ;
}
if ( filterStatus . flags . horiz_pos_abs ) {
flags | = EKF_POS_HORIZ_ABS ;
}
if ( filterStatus . flags . vert_pos ) {
flags | = EKF_POS_VERT_ABS ;
}
if ( filterStatus . flags . terrain_alt ) {
flags | = EKF_POS_VERT_AGL ;
}
if ( filterStatus . flags . const_pos_mode ) {
flags | = EKF_CONST_POS_MODE ;
}
if ( filterStatus . flags . pred_horiz_pos_rel ) {
flags | = EKF_PRED_POS_HORIZ_REL ;
}
if ( filterStatus . flags . pred_horiz_pos_abs ) {
flags | = EKF_PRED_POS_HORIZ_ABS ;
}
2020-02-07 16:33:52 -04:00
if ( ! filterStatus . flags . initalized ) {
flags | = EKF_UNINITIALIZED ;
}
2016-07-14 02:08:43 -03:00
if ( filterStatus . flags . gps_glitching ) {
flags | = ( 1 < < 15 ) ;
}
// get variances
float velVar , posVar , hgtVar , tasVar ;
Vector3f magVar ;
Vector2f offset ;
getVariances ( velVar , posVar , hgtVar , magVar , tasVar , offset ) ;
// Only report range finder normalised innovation levels if the EKF needs the data for primary
// height estimation or optical flow operation. This prevents false alarms at the GCS if a
// range finder is fitted for other applications
float temp ;
2020-07-04 00:27:49 -03:00
if ( ( ( frontend - > _useRngSwHgt > 0 ) & & activeHgtSource = = AP_NavEKF_Source : : SourceZ : : RANGEFINDER ) | | ( PV_AidingMode = = AID_RELATIVE & & flowDataValid ) ) {
2016-07-14 02:08:43 -03:00
temp = sqrtf ( auxRngTestRatio ) ;
} else {
temp = 0.0f ;
}
2020-04-21 00:30:46 -03:00
const float mag_max = fmaxf ( fmaxf ( magVar . x , magVar . y ) , magVar . z ) ;
2016-07-14 02:08:43 -03:00
// send message
2020-04-21 00:30:46 -03:00
mavlink_msg_ekf_status_report_send ( chan , flags , velVar , posVar , hgtVar , mag_max , temp , tasVar ) ;
2016-07-14 02:08:43 -03:00
}
// report the reason for why the backend is refusing to initialise
const char * NavEKF3_core : : prearm_failure_reason ( void ) const
{
2019-07-02 00:42:52 -03:00
if ( gpsGoodToAlign ) {
2016-07-14 02:08:43 -03:00
// we are not failing
return nullptr ;
}
return prearm_fail_string ;
}
// report the number of frames lapsed since the last state prediction
// this is used by other instances to level load
uint8_t NavEKF3_core : : getFramesSincePredict ( void ) const
{
return framesSincePredict ;
}