2014-01-01 21:15:58 -04:00
|
|
|
/*
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* NavEKF based AHRS (Attitude Heading Reference System) interface for
|
|
|
|
* ArduPilot
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
#include <AP_HAL.h>
|
|
|
|
#include <AP_AHRS.h>
|
|
|
|
|
|
|
|
#if AP_AHRS_NAVEKF_AVAILABLE
|
|
|
|
|
2014-01-01 22:05:28 -04:00
|
|
|
extern const AP_HAL::HAL& hal;
|
|
|
|
|
2014-01-01 21:15:58 -04:00
|
|
|
// return the smoothed gyro vector corrected for drift
|
2014-07-13 08:56:39 -03:00
|
|
|
const Vector3f &AP_AHRS_NavEKF::get_gyro(void) const
|
2014-01-01 21:15:58 -04:00
|
|
|
{
|
2014-04-15 17:21:12 -03:00
|
|
|
if (!using_EKF()) {
|
|
|
|
return AP_AHRS_DCM::get_gyro();
|
|
|
|
}
|
2014-07-13 08:56:39 -03:00
|
|
|
return _gyro_estimate;
|
2014-01-01 21:15:58 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
const Matrix3f &AP_AHRS_NavEKF::get_dcm_matrix(void) const
|
|
|
|
{
|
2014-01-04 00:21:24 -04:00
|
|
|
if (!using_EKF()) {
|
2014-01-01 22:05:28 -04:00
|
|
|
return AP_AHRS_DCM::get_dcm_matrix();
|
|
|
|
}
|
|
|
|
return _dcm_matrix;
|
2014-01-01 21:15:58 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
const Vector3f &AP_AHRS_NavEKF::get_gyro_drift(void) const
|
|
|
|
{
|
2014-07-13 08:56:39 -03:00
|
|
|
if (!using_EKF()) {
|
|
|
|
return AP_AHRS_DCM::get_gyro_drift();
|
|
|
|
}
|
|
|
|
return _gyro_bias;
|
2014-01-01 21:15:58 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void AP_AHRS_NavEKF::update(void)
|
|
|
|
{
|
|
|
|
AP_AHRS_DCM::update();
|
2014-01-01 22:47:40 -04:00
|
|
|
|
|
|
|
// keep DCM attitude available for get_secondary_attitude()
|
|
|
|
_dcm_attitude(roll, pitch, yaw);
|
|
|
|
|
2014-01-01 22:05:28 -04:00
|
|
|
if (!ekf_started) {
|
2014-03-09 05:44:13 -03:00
|
|
|
// if we have a GPS lock we can start the EKF
|
2014-03-30 08:00:25 -03:00
|
|
|
if (get_gps().status() >= AP_GPS::GPS_OK_FIX_3D) {
|
2014-01-04 02:04:34 -04:00
|
|
|
if (start_time_ms == 0) {
|
|
|
|
start_time_ms = hal.scheduler->millis();
|
|
|
|
}
|
|
|
|
if (hal.scheduler->millis() - start_time_ms > startup_delay_ms) {
|
|
|
|
ekf_started = true;
|
2014-02-20 04:31:51 -04:00
|
|
|
EKF.InitialiseFilterDynamic();
|
2014-01-04 02:04:34 -04:00
|
|
|
}
|
2014-01-01 22:05:28 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ekf_started) {
|
|
|
|
EKF.UpdateFilter();
|
2014-01-04 01:00:47 -04:00
|
|
|
EKF.getRotationBodyToNED(_dcm_matrix);
|
2014-01-02 01:16:29 -04:00
|
|
|
if (using_EKF()) {
|
2014-01-01 22:05:28 -04:00
|
|
|
Vector3f eulers;
|
|
|
|
EKF.getEulerAngles(eulers);
|
|
|
|
roll = eulers.x;
|
|
|
|
pitch = eulers.y;
|
|
|
|
yaw = eulers.z;
|
|
|
|
roll_sensor = degrees(roll) * 100;
|
|
|
|
pitch_sensor = degrees(pitch) * 100;
|
|
|
|
yaw_sensor = degrees(yaw) * 100;
|
|
|
|
if (yaw_sensor < 0)
|
|
|
|
yaw_sensor += 36000;
|
2014-02-13 04:53:14 -04:00
|
|
|
update_trig();
|
2014-07-13 08:56:39 -03:00
|
|
|
|
|
|
|
// keep _gyro_bias for get_gyro_drift()
|
|
|
|
EKF.getGyroBias(_gyro_bias);
|
|
|
|
_gyro_bias = -_gyro_bias;
|
|
|
|
|
|
|
|
// calculate corrected gryo estimate for get_gyro()
|
|
|
|
_gyro_estimate.zero();
|
|
|
|
uint8_t healthy_count = 0;
|
|
|
|
for (uint8_t i=0; i<_ins.get_gyro_count(); i++) {
|
|
|
|
if (_ins.get_gyro_health(i)) {
|
|
|
|
_gyro_estimate += _ins.get_gyro(i);
|
|
|
|
healthy_count++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (healthy_count > 1) {
|
|
|
|
_gyro_estimate /= healthy_count;
|
|
|
|
}
|
|
|
|
_gyro_estimate += _gyro_bias;
|
2014-01-01 22:05:28 -04:00
|
|
|
}
|
|
|
|
}
|
2014-01-01 21:15:58 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void AP_AHRS_NavEKF::reset(bool recover_eulers)
|
|
|
|
{
|
|
|
|
AP_AHRS_DCM::reset(recover_eulers);
|
2014-01-01 22:05:28 -04:00
|
|
|
if (ekf_started) {
|
2014-01-30 18:29:20 -04:00
|
|
|
EKF.InitialiseFilterBootstrap();
|
2014-01-01 22:05:28 -04:00
|
|
|
}
|
2014-01-01 21:15:58 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// reset the current attitude, used on new IMU calibration
|
|
|
|
void AP_AHRS_NavEKF::reset_attitude(const float &_roll, const float &_pitch, const float &_yaw)
|
|
|
|
{
|
|
|
|
AP_AHRS_DCM::reset_attitude(_roll, _pitch, _yaw);
|
2014-01-01 22:05:28 -04:00
|
|
|
if (ekf_started) {
|
2014-01-30 18:29:20 -04:00
|
|
|
EKF.InitialiseFilterBootstrap();
|
2014-01-01 22:05:28 -04:00
|
|
|
}
|
2014-01-01 21:15:58 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// dead-reckoning support
|
|
|
|
bool AP_AHRS_NavEKF::get_position(struct Location &loc)
|
|
|
|
{
|
2014-01-02 01:16:29 -04:00
|
|
|
if (using_EKF() && EKF.getLLH(loc)) {
|
|
|
|
return true;
|
2014-01-01 22:05:28 -04:00
|
|
|
}
|
2014-01-01 21:15:58 -04:00
|
|
|
return AP_AHRS_DCM::get_position(loc);
|
|
|
|
}
|
|
|
|
|
2014-01-01 22:05:28 -04:00
|
|
|
// status reporting of estimated errors
|
2014-01-01 21:15:58 -04:00
|
|
|
float AP_AHRS_NavEKF::get_error_rp(void)
|
|
|
|
{
|
|
|
|
return AP_AHRS_DCM::get_error_rp();
|
|
|
|
}
|
|
|
|
|
|
|
|
float AP_AHRS_NavEKF::get_error_yaw(void)
|
|
|
|
{
|
|
|
|
return AP_AHRS_DCM::get_error_yaw();
|
|
|
|
}
|
|
|
|
|
|
|
|
// return a wind estimation vector, in m/s
|
|
|
|
Vector3f AP_AHRS_NavEKF::wind_estimate(void)
|
|
|
|
{
|
2014-04-13 06:42:49 -03:00
|
|
|
if (!using_EKF()) {
|
2014-04-09 17:49:13 -03:00
|
|
|
// EKF does not estimate wind speed when there is no airspeed
|
|
|
|
// sensor active
|
2014-01-01 22:05:28 -04:00
|
|
|
return AP_AHRS_DCM::wind_estimate();
|
|
|
|
}
|
|
|
|
Vector3f wind;
|
|
|
|
EKF.getWind(wind);
|
|
|
|
return wind;
|
2014-01-01 21:15:58 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// return an airspeed estimate if available. return true
|
|
|
|
// if we have an estimate
|
2014-02-24 21:43:59 -04:00
|
|
|
bool AP_AHRS_NavEKF::airspeed_estimate(float *airspeed_ret) const
|
2014-01-01 21:15:58 -04:00
|
|
|
{
|
|
|
|
return AP_AHRS_DCM::airspeed_estimate(airspeed_ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
// true if compass is being used
|
|
|
|
bool AP_AHRS_NavEKF::use_compass(void)
|
|
|
|
{
|
|
|
|
return AP_AHRS_DCM::use_compass();
|
|
|
|
}
|
|
|
|
|
2014-01-01 22:47:40 -04:00
|
|
|
|
|
|
|
// return secondary attitude solution if available, as eulers in radians
|
|
|
|
bool AP_AHRS_NavEKF::get_secondary_attitude(Vector3f &eulers)
|
|
|
|
{
|
2014-01-02 01:16:29 -04:00
|
|
|
if (using_EKF()) {
|
2014-01-01 22:47:40 -04:00
|
|
|
// return DCM attitude
|
|
|
|
eulers = _dcm_attitude;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (ekf_started) {
|
|
|
|
// EKF is secondary
|
|
|
|
EKF.getEulerAngles(eulers);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
// no secondary available
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// return secondary position solution if available
|
|
|
|
bool AP_AHRS_NavEKF::get_secondary_position(struct Location &loc)
|
|
|
|
{
|
2014-01-02 01:16:29 -04:00
|
|
|
if (using_EKF()) {
|
2014-01-01 22:47:40 -04:00
|
|
|
// return DCM position
|
|
|
|
AP_AHRS_DCM::get_position(loc);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (ekf_started) {
|
|
|
|
// EKF is secondary
|
|
|
|
EKF.getLLH(loc);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
// no secondary available
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-01-01 23:25:41 -04:00
|
|
|
// EKF has a better ground speed vector estimate
|
|
|
|
Vector2f AP_AHRS_NavEKF::groundspeed_vector(void)
|
|
|
|
{
|
2014-01-02 01:16:29 -04:00
|
|
|
if (!using_EKF()) {
|
2014-01-01 23:25:41 -04:00
|
|
|
return AP_AHRS_DCM::groundspeed_vector();
|
|
|
|
}
|
|
|
|
Vector3f vec;
|
|
|
|
EKF.getVelNED(vec);
|
|
|
|
return Vector2f(vec.x, vec.y);
|
|
|
|
}
|
|
|
|
|
2014-03-30 08:00:25 -03:00
|
|
|
void AP_AHRS_NavEKF::set_home(const Location &loc)
|
2014-01-02 07:06:10 -04:00
|
|
|
{
|
2014-03-30 08:00:25 -03:00
|
|
|
AP_AHRS_DCM::set_home(loc);
|
2014-01-02 07:06:10 -04:00
|
|
|
}
|
|
|
|
|
2014-01-03 20:15:34 -04:00
|
|
|
// return true if inertial navigation is active
|
|
|
|
bool AP_AHRS_NavEKF::have_inertial_nav(void) const
|
|
|
|
{
|
|
|
|
return using_EKF();
|
|
|
|
}
|
|
|
|
|
2014-02-08 04:11:12 -04:00
|
|
|
// return a ground velocity in meters/second, North/East/Down
|
|
|
|
// order. Must only be called if have_inertial_nav() is true
|
|
|
|
bool AP_AHRS_NavEKF::get_velocity_NED(Vector3f &vec) const
|
2014-01-03 20:15:34 -04:00
|
|
|
{
|
|
|
|
if (using_EKF()) {
|
|
|
|
EKF.getVelNED(vec);
|
2014-02-08 04:11:12 -04:00
|
|
|
return true;
|
2014-01-03 20:15:34 -04:00
|
|
|
}
|
2014-02-08 04:11:12 -04:00
|
|
|
return false;
|
2014-01-03 20:15:34 -04:00
|
|
|
}
|
|
|
|
|
2014-02-08 04:11:12 -04:00
|
|
|
// return a relative ground position in meters/second, North/East/Down
|
|
|
|
// order. Must only be called if have_inertial_nav() is true
|
|
|
|
bool AP_AHRS_NavEKF::get_relative_position_NED(Vector3f &vec) const
|
2014-01-03 20:15:34 -04:00
|
|
|
{
|
2014-02-08 04:11:12 -04:00
|
|
|
if (using_EKF()) {
|
|
|
|
return EKF.getPosNED(vec);
|
2014-01-03 20:15:34 -04:00
|
|
|
}
|
2014-02-08 04:11:12 -04:00
|
|
|
return false;
|
2014-01-03 20:15:34 -04:00
|
|
|
}
|
|
|
|
|
2014-02-14 18:24:12 -04:00
|
|
|
bool AP_AHRS_NavEKF::using_EKF(void) const
|
|
|
|
{
|
|
|
|
return ekf_started && _ekf_use && EKF.healthy();
|
|
|
|
}
|
|
|
|
|
2014-05-15 04:09:18 -03:00
|
|
|
/*
|
|
|
|
check if the AHRS subsystem is healthy
|
|
|
|
*/
|
|
|
|
bool AP_AHRS_NavEKF::healthy(void)
|
|
|
|
{
|
|
|
|
if (_ekf_use) {
|
|
|
|
return ekf_started && EKF.healthy();
|
|
|
|
}
|
|
|
|
return AP_AHRS_DCM::healthy();
|
|
|
|
}
|
|
|
|
|
2014-01-01 21:15:58 -04:00
|
|
|
#endif // AP_AHRS_NAVEKF_AVAILABLE
|
2014-02-14 18:24:12 -04:00
|
|
|
|