2015-08-11 03:28:42 -03:00
|
|
|
#include <AP_HAL/AP_HAL.h>
|
2016-03-10 20:41:18 -04:00
|
|
|
|
|
|
|
#include "AP_Compass.h"
|
2014-11-15 21:58:23 -04:00
|
|
|
#include "AP_Compass_Backend.h"
|
2018-09-28 13:11:53 -03:00
|
|
|
|
2019-06-13 19:56:12 -03:00
|
|
|
#include <AP_BattMonitor/AP_BattMonitor.h>
|
2020-04-14 07:38:50 -03:00
|
|
|
#include <AP_Vehicle/AP_Vehicle_Type.h>
|
2022-08-01 05:07:10 -03:00
|
|
|
#include <AP_BoardConfig/AP_BoardConfig.h>
|
2014-11-15 21:58:23 -04:00
|
|
|
|
2015-02-23 19:17:44 -04:00
|
|
|
extern const AP_HAL::HAL& hal;
|
|
|
|
|
2018-08-06 19:21:27 -03:00
|
|
|
AP_Compass_Backend::AP_Compass_Backend()
|
|
|
|
: _compass(AP::compass())
|
2016-11-03 21:47:43 -03:00
|
|
|
{
|
|
|
|
}
|
2015-01-19 12:58:26 -04:00
|
|
|
|
2015-03-18 19:15:22 -03:00
|
|
|
void AP_Compass_Backend::rotate_field(Vector3f &mag, uint8_t instance)
|
2015-02-23 19:17:44 -04:00
|
|
|
{
|
2019-11-20 03:18:10 -04:00
|
|
|
Compass::mag_state &state = _compass._state[Compass::StateIndex(instance)];
|
2020-04-22 02:18:07 -03:00
|
|
|
if (MAG_BOARD_ORIENTATION != ROTATION_NONE) {
|
|
|
|
mag.rotate(MAG_BOARD_ORIENTATION);
|
|
|
|
}
|
2016-11-06 06:04:48 -04:00
|
|
|
mag.rotate(state.rotation);
|
2015-01-19 12:58:26 -04:00
|
|
|
|
2022-08-01 05:07:10 -03:00
|
|
|
#ifdef HAL_HEATER_MAG_OFFSET
|
|
|
|
/*
|
|
|
|
apply compass compensations for boards that have a heater which
|
|
|
|
interferes with an internal compass. This needs to be applied
|
|
|
|
before the board orientation so it is independent of
|
|
|
|
AHRS_ORIENTATION
|
|
|
|
*/
|
|
|
|
if (!is_external(instance)) {
|
|
|
|
const uint32_t dev_id = uint32_t(_compass._state[Compass::StateIndex(instance)].dev_id);
|
|
|
|
static const struct offset {
|
|
|
|
uint32_t dev_id;
|
|
|
|
Vector3f ofs;
|
|
|
|
} offsets[] = HAL_HEATER_MAG_OFFSET;
|
|
|
|
const auto *bc = AP::boardConfig();
|
|
|
|
if (bc) {
|
|
|
|
for (const auto &o : offsets) {
|
|
|
|
if (o.dev_id == dev_id) {
|
|
|
|
mag += o.ofs * bc->get_heater_duty_cycle() * 0.01;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2021-12-04 00:22:56 -04:00
|
|
|
if (!state.external) {
|
2021-11-05 13:11:09 -03:00
|
|
|
mag.rotate(_compass._board_orientation);
|
2015-03-13 02:43:34 -03:00
|
|
|
} else {
|
|
|
|
// add user selectable orientation
|
2021-12-04 00:22:56 -04:00
|
|
|
mag.rotate((enum Rotation)state.orientation.get());
|
2015-02-23 19:17:44 -04:00
|
|
|
}
|
2015-03-18 19:15:22 -03:00
|
|
|
}
|
2015-02-23 19:17:44 -04:00
|
|
|
|
2017-09-05 22:58:35 -03:00
|
|
|
void AP_Compass_Backend::publish_raw_field(const Vector3f &mag, uint8_t instance)
|
2015-03-18 19:15:22 -03:00
|
|
|
{
|
2015-10-20 23:22:24 -03:00
|
|
|
// note that we do not set last_update_usec here as otherwise the
|
|
|
|
// EKF and DCM would end up consuming compass data at the full
|
|
|
|
// sensor rate. We want them to consume only the filtered fields
|
2019-05-26 22:49:12 -03:00
|
|
|
#if COMPASS_CAL_ENABLED
|
2020-04-14 11:09:12 -03:00
|
|
|
auto& cal = _compass._calibrator[_compass._get_priority(Compass::StateIndex(instance))];
|
|
|
|
if (cal != nullptr) {
|
2021-05-11 08:45:27 -03:00
|
|
|
Compass::mag_state &state = _compass._state[Compass::StateIndex(instance)];
|
|
|
|
state.last_update_ms = AP_HAL::millis();
|
2020-04-14 11:09:12 -03:00
|
|
|
cal->new_sample(mag);
|
|
|
|
}
|
2019-05-26 22:49:12 -03:00
|
|
|
#endif
|
2015-02-23 19:17:44 -04:00
|
|
|
}
|
|
|
|
|
2015-03-18 19:15:22 -03:00
|
|
|
void AP_Compass_Backend::correct_field(Vector3f &mag, uint8_t i)
|
2015-02-23 19:17:44 -04:00
|
|
|
{
|
2019-11-20 03:18:10 -04:00
|
|
|
Compass::mag_state &state = _compass._state[Compass::StateIndex(i)];
|
2015-03-18 19:15:22 -03:00
|
|
|
|
2021-12-04 00:22:56 -04:00
|
|
|
const Vector3f &offsets = state.offset.get();
|
2022-10-26 04:14:11 -03:00
|
|
|
#if AP_COMPASS_DIAGONALS_ENABLED
|
2021-12-04 00:22:56 -04:00
|
|
|
const Vector3f &diagonals = state.diagonals.get();
|
|
|
|
const Vector3f &offdiagonals = state.offdiagonals.get();
|
2022-09-27 03:25:12 -03:00
|
|
|
#endif
|
2015-02-23 19:17:44 -04:00
|
|
|
|
2018-01-05 06:08:04 -04:00
|
|
|
// add in the basic offsets
|
2015-02-23 19:17:44 -04:00
|
|
|
mag += offsets;
|
2015-03-18 20:18:47 -03:00
|
|
|
|
2019-11-26 18:11:54 -04:00
|
|
|
// add in scale factor, use a wide sanity check. The calibrator
|
|
|
|
// uses a narrower check.
|
|
|
|
if (_compass.have_scale_factor(i)) {
|
2021-12-04 00:22:56 -04:00
|
|
|
mag *= state.scale_factor;
|
2019-11-26 18:11:54 -04:00
|
|
|
}
|
|
|
|
|
2022-10-26 04:14:11 -03:00
|
|
|
#if AP_COMPASS_DIAGONALS_ENABLED
|
2018-01-05 06:08:04 -04:00
|
|
|
// apply eliptical correction
|
2023-01-16 02:04:15 -04:00
|
|
|
if (!diagonals.is_zero()) {
|
|
|
|
Matrix3f mat(
|
|
|
|
diagonals.x, offdiagonals.x, offdiagonals.y,
|
|
|
|
offdiagonals.x, diagonals.y, offdiagonals.z,
|
|
|
|
offdiagonals.y, offdiagonals.z, diagonals.z
|
|
|
|
);
|
2015-03-18 20:18:47 -03:00
|
|
|
|
2023-01-16 02:04:15 -04:00
|
|
|
mag = mat * mag;
|
|
|
|
}
|
2022-09-27 03:25:12 -03:00
|
|
|
#endif
|
2018-01-05 06:08:04 -04:00
|
|
|
|
2019-05-26 22:49:12 -03:00
|
|
|
#if COMPASS_MOT_ENABLED
|
2021-12-04 00:22:56 -04:00
|
|
|
const Vector3f &mot = state.motor_compensation.get();
|
2018-01-05 06:08:04 -04:00
|
|
|
/*
|
|
|
|
calculate motor-power based compensation
|
|
|
|
note that _motor_offset[] is kept even if compensation is not
|
|
|
|
being applied so it can be logged correctly
|
|
|
|
*/
|
|
|
|
state.motor_offset.zero();
|
|
|
|
if (_compass._per_motor.enabled() && i == 0) {
|
|
|
|
// per-motor correction is only valid for first compass
|
|
|
|
_compass._per_motor.compensate(state.motor_offset);
|
2018-03-01 17:42:31 -04:00
|
|
|
} else if (_compass._motor_comp_type == AP_COMPASS_MOT_COMP_THROTTLE) {
|
|
|
|
state.motor_offset = mot * _compass._thr;
|
|
|
|
} else if (_compass._motor_comp_type == AP_COMPASS_MOT_COMP_CURRENT) {
|
|
|
|
AP_BattMonitor &battery = AP::battery();
|
2019-07-07 11:36:39 -03:00
|
|
|
float current;
|
|
|
|
if (battery.current_amps(current)) {
|
|
|
|
state.motor_offset = mot * current;
|
2018-03-01 17:42:31 -04:00
|
|
|
}
|
2018-01-05 06:08:04 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
we apply the motor offsets after we apply the eliptical
|
|
|
|
correction. This is needed to match the way that the motor
|
|
|
|
compensation values are calculated, as they are calculated based
|
|
|
|
on final field outputs, not on the raw outputs
|
|
|
|
*/
|
|
|
|
mag += state.motor_offset;
|
2019-05-26 22:49:12 -03:00
|
|
|
#endif // COMPASS_MOT_ENABLED
|
2015-02-23 19:17:44 -04:00
|
|
|
}
|
2015-01-19 12:58:26 -04:00
|
|
|
|
2018-09-28 13:11:53 -03:00
|
|
|
void AP_Compass_Backend::accumulate_sample(Vector3f &field, uint8_t instance,
|
|
|
|
uint32_t max_samples)
|
|
|
|
{
|
|
|
|
/* rotate raw_field from sensor frame to body frame */
|
|
|
|
rotate_field(field, instance);
|
|
|
|
|
|
|
|
/* publish raw_field (uncorrected point sample) for calibration use */
|
|
|
|
publish_raw_field(field, instance);
|
|
|
|
|
|
|
|
/* correct raw_field for known errors */
|
|
|
|
correct_field(field, instance);
|
|
|
|
|
|
|
|
if (!field_ok(field)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
WITH_SEMAPHORE(_sem);
|
|
|
|
|
2019-11-20 03:18:10 -04:00
|
|
|
Compass::mag_state &state = _compass._state[Compass::StateIndex(instance)];
|
2018-10-16 05:59:51 -03:00
|
|
|
state.accum += field;
|
|
|
|
state.accum_count++;
|
|
|
|
if (max_samples && state.accum_count >= max_samples) {
|
|
|
|
state.accum_count /= 2;
|
|
|
|
state.accum /= 2;
|
2018-09-28 13:11:53 -03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void AP_Compass_Backend::drain_accumulated_samples(uint8_t instance,
|
|
|
|
const Vector3f *scaling)
|
|
|
|
{
|
2018-10-11 20:35:03 -03:00
|
|
|
WITH_SEMAPHORE(_sem);
|
2018-09-28 13:11:53 -03:00
|
|
|
|
2019-11-20 03:18:10 -04:00
|
|
|
Compass::mag_state &state = _compass._state[Compass::StateIndex(instance)];
|
2018-10-16 05:59:51 -03:00
|
|
|
|
|
|
|
if (state.accum_count == 0) {
|
2018-09-28 13:11:53 -03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (scaling) {
|
2018-10-16 05:59:51 -03:00
|
|
|
state.accum *= *scaling;
|
2018-09-28 13:11:53 -03:00
|
|
|
}
|
2018-10-16 05:59:51 -03:00
|
|
|
state.accum /= state.accum_count;
|
2018-09-28 13:11:53 -03:00
|
|
|
|
2018-10-16 05:59:51 -03:00
|
|
|
publish_filtered_field(state.accum, instance);
|
2018-09-28 13:11:53 -03:00
|
|
|
|
2018-10-16 05:59:51 -03:00
|
|
|
state.accum.zero();
|
|
|
|
state.accum_count = 0;
|
2018-09-28 13:11:53 -03:00
|
|
|
}
|
|
|
|
|
2015-03-18 19:15:22 -03:00
|
|
|
/*
|
|
|
|
copy latest data to the frontend from a backend
|
|
|
|
*/
|
|
|
|
void AP_Compass_Backend::publish_filtered_field(const Vector3f &mag, uint8_t instance)
|
|
|
|
{
|
2019-11-20 03:18:10 -04:00
|
|
|
Compass::mag_state &state = _compass._state[Compass::StateIndex(instance)];
|
2015-03-18 19:15:22 -03:00
|
|
|
|
|
|
|
state.field = mag;
|
|
|
|
|
2015-11-19 23:09:17 -04:00
|
|
|
state.last_update_ms = AP_HAL::millis();
|
|
|
|
state.last_update_usec = AP_HAL::micros();
|
2015-03-18 19:15:22 -03:00
|
|
|
}
|
|
|
|
|
2016-05-04 04:11:02 -03:00
|
|
|
void AP_Compass_Backend::set_last_update_usec(uint32_t last_update, uint8_t instance)
|
|
|
|
{
|
2019-11-20 03:18:10 -04:00
|
|
|
Compass::mag_state &state = _compass._state[Compass::StateIndex(instance)];
|
2016-05-04 04:11:02 -03:00
|
|
|
state.last_update_usec = last_update;
|
|
|
|
}
|
|
|
|
|
2015-03-18 19:15:22 -03:00
|
|
|
/*
|
|
|
|
register a new backend with frontend, returning instance which
|
|
|
|
should be used in publish_field()
|
|
|
|
*/
|
2019-11-20 03:18:10 -04:00
|
|
|
bool AP_Compass_Backend::register_compass(int32_t dev_id, uint8_t& instance) const
|
2015-03-18 19:15:22 -03:00
|
|
|
{
|
2019-11-20 03:18:10 -04:00
|
|
|
return _compass.register_compass(dev_id, instance);
|
2015-03-18 19:15:22 -03:00
|
|
|
}
|
|
|
|
|
2015-02-23 19:17:44 -04:00
|
|
|
|
|
|
|
/*
|
|
|
|
set dev_id for an instance
|
|
|
|
*/
|
|
|
|
void AP_Compass_Backend::set_dev_id(uint8_t instance, uint32_t dev_id)
|
|
|
|
{
|
2021-12-04 00:22:56 -04:00
|
|
|
_compass._state[Compass::StateIndex(instance)].dev_id.set_and_notify(dev_id);
|
2019-11-20 03:18:10 -04:00
|
|
|
_compass._state[Compass::StateIndex(instance)].detected_dev_id = dev_id;
|
2015-02-23 19:17:44 -04:00
|
|
|
}
|
|
|
|
|
2018-06-27 00:43:27 -03:00
|
|
|
/*
|
|
|
|
save dev_id, used by SITL
|
|
|
|
*/
|
|
|
|
void AP_Compass_Backend::save_dev_id(uint8_t instance)
|
|
|
|
{
|
2021-12-04 00:22:56 -04:00
|
|
|
_compass._state[Compass::StateIndex(instance)].dev_id.save();
|
2018-06-27 00:43:27 -03:00
|
|
|
}
|
|
|
|
|
2015-02-23 19:17:44 -04:00
|
|
|
/*
|
|
|
|
set external for an instance
|
|
|
|
*/
|
|
|
|
void AP_Compass_Backend::set_external(uint8_t instance, bool external)
|
|
|
|
{
|
2021-12-04 00:22:56 -04:00
|
|
|
if (_compass._state[Compass::StateIndex(instance)].external != 2) {
|
|
|
|
_compass._state[Compass::StateIndex(instance)].external.set_and_notify(external);
|
2016-04-28 20:16:14 -03:00
|
|
|
}
|
2015-02-23 19:17:44 -04:00
|
|
|
}
|
2016-01-28 13:07:00 -04:00
|
|
|
|
|
|
|
bool AP_Compass_Backend::is_external(uint8_t instance)
|
|
|
|
{
|
2021-12-04 00:22:56 -04:00
|
|
|
return _compass._state[Compass::StateIndex(instance)].external;
|
2016-01-28 13:07:00 -04:00
|
|
|
}
|
2016-11-06 06:04:48 -04:00
|
|
|
|
|
|
|
// set rotation of an instance
|
|
|
|
void AP_Compass_Backend::set_rotation(uint8_t instance, enum Rotation rotation)
|
|
|
|
{
|
2019-11-20 03:18:10 -04:00
|
|
|
_compass._state[Compass::StateIndex(instance)].rotation = rotation;
|
2016-11-06 06:04:48 -04:00
|
|
|
}
|
2018-03-12 04:59:32 -03:00
|
|
|
|
|
|
|
static constexpr float FILTER_KOEF = 0.1f;
|
|
|
|
|
|
|
|
/* Check that the compass value is valid by using a mean filter. If
|
|
|
|
* the value is further than filtrer_range from mean value, it is
|
|
|
|
* rejected.
|
|
|
|
*/
|
|
|
|
bool AP_Compass_Backend::field_ok(const Vector3f &field)
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
if (field.is_inf() || field.is_nan()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const float range = (float)_compass.get_filter_range();
|
2018-04-11 21:07:33 -03:00
|
|
|
if (range <= 0) {
|
2018-03-12 04:59:32 -03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
const float length = field.length();
|
|
|
|
|
|
|
|
if (is_zero(_mean_field_length)) {
|
|
|
|
_mean_field_length = length;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ret = true;
|
|
|
|
const float d = fabsf(_mean_field_length - length) / (_mean_field_length + length); // diff divide by mean value in percent ( with the *200.0f on later line)
|
|
|
|
float koeff = FILTER_KOEF;
|
|
|
|
|
|
|
|
if (d * 200.0f > range) { // check the difference from mean value outside allowed range
|
|
|
|
// printf("\nCompass field length error: mean %f got %f\n", (double)_mean_field_length, (double)length );
|
|
|
|
ret = false;
|
|
|
|
koeff /= (d * 10.0f); // 2.5 and more, so one bad sample never change mean more than 4%
|
|
|
|
_error_count++;
|
|
|
|
}
|
|
|
|
_mean_field_length = _mean_field_length * (1 - koeff) + length * koeff; // complimentary filter 1/k
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-07-19 04:24:21 -03:00
|
|
|
|
|
|
|
enum Rotation AP_Compass_Backend::get_board_orientation(void) const
|
|
|
|
{
|
|
|
|
return _compass._board_orientation;
|
|
|
|
}
|