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 ] ) ;
2021-05-04 08:12:23 -03:00
if ( onGround & & ( PV_AidingMode = = AID_NONE ) & & ( ( horizErrSq > 1.0f ) | | ( fabsF ( hgtInnovFiltState ) > 1.0f ) ) ) {
2016-07-14 02:08:43 -03:00
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-12-10 21:48:00 -04:00
if ( arsp ! = nullptr & & arsp - > get_num_sensors ( ) > = 2 & & ( frontend - > _affinity & EKF_AFFINITY_ARSP ) ) {
2020-06-19 03:21:46 -03:00
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 ;
}
// 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 ;
}
2021-05-04 08:12:23 -03:00
gyroBias = ( stateStruct . gyro_bias / dtEkfAvg ) . tofloat ( ) ;
2016-07-14 02:08:43 -03:00
}
// return accelerometer bias in m/s/s
void NavEKF3_core : : getAccelBias ( Vector3f & accelBias ) const
{
if ( ! statesInitialised ) {
accelBias . zero ( ) ;
return ;
}
2021-05-04 08:12:23 -03:00
accelBias = ( stateStruct . accel_bias / dtEkfAvg ) . tofloat ( ) ;
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
{
2021-05-04 08:12:23 -03:00
ret = outputDataNew . quat . tofloat ( ) ;
2016-07-14 02:08:43 -03:00
}
// 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
{
2021-05-04 08:12:23 -03:00
pos = posResetNE . tofloat ( ) ;
2016-07-14 02:08:43 -03:00
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
{
2021-05-04 08:12:23 -03:00
vel = velResetNE . tofloat ( ) ;
2016-07-14 02:08:43 -03:00
return lastVelReset_ms ;
}
// return the NED wind speed estimates in m/s (positive is air moving in the direction of the axis)
2020-06-06 20:23:52 -03:00
// returns true if wind state estimation is active
bool NavEKF3_core : : getWind ( Vector3f & wind ) const
2016-07-14 02:08:43 -03:00
{
wind . x = stateStruct . wind_vel . x ;
wind . y = stateStruct . wind_vel . y ;
wind . z = 0.0f ; // currently don't estimate this
2020-06-06 20:23:52 -03:00
return ! inhibitWindStates ;
2016-07-14 02:08:43 -03:00
}
// 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)
2021-05-04 08:12:23 -03:00
vel = ( outputDataNew . velocity + velOffsetNED ) . tofloat ( ) ;
2016-07-14 02:08:43 -03:00
}
2020-11-20 17:39:04 -04:00
// return estimate of true airspeed vector in body frame in m/s
// returns false if estimate is unavailable
bool NavEKF3_core : : getAirSpdVec ( Vector3f & vel ) const
{
if ( inhibitWindStates | | PV_AidingMode = = AID_NONE ) {
return false ;
}
2021-05-04 08:12:23 -03:00
vel = ( outputDataNew . velocity + velOffsetNED ) . tofloat ( ) ;
2020-11-20 17:39:04 -04:00
vel . x - = stateStruct . wind_vel . x ;
vel . y - = stateStruct . wind_vel . y ;
Matrix3f Tnb ; // rotation from nav to body frame
outputDataNew . quat . inverse ( ) . rotation_matrix ( Tnb ) ;
vel = Tnb * vel ;
return true ;
}
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
}
// 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)
2021-07-10 02:17:11 -03:00
posNE = ( outputDataNew . position . xy ( ) + posOffsetNED . xy ( ) + public_origin . get_distance_NE_ftype ( EKF_origin ) ) . tofloat ( ) ;
2016-07-14 02:08:43 -03:00
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 ) ;
2021-07-10 02:17:11 -03:00
posNE = public_origin . get_distance_NE_ftype ( gpsloc ) . tofloat ( ) ;
2016-07-14 02:08:43 -03:00
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
2021-07-10 02:17:11 -03:00
posNE = outputDataNew . position . xy ( ) . tofloat ( ) ;
2016-07-14 02:08:43 -03:00
return false ;
}
} else {
// If the origin has not been set, then we have no means of providing a relative position
2021-07-10 02:17:11 -03:00
posNE . zero ( ) ;
2016-07-14 02:08:43 -03:00
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
{
2018-09-13 17:39:43 -03:00
Location origin ;
2021-02-26 17:30:40 -04:00
if ( getOriginLLH ( origin ) ) {
float posD ;
if ( getPosD ( posD ) & & PV_AidingMode ! = AID_NONE ) {
// Altitude returned is an absolute altitude relative to the WGS-84 spherioid
loc . alt = origin . alt - posD * 100 ;
loc . relative_alt = 0 ;
loc . terrain_alt = 0 ;
if ( filterStatus . flags . horiz_pos_abs | | filterStatus . flags . horiz_pos_rel ) {
// The EKF is able to provide a position estimate
2020-04-24 03:04:39 -03:00
loc . lat = EKF_origin . lat ;
loc . lng = EKF_origin . lng ;
2021-08-09 05:02:14 -03:00
loc . offset ( outputDataNew . position . x + posOffsetNED . x ,
outputDataNew . position . y + posOffsetNED . y ) ;
2021-02-26 17:30:40 -04:00
return true ;
} else {
// We have been be doing inertial dead reckoning for too long so use raw GPS if available
if ( getGPSLLH ( loc ) ) {
return true ;
2020-05-01 23:51:54 -03:00
} else {
2021-02-26 17:30:40 -04:00
// Return the EKF estimate but mark it as invalid
loc . lat = EKF_origin . lat ;
loc . lng = EKF_origin . lng ;
2021-08-09 05:02:14 -03:00
loc . offset ( outputDataNew . position . x + posOffsetNED . x ,
outputDataNew . position . y + posOffsetNED . y ) ;
2021-02-26 17:30:40 -04:00
return false ;
2020-05-01 23:51:54 -03:00
}
2021-02-26 17:30:40 -04:00
}
} else {
// Return a raw GPS reading if available and the last recorded positon if not
if ( getGPSLLH ( loc ) ) {
return true ;
} else {
loc . lat = EKF_origin . lat ;
loc . lng = EKF_origin . lng ;
2021-08-09 05:02:14 -03:00
loc . offset ( lastKnownPositionNE . x + posOffsetNED . x ,
lastKnownPositionNE . y + posOffsetNED . y ) ;
2016-07-14 02:08:43 -03:00
return false ;
}
}
} else {
2021-02-26 17:30:40 -04:00
// The EKF is not navigating so use raw GPS if available
return getGPSLLH ( loc ) ;
2016-07-14 02:08:43 -03:00
}
}
2021-02-26 17:30:40 -04:00
bool NavEKF3_core : : getGPSLLH ( struct Location & loc ) const
{
const auto & gps = dal . gps ( ) ;
if ( ( gps . status ( selected_gps ) > = AP_DAL_GPS : : GPS_OK_FIX_3D ) ) {
2021-03-04 23:13:32 -04:00
loc = gps . location ( selected_gps ) ;
2021-02-26 17:30:40 -04:00
return true ;
}
return false ;
}
2016-07-14 02:08:43 -03:00
// 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 ) {
2021-07-10 02:17:11 -03:00
loc = public_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
{
2021-05-04 08:12:23 -03:00
magNED = ( stateStruct . earth_magfield * 1000.0f ) . tofloat ( ) ;
2016-07-14 02:08:43 -03:00
}
// return body magnetic field estimates in measurement units / 1000
void NavEKF3_core : : getMagXYZ ( Vector3f & magXYZ ) const
{
2021-05-04 08:12:23 -03:00
magXYZ = ( stateStruct . body_magfield * 1000.0f ) . tofloat ( ) ;
2016-07-14 02:08:43 -03:00
}
// return magnetometer offsets
// return true if offsets are valid
bool NavEKF3_core : : getMagOffsets ( uint8_t mag_idx , Vector3f & magOffsets ) const
{
2021-07-25 21:32:12 -03:00
const auto & compass = dal . compass ( ) ;
if ( ! compass . available ( ) ) {
2017-07-31 16:22:51 -03:00
return false ;
}
2021-07-25 21:32:12 -03:00
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 & &
2021-07-25 21:32:12 -03:00
compass . healthy ( magSelectIndex ) & &
2016-07-14 02:08:43 -03:00
variancesConverged ) {
2021-07-25 21:32:12 -03:00
magOffsets = compass . get_offsets ( magSelectIndex ) - stateStruct . body_magfield . tofloat ( ) * 1000.0 ;
2016-07-14 02:08:43 -03:00
return true ;
} else {
2021-07-25 21:32:12 -03:00
magOffsets = 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
2021-07-19 08:27:38 -03:00
bool NavEKF3_core : : getInnovations ( Vector3f & velInnov , Vector3f & posInnov , Vector3f & magInnov , float & tasInnov , float & yawInnov ) const
2016-07-14 02:08:43 -03:00
{
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 ;
2021-07-19 08:27:38 -03:00
return true ;
2016-07-14 02:08:43 -03:00
}
2020-11-20 17:39:04 -04:00
// return the synthetic air data drag and sideslip innovations
void NavEKF3_core : : getSynthAirDataInnovations ( Vector2f & dragInnov , float & betaInnov ) const
{
2021-01-19 00:36:39 -04:00
# if EK3_FEATURE_DRAG_FUSION
2020-11-20 17:39:04 -04:00
dragInnov . x = innovDrag [ 0 ] ;
dragInnov . y = innovDrag [ 1 ] ;
betaInnov = innovBeta ;
2021-01-19 00:36:39 -04:00
# endif
2020-11-20 17:39:04 -04:00
}
2016-07-14 02:08:43 -03:00
// 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
2021-07-19 08:27:38 -03:00
bool NavEKF3_core : : getVariances ( float & velVar , float & posVar , float & hgtVar , Vector3f & magVar , float & tasVar , Vector2f & offset ) const
2016-07-14 02:08:43 -03:00
{
2021-05-04 08:12:23 -03:00
velVar = sqrtF ( velTestRatio ) ;
posVar = sqrtF ( posTestRatio ) ;
hgtVar = sqrtF ( hgtTestRatio ) ;
2016-07-14 02:08:43 -03:00
// If we are using simple compass yaw fusion, populate all three components with the yaw test ratio to provide an equivalent output
2021-05-04 08:12:23 -03:00
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 . tofloat ( ) ;
2021-07-19 08:27:38 -03:00
return true ;
2016-07-14 02:08:43 -03:00
}
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 ;
}
2021-05-04 08:12:23 -03:00
innovations = gpsVelInnov . tofloat ( ) ;
variances = gpsVelVarInnov . tofloat ( ) ;
2020-10-13 02:20:48 -03:00
return true ;
2021-01-19 00:27:03 -04:00
# if EK3_FEATURE_EXTERNAL_NAV
2020-10-13 02:20:48 -03:00
case AP_NavEKF_Source : : SourceXY : : EXTNAV :
// check for timeouts
if ( AP_HAL : : millis ( ) - extNavVelInnovTime_ms > 500 ) {
return false ;
}
2021-05-04 08:12:23 -03:00
innovations = extNavVelInnov . tofloat ( ) ;
variances = extNavVelVarInnov . tofloat ( ) ;
2020-10-13 02:20:48 -03:00
return true ;
2021-01-19 00:27:03 -04:00
# endif // EK3_FEATURE_EXTERNAL_NAV
2021-08-16 09:41:54 -03:00
case AP_NavEKF_Source : : SourceXY : : OPTFLOW :
// check for timeouts
if ( AP_HAL : : millis ( ) - flowInnovTime_ms > 500 ) {
return false ;
}
2021-08-16 10:00:52 -03:00
innovations . x = flowInnov [ 0 ] ;
innovations . y = flowInnov [ 1 ] ;
2021-08-16 09:41:54 -03:00
innovations . z = 0 ;
2021-08-16 10:00:52 -03:00
variances . x = flowVarInnov [ 0 ] ;
variances . y = flowVarInnov [ 1 ] ;
2021-08-16 09:41:54 -03:00
variances . z = 0 ;
return true ;
2020-10-13 02:20:48 -03:00
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 the navigation filter status message
void NavEKF3_core : : getFilterStatus ( nav_filter_status & status ) const
{
status = filterStatus ;
}
// 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
2021-07-19 08:27:38 -03:00
float velVar = 0 , posVar = 0 , hgtVar = 0 , tasVar = 0 ;
2016-07-14 02:08:43 -03:00
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 ) ) {
2021-05-04 08:12:23 -03:00
temp = sqrtF ( auxRngTestRatio ) ;
2016-07-14 02:08:43 -03:00
} else {
temp = 0.0f ;
}
2021-05-04 08:12:23 -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 ;
}