#include "eskf.h" #include #include #include #include #define ESKF_POSITION_INDEX 0u #define ESKF_VELOCITY_INDEX 3u #define ESKF_ATTITUDE_INDEX 6u #define ESKF_ACCEL_BIAS_INDEX 9u #define ESKF_GYRO_BIAS_INDEX 12u #define ESKF_PI 3.14159265358979323846264338327950288 static double square(double value) { return value * value; } static double vector_norm3(const double value[3]) { return sqrt(square(value[0]) + square(value[1]) + square(value[2])); } static bool vector_is_finite(const double* value, size_t count) { if(value == NULL) { return false; } for(size_t i = 0 ; i < count ; i++) { if(!isfinite(value[i])) { return false; } } return true; } static bool quaternion_normalize(double quaternion[4]) { double norm = sqrt(square(quaternion[0]) + square(quaternion[1]) + square(quaternion[2]) + square(quaternion[3])); if(!(norm > DBL_EPSILON) || !isfinite(norm)) { return false; } for(unsigned int i = 0 ; i < 4u ; i++) { quaternion[i] /= norm; } return true; } static void quaternion_multiply(const double first[4], const double second[4], double output[4]) { output[0] = first[0] * second[0] - first[1] * second[1] - first[2] * second[2] - first[3] * second[3]; output[1] = first[0] * second[1] + first[1] * second[0] + first[2] * second[3] - first[3] * second[2]; output[2] = first[0] * second[2] - first[1] * second[3] + first[2] * second[0] + first[3] * second[1]; output[3] = first[0] * second[3] + first[1] * second[2] - first[2] * second[1] + first[3] * second[0]; } static void rotation_vector_quaternion(const double rotation_vector[3], double quaternion[4]) { double angle = vector_norm3(rotation_vector); if(angle < 1.0e-10) { quaternion[0] = 1.0; quaternion[1] = 0.5 * rotation_vector[0]; quaternion[2] = 0.5 * rotation_vector[1]; quaternion[3] = 0.5 * rotation_vector[2]; (void)quaternion_normalize(quaternion); return; } double scale = sin(0.5 * angle) / angle; quaternion[0] = cos(0.5 * angle); quaternion[1] = scale * rotation_vector[0]; quaternion[2] = scale * rotation_vector[1]; quaternion[3] = scale * rotation_vector[2]; } static void quaternion_rotation_matrix(const double quaternion[4], double rotation[3][3]) { const double w = quaternion[0]; const double x = quaternion[1]; const double y = quaternion[2]; const double z = quaternion[3]; rotation[0][0] = 1.0 - 2.0 * (y * y + z * z); rotation[0][1] = 2.0 * (x * y - w * z); rotation[0][2] = 2.0 * (x * z + w * y); rotation[1][0] = 2.0 * (x * y + w * z); rotation[1][1] = 1.0 - 2.0 * (x * x + z * z); rotation[1][2] = 2.0 * (y * z - w * x); rotation[2][0] = 2.0 * (x * z - w * y); rotation[2][1] = 2.0 * (y * z + w * x); rotation[2][2] = 1.0 - 2.0 * (x * x + y * y); } static void skew_matrix(const double vector[3], double skew[3][3]) { skew[0][0] = 0.0; skew[0][1] = -vector[2]; skew[0][2] = vector[1]; skew[1][0] = vector[2]; skew[1][1] = 0.0; skew[1][2] = -vector[0]; skew[2][0] = -vector[1]; skew[2][1] = vector[0]; skew[2][2] = 0.0; } static bool inverse_symmetric3(const double input[9], double inverse[9]) { double lower[3][3] = {{0.0}}; double scale = fmax(fabs(input[0]), fmax(fabs(input[4]), fabs(input[8]))); if(!(scale > 0.0) || !isfinite(scale)) { return false; } for(unsigned int row = 0 ; row < 3u ; row++) { for(unsigned int col = 0 ; col <= row ; col++) { double value = 0.5 * (input[row * 3u + col] + input[col * 3u + row]); for(unsigned int k = 0 ; k < col ; k++) { value -= lower[row][k] * lower[col][k]; } if(row == col) { if(value <= scale * DBL_EPSILON * 128.0 || !isfinite(value)) { return false; } lower[row][col] = sqrt(value); } else { lower[row][col] = value / lower[col][col]; } } } for(unsigned int column = 0 ; column < 3u ; column++) { double forward[3] = {0.0, 0.0, 0.0}; double solution[3] = {0.0, 0.0, 0.0}; for(unsigned int row = 0 ; row < 3u ; row++) { double rhs = (row == column) ? 1.0 : 0.0; for(unsigned int k = 0 ; k < row ; k++) { rhs -= lower[row][k] * forward[k]; } forward[row] = rhs / lower[row][row]; } for(int row = 2 ; row >= 0 ; row--) { double rhs = forward[row]; for(unsigned int k = (unsigned int)row + 1u ; k < 3u ; k++) { rhs -= lower[k][row] * solution[k]; } solution[row] = rhs / lower[row][row]; } for(unsigned int row = 0 ; row < 3u ; row++) { inverse[row * 3u + column] = solution[row]; } } return vector_is_finite(inverse, 9u); } static void covariance_symmetrize_and_floor(eskf_t* filter, double floor_value) { for(unsigned int row = 0 ; row < ESKF_ERROR_STATE_DIM ; row++) { for(unsigned int col = row + 1u ; col < ESKF_ERROR_STATE_DIM ; col++) { double symmetric = 0.5 * (filter->covariance[row][col] + filter->covariance[col][row]); filter->covariance[row][col] = symmetric; filter->covariance[col][row] = symmetric; } if(filter->covariance[row][row] < floor_value) { filter->covariance[row][row] = floor_value; } } } void eskf_default_config(eskf_config_t* config) { if(config == NULL) { return; } memset(config, 0, sizeof(*config)); for(unsigned int i = 0 ; i < 3u ; i++) { config->accel_noise_density[i] = 0.03; config->gyro_noise_density[i] = 0.003; config->accel_bias_random_walk[i] = 0.002; config->gyro_bias_random_walk[i] = 0.002 * ESKF_PI / 180.0; config->initial_position_std_m[i] = (i < 2u) ? 10.0 : 20.0; config->initial_velocity_std_mps[i] = 1.0; config->initial_attitude_std_rad[i] = ((i < 2u) ? 5.0 : 30.0) * ESKF_PI / 180.0; config->initial_accel_bias_std_mps2[i] = 0.1; config->initial_gyro_bias_std_rps[i] = 0.05 * ESKF_PI / 180.0; } config->gravity_mps2 = 9.80665; config->max_propagation_step_s = 0.02; config->position_nis_gate = 16.26623619623813; /* chi-square 3 DOF, 99.9%. */ config->covariance_diagonal_floor = 1.0e-12; } bool eskf_quaternion_align_vectors(const double source[3], const double target[3], double quaternion_out[4]) { if(!vector_is_finite(source, 3u) || !vector_is_finite(target, 3u) || (quaternion_out == NULL)) { return false; } double source_norm = vector_norm3(source); double target_norm = vector_norm3(target); if(!(source_norm > DBL_EPSILON) || !(target_norm > DBL_EPSILON)) { return false; } double first[3]; double second[3]; for(unsigned int i = 0 ; i < 3u ; i++) { first[i] = source[i] / source_norm; second[i] = target[i] / target_norm; } double dot = first[0] * second[0] + first[1] * second[1] + first[2] * second[2]; if(dot < -0.999999) { double axis[3] = {0.0, first[2], -first[1]}; if(vector_norm3(axis) < 1.0e-6) { axis[0] = -first[2]; axis[1] = 0.0; axis[2] = first[0]; } double axis_norm = vector_norm3(axis); quaternion_out[0] = 0.0; for(unsigned int i = 0 ; i < 3u ; i++) { quaternion_out[i + 1u] = axis[i] / axis_norm; } return true; } quaternion_out[0] = 1.0 + dot; quaternion_out[1] = first[1] * second[2] - first[2] * second[1]; quaternion_out[2] = first[2] * second[0] - first[0] * second[2]; quaternion_out[3] = first[0] * second[1] - first[1] * second[0]; return quaternion_normalize(quaternion_out); } bool eskf_initialize(eskf_t* filter, const eskf_config_t* config, const double initial_position_m[3], const double body_to_nav_quaternion[4], const double accel_bias_mps2[3], const double gyro_bias_rps[3]) { if((filter == NULL) || (config == NULL) || !vector_is_finite(initial_position_m, 3u) || !vector_is_finite(body_to_nav_quaternion, 4u) || !vector_is_finite(accel_bias_mps2, 3u) || !vector_is_finite(gyro_bias_rps, 3u) || !(config->gravity_mps2 > 0.0)) { return false; } memset(filter, 0, sizeof(*filter)); memcpy(filter->position_m, initial_position_m, sizeof(filter->position_m)); memcpy(filter->body_to_nav_quaternion, body_to_nav_quaternion, sizeof(filter->body_to_nav_quaternion)); memcpy(filter->accel_bias_mps2, accel_bias_mps2, sizeof(filter->accel_bias_mps2)); memcpy(filter->gyro_bias_rps, gyro_bias_rps, sizeof(filter->gyro_bias_rps)); if(!quaternion_normalize(filter->body_to_nav_quaternion)) { return false; } for(unsigned int i = 0 ; i < 3u ; i++) { filter->covariance[ESKF_POSITION_INDEX + i][ESKF_POSITION_INDEX + i] = square(config->initial_position_std_m[i]); filter->covariance[ESKF_VELOCITY_INDEX + i][ESKF_VELOCITY_INDEX + i] = square(config->initial_velocity_std_mps[i]); filter->covariance[ESKF_ATTITUDE_INDEX + i][ESKF_ATTITUDE_INDEX + i] = square(config->initial_attitude_std_rad[i]); filter->covariance[ESKF_ACCEL_BIAS_INDEX + i][ESKF_ACCEL_BIAS_INDEX + i] = square(config->initial_accel_bias_std_mps2[i]); filter->covariance[ESKF_GYRO_BIAS_INDEX + i][ESKF_GYRO_BIAS_INDEX + i] = square(config->initial_gyro_bias_std_rps[i]); } covariance_symmetrize_and_floor(filter, config->covariance_diagonal_floor); filter->last_position_nis = NAN; filter->initialized = true; return true; } static bool eskf_propagate_step(eskf_t* filter, const eskf_config_t* config, const double accel_mps2[3], const double gyro_rps[3], double dt_s) { double specific_force[3]; double angular_rate[3]; for(unsigned int i = 0 ; i < 3u ; i++) { specific_force[i] = accel_mps2[i] - filter->accel_bias_mps2[i]; angular_rate[i] = gyro_rps[i] - filter->gyro_bias_rps[i]; filter->last_unbiased_gyro_rps[i] = angular_rate[i]; } double rotation[3][3]; quaternion_rotation_matrix(filter->body_to_nav_quaternion, rotation); double linear_accel[3] = {0.0, 0.0, -config->gravity_mps2}; for(unsigned int row = 0 ; row < 3u ; row++) { for(unsigned int col = 0 ; col < 3u ; col++) { linear_accel[row] += rotation[row][col] * specific_force[col]; } filter->last_linear_accel_nav_mps2[row] = linear_accel[row]; filter->position_m[row] += filter->velocity_mps[row] * dt_s + 0.5 * linear_accel[row] * dt_s * dt_s; filter->velocity_mps[row] += linear_accel[row] * dt_s; } double rotation_vector[3] = { angular_rate[0] * dt_s, angular_rate[1] * dt_s, angular_rate[2] * dt_s }; double delta_quaternion[4]; double updated_quaternion[4]; rotation_vector_quaternion(rotation_vector, delta_quaternion); quaternion_multiply(filter->body_to_nav_quaternion, delta_quaternion, updated_quaternion); memcpy(filter->body_to_nav_quaternion, updated_quaternion, sizeof(updated_quaternion)); if(!quaternion_normalize(filter->body_to_nav_quaternion)) { return false; } double system[ESKF_ERROR_STATE_DIM][ESKF_ERROR_STATE_DIM] = {{0.0}}; for(unsigned int i = 0 ; i < 3u ; i++) { system[ESKF_POSITION_INDEX + i][ESKF_VELOCITY_INDEX + i] = 1.0; system[ESKF_ATTITUDE_INDEX + i][ESKF_GYRO_BIAS_INDEX + i] = -1.0; } double force_skew[3][3]; double rate_skew[3][3]; skew_matrix(specific_force, force_skew); skew_matrix(angular_rate, rate_skew); for(unsigned int row = 0 ; row < 3u ; row++) { for(unsigned int col = 0 ; col < 3u ; col++) { double rotated_skew = 0.0; for(unsigned int k = 0 ; k < 3u ; k++) { rotated_skew += rotation[row][k] * force_skew[k][col]; } system[ESKF_VELOCITY_INDEX + row][ESKF_ATTITUDE_INDEX + col] = -rotated_skew; system[ESKF_VELOCITY_INDEX + row][ESKF_ACCEL_BIAS_INDEX + col] = -rotation[row][col]; system[ESKF_ATTITUDE_INDEX + row][ESKF_ATTITUDE_INDEX + col] = -rate_skew[row][col]; } } double transition[ESKF_ERROR_STATE_DIM][ESKF_ERROR_STATE_DIM] = {{0.0}}; for(unsigned int row = 0 ; row < ESKF_ERROR_STATE_DIM ; row++) { for(unsigned int col = 0 ; col < ESKF_ERROR_STATE_DIM ; col++) { transition[row][col] = system[row][col] * dt_s; } transition[row][row] += 1.0; } double intermediate[ESKF_ERROR_STATE_DIM][ESKF_ERROR_STATE_DIM] = {{0.0}}; double propagated[ESKF_ERROR_STATE_DIM][ESKF_ERROR_STATE_DIM] = {{0.0}}; for(unsigned int row = 0 ; row < ESKF_ERROR_STATE_DIM ; row++) { for(unsigned int col = 0 ; col < ESKF_ERROR_STATE_DIM ; col++) { for(unsigned int k = 0 ; k < ESKF_ERROR_STATE_DIM ; k++) { intermediate[row][col] += transition[row][k] * filter->covariance[k][col]; } } } for(unsigned int row = 0 ; row < ESKF_ERROR_STATE_DIM ; row++) { for(unsigned int col = 0 ; col < ESKF_ERROR_STATE_DIM ; col++) { for(unsigned int k = 0 ; k < ESKF_ERROR_STATE_DIM ; k++) { propagated[row][col] += intermediate[row][k] * transition[col][k]; } } } for(unsigned int row = 0 ; row < 3u ; row++) { for(unsigned int col = 0 ; col < 3u ; col++) { double accel_noise = 0.0; for(unsigned int axis = 0 ; axis < 3u ; axis++) { accel_noise += rotation[row][axis] * square(config->accel_noise_density[axis]) * rotation[col][axis]; } propagated[ESKF_VELOCITY_INDEX + row][ESKF_VELOCITY_INDEX + col] += accel_noise * dt_s; } propagated[ESKF_ATTITUDE_INDEX + row][ESKF_ATTITUDE_INDEX + row] += square(config->gyro_noise_density[row]) * dt_s; propagated[ESKF_ACCEL_BIAS_INDEX + row][ESKF_ACCEL_BIAS_INDEX + row] += square(config->accel_bias_random_walk[row]) * dt_s; propagated[ESKF_GYRO_BIAS_INDEX + row][ESKF_GYRO_BIAS_INDEX + row] += square(config->gyro_bias_random_walk[row]) * dt_s; } memcpy(filter->covariance, propagated, sizeof(propagated)); covariance_symmetrize_and_floor(filter, config->covariance_diagonal_floor); filter->propagation_count++; return eskf_state_is_finite(filter); } bool eskf_propagate(eskf_t* filter, const eskf_config_t* config, const double accel_mps2[3], const double gyro_rps[3], double dt_s) { if((filter == NULL) || !filter->initialized || (config == NULL) || !vector_is_finite(accel_mps2, 3u) || !vector_is_finite(gyro_rps, 3u) || !isfinite(dt_s) || (dt_s < 0.0) || !(config->max_propagation_step_s > 0.0)) { return false; } if(dt_s == 0.0) { return true; } unsigned int steps = (unsigned int)ceil(dt_s / config->max_propagation_step_s); if((steps == 0u) || (steps > 10000u)) { return false; } double step_s = dt_s / (double)steps; for(unsigned int step = 0 ; step < steps ; step++) { if(!eskf_propagate_step(filter, config, accel_mps2, gyro_rps, step_s)) { return false; } } return true; } static bool eskf_inject_error(eskf_t* filter, const double error[ESKF_ERROR_STATE_DIM]) { for(unsigned int i = 0 ; i < 3u ; i++) { filter->position_m[i] += error[ESKF_POSITION_INDEX + i]; filter->velocity_mps[i] += error[ESKF_VELOCITY_INDEX + i]; filter->accel_bias_mps2[i] += error[ESKF_ACCEL_BIAS_INDEX + i]; filter->gyro_bias_rps[i] += error[ESKF_GYRO_BIAS_INDEX + i]; } double attitude_error[3] = { error[ESKF_ATTITUDE_INDEX], error[ESKF_ATTITUDE_INDEX + 1u], error[ESKF_ATTITUDE_INDEX + 2u] }; double delta_quaternion[4]; double updated_quaternion[4]; rotation_vector_quaternion(attitude_error, delta_quaternion); quaternion_multiply(filter->body_to_nav_quaternion, delta_quaternion, updated_quaternion); memcpy(filter->body_to_nav_quaternion, updated_quaternion, sizeof(updated_quaternion)); if(!quaternion_normalize(filter->body_to_nav_quaternion)) { return false; } /* Right attitude-error reset after q <- q (x) Exp(delta_theta). */ double reset[ESKF_ERROR_STATE_DIM][ESKF_ERROR_STATE_DIM] = {{0.0}}; for(unsigned int i = 0 ; i < ESKF_ERROR_STATE_DIM ; i++) { reset[i][i] = 1.0; } double attitude_skew[3][3]; skew_matrix(attitude_error, attitude_skew); for(unsigned int row = 0 ; row < 3u ; row++) { for(unsigned int col = 0 ; col < 3u ; col++) { reset[ESKF_ATTITUDE_INDEX + row][ESKF_ATTITUDE_INDEX + col] -= 0.5 * attitude_skew[row][col]; } } double intermediate[ESKF_ERROR_STATE_DIM][ESKF_ERROR_STATE_DIM] = {{0.0}}; double reset_covariance[ESKF_ERROR_STATE_DIM][ESKF_ERROR_STATE_DIM] = {{0.0}}; for(unsigned int row = 0 ; row < ESKF_ERROR_STATE_DIM ; row++) { for(unsigned int col = 0 ; col < ESKF_ERROR_STATE_DIM ; col++) { for(unsigned int k = 0 ; k < ESKF_ERROR_STATE_DIM ; k++) { intermediate[row][col] += reset[row][k] * filter->covariance[k][col]; } } } for(unsigned int row = 0 ; row < ESKF_ERROR_STATE_DIM ; row++) { for(unsigned int col = 0 ; col < ESKF_ERROR_STATE_DIM ; col++) { for(unsigned int k = 0 ; k < ESKF_ERROR_STATE_DIM ; k++) { reset_covariance[row][col] += intermediate[row][k] * reset[col][k]; } } } memcpy(filter->covariance, reset_covariance, sizeof(reset_covariance)); return true; } static bool eskf_selected_update(eskf_t* filter, unsigned int selected_state_index, const double residual[3], const double measurement_covariance[9], bool gate_enabled, double gate, double* nis_out) { if((selected_state_index + 3u) > ESKF_ERROR_STATE_DIM || !vector_is_finite(residual, 3u) || !vector_is_finite(measurement_covariance, 9u)) { return false; } double covariance_inverse[9]; if(!inverse_symmetric3(measurement_covariance, covariance_inverse)) { return false; } double innovation[9]; for(unsigned int row = 0 ; row < 3u ; row++) { for(unsigned int col = 0 ; col < 3u ; col++) { innovation[row * 3u + col] = filter->covariance[selected_state_index + row][selected_state_index + col] + measurement_covariance[row * 3u + col]; } } double innovation_inverse[9]; if(!inverse_symmetric3(innovation, innovation_inverse)) { return false; } double nis = 0.0; for(unsigned int row = 0 ; row < 3u ; row++) { for(unsigned int col = 0 ; col < 3u ; col++) { nis += residual[row] * innovation_inverse[row * 3u + col] * residual[col]; } } if(nis_out != NULL) { *nis_out = nis; } if(!isfinite(nis) || (gate_enabled && (nis > gate))) { return false; } double gain[ESKF_ERROR_STATE_DIM][3] = {{0.0}}; for(unsigned int row = 0 ; row < ESKF_ERROR_STATE_DIM ; row++) { for(unsigned int col = 0 ; col < 3u ; col++) { for(unsigned int k = 0 ; k < 3u ; k++) { gain[row][col] += filter->covariance[row][selected_state_index + k] * innovation_inverse[k * 3u + col]; } } } double error[ESKF_ERROR_STATE_DIM] = {0.0}; for(unsigned int row = 0 ; row < ESKF_ERROR_STATE_DIM ; row++) { for(unsigned int col = 0 ; col < 3u ; col++) { error[row] += gain[row][col] * residual[col]; } } double joseph_left[ESKF_ERROR_STATE_DIM][ESKF_ERROR_STATE_DIM] = {{0.0}}; for(unsigned int row = 0 ; row < ESKF_ERROR_STATE_DIM ; row++) { joseph_left[row][row] = 1.0; for(unsigned int col = 0 ; col < 3u ; col++) { joseph_left[row][selected_state_index + col] -= gain[row][col]; } } double intermediate[ESKF_ERROR_STATE_DIM][ESKF_ERROR_STATE_DIM] = {{0.0}}; double updated_covariance[ESKF_ERROR_STATE_DIM][ESKF_ERROR_STATE_DIM] = {{0.0}}; for(unsigned int row = 0 ; row < ESKF_ERROR_STATE_DIM ; row++) { for(unsigned int col = 0 ; col < ESKF_ERROR_STATE_DIM ; col++) { for(unsigned int k = 0 ; k < ESKF_ERROR_STATE_DIM ; k++) { intermediate[row][col] += joseph_left[row][k] * filter->covariance[k][col]; } } } for(unsigned int row = 0 ; row < ESKF_ERROR_STATE_DIM ; row++) { for(unsigned int col = 0 ; col < ESKF_ERROR_STATE_DIM ; col++) { for(unsigned int k = 0 ; k < ESKF_ERROR_STATE_DIM ; k++) { updated_covariance[row][col] += intermediate[row][k] * joseph_left[col][k]; } for(unsigned int first = 0 ; first < 3u ; first++) { for(unsigned int second = 0 ; second < 3u ; second++) { updated_covariance[row][col] += gain[row][first] * measurement_covariance[first * 3u + second] * gain[col][second]; } } } } memcpy(filter->covariance, updated_covariance, sizeof(updated_covariance)); if(!eskf_inject_error(filter, error)) { return false; } covariance_symmetrize_and_floor(filter, 1.0e-12); return eskf_state_is_finite(filter); } eskf_update_result_t eskf_position_update( eskf_t* filter, const eskf_config_t* config, const double position_m[3], const double covariance_m2[9], double* nis_out) { if(nis_out != NULL) { *nis_out = NAN; } if((filter == NULL) || !filter->initialized || (config == NULL) || !vector_is_finite(position_m, 3u) || !vector_is_finite(covariance_m2, 9u)) { return ESKF_UPDATE_INVALID; } double residual[3]; for(unsigned int i = 0 ; i < 3u ; i++) { residual[i] = position_m[i] - filter->position_m[i]; } double nis = NAN; if(!eskf_selected_update(filter, ESKF_POSITION_INDEX, residual, covariance_m2, true, config->position_nis_gate, &nis)) { filter->last_position_nis = nis; if(isfinite(nis) && (nis > config->position_nis_gate)) { filter->position_update_rejected_count++; if(nis_out != NULL) { *nis_out = nis; } return ESKF_UPDATE_REJECTED_NIS; } return ESKF_UPDATE_INVALID; } filter->last_position_nis = nis; filter->position_update_accepted_count++; if(nis_out != NULL) { *nis_out = nis; } return ESKF_UPDATE_ACCEPTED; } bool eskf_zero_velocity_update(eskf_t* filter, double velocity_std_mps) { if((filter == NULL) || !filter->initialized || !(velocity_std_mps > 0.0) || !isfinite(velocity_std_mps)) { return false; } double residual[3] = { -filter->velocity_mps[0], -filter->velocity_mps[1], -filter->velocity_mps[2] }; double covariance[9] = {0.0}; for(unsigned int i = 0 ; i < 3u ; i++) { covariance[i * 3u + i] = square(velocity_std_mps); } return eskf_selected_update(filter, ESKF_VELOCITY_INDEX, residual, covariance, false, 0.0, NULL); } bool eskf_zero_angular_rate_update( eskf_t* filter, const double measured_gyro_rps[3], const double measurement_std_rps[3]) { if((filter == NULL) || !filter->initialized || !vector_is_finite(measured_gyro_rps, 3u) || !vector_is_finite(measurement_std_rps, 3u)) { return false; } double residual[3]; double covariance[9] = {0.0}; for(unsigned int i = 0 ; i < 3u ; i++) { if(!(measurement_std_rps[i] > 0.0)) { return false; } residual[i] = measured_gyro_rps[i] - filter->gyro_bias_rps[i]; covariance[i * 3u + i] = square(measurement_std_rps[i]); } return eskf_selected_update(filter, ESKF_GYRO_BIAS_INDEX, residual, covariance, false, 0.0, NULL); } double eskf_speed_mps(const eskf_t* filter) { if((filter == NULL) || !filter->initialized) { return NAN; } return vector_norm3(filter->velocity_mps); } bool eskf_state_is_finite(const eskf_t* filter) { if((filter == NULL) || !filter->initialized || !vector_is_finite(filter->position_m, 3u) || !vector_is_finite(filter->velocity_mps, 3u) || !vector_is_finite(filter->body_to_nav_quaternion, 4u) || !vector_is_finite(filter->accel_bias_mps2, 3u) || !vector_is_finite(filter->gyro_bias_rps, 3u) || !vector_is_finite(&filter->covariance[0][0], ESKF_ERROR_STATE_DIM * ESKF_ERROR_STATE_DIM)) { return false; } for(unsigned int i = 0 ; i < ESKF_ERROR_STATE_DIM ; i++) { if(filter->covariance[i][i] < 0.0) { return false; } } return true; }