#include "Rover.h"
#ifndef EKF_CHECK_ITERATIONS_MAX
# define EKF_CHECK_ITERATIONS_MAX 10
#endif
#ifndef EKF_CHECK_WARNING_TIME
# define EKF_CHECK_WARNING_TIME (30*1000)
#endif
static struct {
uint8_t fail_count;
uint8_t bad_variance : 1;
uint32_t last_warn_time;
} ekf_check_state;
void Rover::ekf_check()
{
Location temp_loc;
if (!ahrs.get_origin(temp_loc)) {
return;
}
if (!arming.is_armed() || (g.fs_ekf_thresh <= 0.0f)) {
ekf_check_state.fail_count = 0;
ekf_check_state.bad_variance = false;
AP_Notify::flags.ekf_bad = ekf_check_state.bad_variance;
failsafe_ekf_off_event();
return;
}
if (ekf_over_threshold()) {
if (!ekf_check_state.bad_variance) {
ekf_check_state.fail_count++;
if (ekf_check_state.fail_count >= EKF_CHECK_ITERATIONS_MAX) {
ekf_check_state.fail_count = EKF_CHECK_ITERATIONS_MAX;
ekf_check_state.bad_variance = true;
LOGGER_WRITE_ERROR(LogErrorSubsystem::EKFCHECK,
LogErrorCode::EKFCHECK_BAD_VARIANCE);
if ((AP_HAL::millis() - ekf_check_state.last_warn_time) > EKF_CHECK_WARNING_TIME) {
GCS_SEND_TEXT(MAV_SEVERITY_CRITICAL,"EKF variance");
ekf_check_state.last_warn_time = AP_HAL::millis();
}
failsafe_ekf_event();
}
}
} else {
if (ekf_check_state.fail_count > 0) {
ekf_check_state.fail_count--;
if (ekf_check_state.bad_variance && ekf_check_state.fail_count == 0) {
ekf_check_state.bad_variance = false;
LOGGER_WRITE_ERROR(LogErrorSubsystem::EKFCHECK,
LogErrorCode::EKFCHECK_VARIANCE_CLEARED);
failsafe_ekf_off_event();
}
}
}
AP_Notify::flags.ekf_bad = ekf_check_state.bad_variance;
}
bool Rover::ekf_over_threshold()
{
if (g.fs_ekf_thresh <= 0.0f) {
return false;
}
float position_variance, vel_variance, height_variance, tas_variance;
Vector3f mag_variance;
ahrs.get_variances(vel_variance, position_variance, height_variance, mag_variance, tas_variance);
uint8_t over_thresh_count = 0;
if (mag_variance.length() >= g.fs_ekf_thresh) {
over_thresh_count++;
}
if (vel_variance >= g.fs_ekf_thresh) {
over_thresh_count++;
}
if (position_variance >= g.fs_ekf_thresh) {
over_thresh_count++;
}
bool optflow_healthy = false;
#if AP_OPTICALFLOW_ENABLED
optflow_healthy = optflow.healthy();
#endif
if (!optflow_healthy && (vel_variance >= (2.0f * g.fs_ekf_thresh))) {
over_thresh_count += 2;
} else if (vel_variance >= g.fs_ekf_thresh) {
over_thresh_count++;
}
if (over_thresh_count >= 2) {
return true;
}
return !ekf_position_ok();
}
bool Rover::ekf_position_ok()
{
if (!ahrs.have_inertial_nav()) {
return false;
}
nav_filter_status filt_status;
rover.ahrs.get_filter_status(filt_status);
if (!arming.is_armed()) {
return (filt_status.flags.horiz_pos_abs || filt_status.flags.pred_horiz_pos_abs || filt_status.flags.horiz_pos_rel || filt_status.flags.pred_horiz_pos_rel);
} else {
return ((filt_status.flags.horiz_pos_abs || filt_status.flags.horiz_pos_rel) && !filt_status.flags.const_pos_mode);
}
}
void Rover::failsafe_ekf_event()
{
if (failsafe.ekf) {
return;
}
failsafe.ekf = true;
LOGGER_WRITE_ERROR(LogErrorSubsystem::FAILSAFE_EKFINAV,
LogErrorCode::FAILSAFE_OCCURRED);
if (!control_mode->requires_position()) {
return;
}
switch ((enum fs_ekf_action)g.fs_ekf_action.get()) {
case FS_EKF_DISABLE:
return;
case FS_EKF_REPORT_ONLY:
break;
case FS_EKF_HOLD:
default:
set_mode(mode_hold, ModeReason::EKF_FAILSAFE);
break;
}
GCS_SEND_TEXT(MAV_SEVERITY_CRITICAL,"EKF failsafe");
}
void Rover::failsafe_ekf_off_event(void)
{
if (!failsafe.ekf) {
return;
}
failsafe.ekf = false;
LOGGER_WRITE_ERROR(LogErrorSubsystem::FAILSAFE_EKFINAV,
LogErrorCode::FAILSAFE_RESOLVED);
GCS_SEND_TEXT(MAV_SEVERITY_CRITICAL,"EKF failsafe cleared");
}