mirror of
https://github.com/betaflight/betaflight.git
synced 2025-07-23 16:25:31 +03:00
212 lines
7.1 KiB
C
212 lines
7.1 KiB
C
/*
|
|
* This file is part of Cleanflight.
|
|
*
|
|
* Cleanflight 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.
|
|
*
|
|
* Cleanflight 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 Cleanflight. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <math.h>
|
|
|
|
#include "platform.h"
|
|
|
|
#include "build/debug.h"
|
|
|
|
#include "common/axis.h"
|
|
#include "common/maths.h"
|
|
#include "common/filter.h"
|
|
|
|
#include "drivers/system.h"
|
|
|
|
#include "io/beeper.h"
|
|
#include "io/statusindicator.h"
|
|
|
|
#include "sensors/sensors.h"
|
|
#include "sensors/boardalignment.h"
|
|
#include "sensors/gyro.h"
|
|
|
|
gyro_t gyro; // gyro access functions
|
|
sensor_align_e gyroAlign = 0;
|
|
|
|
static int32_t gyroADC[XYZ_AXIS_COUNT];
|
|
float gyroADCf[XYZ_AXIS_COUNT];
|
|
|
|
static int32_t gyroZero[XYZ_AXIS_COUNT] = { 0, 0, 0 };
|
|
static const gyroConfig_t *gyroConfig;
|
|
static uint16_t calibratingG = 0;
|
|
|
|
static filterApplyFnPtr softLpfFilterApplyFn;
|
|
static void *softLpfFilter[3];
|
|
static filterApplyFnPtr notchFilter1ApplyFn;
|
|
static void *notchFilter1[3];
|
|
static filterApplyFnPtr notchFilter2ApplyFn;
|
|
static void *notchFilter2[3];
|
|
|
|
void gyroInit(const gyroConfig_t *gyroConfigToUse)
|
|
{
|
|
static biquadFilter_t gyroFilterLPF[XYZ_AXIS_COUNT];
|
|
static pt1Filter_t gyroFilterPt1[XYZ_AXIS_COUNT];
|
|
static firFilterDenoise_t gyroDenoiseState[XYZ_AXIS_COUNT];
|
|
static biquadFilter_t gyroFilterNotch_1[XYZ_AXIS_COUNT];
|
|
static biquadFilter_t gyroFilterNotch_2[XYZ_AXIS_COUNT];
|
|
|
|
gyroConfig = gyroConfigToUse;
|
|
|
|
softLpfFilterApplyFn = nullFilterApply;
|
|
notchFilter1ApplyFn = nullFilterApply;
|
|
notchFilter2ApplyFn = nullFilterApply;
|
|
|
|
if (gyroConfig->gyro_soft_lpf_hz) { // Initialisation needs to happen once samplingrate is known
|
|
if (gyroConfig->gyro_soft_lpf_type == FILTER_BIQUAD) {
|
|
softLpfFilterApplyFn = (filterApplyFnPtr)biquadFilterApply;
|
|
for (int axis = 0; axis < 3; axis++) {
|
|
softLpfFilter[axis] = &gyroFilterLPF[axis];
|
|
biquadFilterInitLPF(softLpfFilter[axis], gyroConfig->gyro_soft_lpf_hz, gyro.targetLooptime);
|
|
}
|
|
} else if (gyroConfig->gyro_soft_lpf_type == FILTER_PT1) {
|
|
softLpfFilterApplyFn = (filterApplyFnPtr)pt1FilterApply;
|
|
const float gyroDt = (float) gyro.targetLooptime * 0.000001f;
|
|
for (int axis = 0; axis < 3; axis++) {
|
|
softLpfFilter[axis] = &gyroFilterPt1[axis];
|
|
pt1FilterInit(softLpfFilter[axis], gyroConfig->gyro_soft_lpf_hz, gyroDt);
|
|
}
|
|
} else {
|
|
softLpfFilterApplyFn = (filterApplyFnPtr)firFilterDenoiseUpdate;
|
|
for (int axis = 0; axis < 3; axis++) {
|
|
softLpfFilter[axis] = &gyroDenoiseState[axis];
|
|
firFilterDenoiseInit(softLpfFilter[axis], gyroConfig->gyro_soft_lpf_hz, gyro.targetLooptime);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (gyroConfig->gyro_soft_notch_hz_1) {
|
|
notchFilter1ApplyFn = (filterApplyFnPtr)biquadFilterApply;
|
|
const float gyroSoftNotchQ1 = filterGetNotchQ(gyroConfig->gyro_soft_notch_hz_1, gyroConfig->gyro_soft_notch_cutoff_1);
|
|
for (int axis = 0; axis < 3; axis++) {
|
|
notchFilter1[axis] = &gyroFilterNotch_1[axis];
|
|
biquadFilterInit(notchFilter1[axis], gyroConfig->gyro_soft_notch_hz_1, gyro.targetLooptime, gyroSoftNotchQ1, FILTER_NOTCH);
|
|
}
|
|
}
|
|
if (gyroConfig->gyro_soft_notch_hz_2) {
|
|
notchFilter2ApplyFn = (filterApplyFnPtr)biquadFilterApply;
|
|
const float gyroSoftNotchQ2 = filterGetNotchQ(gyroConfig->gyro_soft_notch_hz_2, gyroConfig->gyro_soft_notch_cutoff_2);
|
|
for (int axis = 0; axis < 3; axis++) {
|
|
notchFilter2[axis] = &gyroFilterNotch_2[axis];
|
|
biquadFilterInit(notchFilter2[axis], gyroConfig->gyro_soft_notch_hz_2, gyro.targetLooptime, gyroSoftNotchQ2, FILTER_NOTCH);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool isGyroCalibrationComplete(void)
|
|
{
|
|
return calibratingG == 0;
|
|
}
|
|
|
|
static bool isOnFinalGyroCalibrationCycle(void)
|
|
{
|
|
return calibratingG == 1;
|
|
}
|
|
|
|
static uint16_t gyroCalculateCalibratingCycles(void)
|
|
{
|
|
return (CALIBRATING_GYRO_CYCLES / gyro.targetLooptime) * CALIBRATING_GYRO_CYCLES;
|
|
}
|
|
|
|
static bool isOnFirstGyroCalibrationCycle(void)
|
|
{
|
|
return calibratingG == gyroCalculateCalibratingCycles();
|
|
}
|
|
|
|
void gyroSetCalibrationCycles(void)
|
|
{
|
|
calibratingG = gyroCalculateCalibratingCycles();
|
|
}
|
|
|
|
static void performGyroCalibration(uint8_t gyroMovementCalibrationThreshold)
|
|
{
|
|
static int32_t g[3];
|
|
static stdev_t var[3];
|
|
|
|
for (int axis = 0; axis < 3; axis++) {
|
|
|
|
// Reset g[axis] at start of calibration
|
|
if (isOnFirstGyroCalibrationCycle()) {
|
|
g[axis] = 0;
|
|
devClear(&var[axis]);
|
|
}
|
|
|
|
// Sum up CALIBRATING_GYRO_CYCLES readings
|
|
g[axis] += gyroADC[axis];
|
|
devPush(&var[axis], gyroADC[axis]);
|
|
|
|
// Reset global variables to prevent other code from using un-calibrated data
|
|
gyroADC[axis] = 0;
|
|
gyroZero[axis] = 0;
|
|
|
|
if (isOnFinalGyroCalibrationCycle()) {
|
|
float dev = devStandardDeviation(&var[axis]);
|
|
// check deviation and startover in case the model was moved
|
|
if (gyroMovementCalibrationThreshold && dev > gyroMovementCalibrationThreshold) {
|
|
gyroSetCalibrationCycles();
|
|
return;
|
|
}
|
|
gyroZero[axis] = (g[axis] + (gyroCalculateCalibratingCycles() / 2)) / gyroCalculateCalibratingCycles();
|
|
}
|
|
}
|
|
|
|
if (isOnFinalGyroCalibrationCycle()) {
|
|
beeper(BEEPER_GYRO_CALIBRATED);
|
|
}
|
|
calibratingG--;
|
|
|
|
}
|
|
|
|
void gyroUpdate(void)
|
|
{
|
|
// range: +/- 8192; +/- 2000 deg/sec
|
|
if (!gyro.read(&gyro)) {
|
|
return;
|
|
}
|
|
gyro.dataReady = false;
|
|
gyroADC[X] = gyro.gyroADCRaw[X];
|
|
gyroADC[Y] = gyro.gyroADCRaw[Y];
|
|
gyroADC[Z] = gyro.gyroADCRaw[Z];
|
|
|
|
alignSensors(gyroADC, gyroAlign);
|
|
|
|
const bool calibrationComplete = isGyroCalibrationComplete();
|
|
if (!calibrationComplete) {
|
|
performGyroCalibration(gyroConfig->gyroMovementCalibrationThreshold);
|
|
}
|
|
|
|
for (int axis = 0; axis < XYZ_AXIS_COUNT; axis++) {
|
|
gyroADC[axis] -= gyroZero[axis];
|
|
// scale gyro output to degrees per second
|
|
gyroADCf[axis] = (float)gyroADC[axis] * gyro.scale;
|
|
|
|
DEBUG_SET(DEBUG_GYRO, axis, lrintf(gyroADCf[axis]));
|
|
|
|
gyroADCf[axis] = softLpfFilterApplyFn(softLpfFilter[axis], gyroADCf[axis]);
|
|
|
|
DEBUG_SET(DEBUG_NOTCH, axis, lrintf(gyroADCf[axis]));
|
|
|
|
gyroADCf[axis] = notchFilter1ApplyFn(notchFilter1[axis], gyroADCf[axis]);
|
|
|
|
gyroADCf[axis] = notchFilter2ApplyFn(notchFilter2[axis], gyroADCf[axis]);
|
|
|
|
if (!calibrationComplete) {
|
|
gyroADC[axis] = lrintf(gyroADCf[axis] / gyro.scale);
|
|
}
|
|
}
|
|
}
|