2 ******************************************************************************
3 * @addtogroup OpenPilotModules OpenPilot Modules
5 * @addtogroup State Estimation
6 * @brief Acquires sensor data and computes state estimate
10 * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2013.
11 * @brief Magnetometer drift compensation, uses previous cycles
12 * AttitudeState for estimation
14 * @see The GNU Public License (GPL) Version 3
16 ******************************************************************************/
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 3 of the License, or
21 * (at your option) any later version.
23 * This program is distributed in the hope that it will be useful, but
24 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
25 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
28 * You should have received a copy of the GNU General Public License along
29 * with this program; if not, write to the Free Software Foundation, Inc.,
30 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
33 #include "inc/stateestimation.h"
34 #include <attitudestate.h>
35 #include <revocalibration.h>
36 #include <revosettings.h>
37 #include <systemalarms.h>
38 #include <homelocation.h>
39 #include <auxmagsettings.h>
40 #include <CoordinateConversions.h>
45 #define STACK_REQUIRED 256
49 RevoCalibrationData revoCalibration
;
50 RevoSettingsData revoSettings
;
51 AuxMagSettingsUsageOptions auxMagUsage
;
54 float homeLocationBe
[3];
64 static int32_t init(stateFilter
*self
);
65 static filterResult
filter(stateFilter
*self
, stateEstimation
*state
);
66 static bool checkMagValidity(struct data
*this, float error
, bool setAlarms
);
67 static void magOffsetEstimation(struct data
*this, float mag
[3]);
68 static float getMagError(struct data
*this, float mag
[3]);
70 int32_t filterMagInitialize(stateFilter
*handle
)
73 handle
->filter
= &filter
;
74 handle
->localdata
= pios_malloc(sizeof(struct data
));
75 return STACK_REQUIRED
;
78 static int32_t init(stateFilter
*self
)
80 struct data
*this = (struct data
*)self
->localdata
;
82 this->magBias
[0] = this->magBias
[1] = this->magBias
[2] = 0.0f
;
83 this->warningcount
= this->errorcount
= 0;
84 HomeLocationBeGet(this->homeLocationBe
);
85 // magBe holds the magnetic vector length (expected)
86 this->magBe
= vector_lengthf(this->homeLocationBe
, 3);
87 this->invMagBe
= 1.0f
/ this->magBe
;
88 RevoCalibrationGet(&this->revoCalibration
);
89 RevoSettingsGet(&this->revoSettings
);
90 AuxMagSettingsUsageGet(&this->auxMagUsage
);
94 static filterResult
filter(stateFilter
*self
, stateEstimation
*state
)
96 struct data
*this = (struct data
*)self
->localdata
;
99 float temp_mag
[3] = { 0 };
100 uint8_t temp_status
= MAGSTATUS_INVALID
;
101 uint8_t magSamples
= 0;
103 // Uses the external mag when available
104 if ((this->auxMagUsage
!= AUXMAGSETTINGS_USAGE_ONBOARDONLY
) &&
105 IS_SET(state
->updated
, SENSORUPDATES_auxMag
)) {
106 auxMagError
= getMagError(this, state
->auxMag
);
107 // Handles alarms only if it will rely on aux mag only
108 bool auxMagValid
= checkMagValidity(this, auxMagError
, (this->auxMagUsage
== AUXMAGSETTINGS_USAGE_AUXONLY
));
109 // if we are going to use Aux only, force the update even if mag is invalid
110 if (auxMagValid
|| (this->auxMagUsage
== AUXMAGSETTINGS_USAGE_AUXONLY
)) {
111 temp_mag
[0] = state
->auxMag
[0];
112 temp_mag
[1] = state
->auxMag
[1];
113 temp_mag
[2] = state
->auxMag
[2];
114 temp_status
= MAGSTATUS_AUX
;
119 if ((this->auxMagUsage
!= AUXMAGSETTINGS_USAGE_AUXONLY
) &&
120 IS_SET(state
->updated
, SENSORUPDATES_boardMag
)) {
121 // TODO:mag Offset estimation works with onboard mag only right now.
122 if (this->revoCalibration
.MagBiasNullingRate
> 0) {
123 magOffsetEstimation(this, state
->boardMag
);
125 boardMagError
= getMagError(this, state
->boardMag
);
126 // sets warning only if no mag data are available (aux is invalid or missing)
127 bool boardMagValid
= checkMagValidity(this, boardMagError
, (temp_status
== MAGSTATUS_INVALID
));
128 // force it to be set to board mag value if no data has been feed to temp_mag yet.
129 // this works also as a failsafe in case aux mag stops feeding data.
130 if (boardMagValid
|| (temp_status
== MAGSTATUS_INVALID
)) {
131 temp_mag
[0] += state
->boardMag
[0];
132 temp_mag
[1] += state
->boardMag
[1];
133 temp_mag
[2] += state
->boardMag
[2];
134 temp_status
= MAGSTATUS_OK
;
139 if (magSamples
> 1) {
145 if (temp_status
!= MAGSTATUS_INVALID
) {
146 state
->mag
[0] = temp_mag
[0];
147 state
->mag
[1] = temp_mag
[1];
148 state
->mag
[2] = temp_mag
[2];
149 state
->updated
|= SENSORUPDATES_mag
;
151 state
->magStatus
= temp_status
;
152 return FILTERRESULT_OK
;
156 * check validity of magnetometers
158 static bool checkMagValidity(struct data
*this, float error
, bool setAlarms
)
160 #define ALARM_THRESHOLD 5
163 if (error
< this->revoSettings
.MagnetometerMaxDeviation
.Warning
) {
164 this->warningcount
= 0;
165 this->errorcount
= 0;
166 AlarmsClear(SYSTEMALARMS_ALARM_MAGNETOMETER
);
170 if (error
< this->revoSettings
.MagnetometerMaxDeviation
.Error
) {
171 this->errorcount
= 0;
172 if (this->warningcount
> ALARM_THRESHOLD
) {
174 AlarmsSet(SYSTEMALARMS_ALARM_MAGNETOMETER
, SYSTEMALARMS_ALARM_WARNING
);
178 this->warningcount
++;
183 if (this->errorcount
> ALARM_THRESHOLD
) {
185 AlarmsSet(SYSTEMALARMS_ALARM_MAGNETOMETER
, SYSTEMALARMS_ALARM_CRITICAL
);
191 // still in "grace period"
195 static float getMagError(struct data
*this, float mag
[3])
198 float magnitude
= vector_lengthf(mag
, 3);
199 // absolute value of relative error against Be
200 float error
= fabsf(magnitude
- this->magBe
) * this->invMagBe
;
206 * Perform an update of the @ref MagBias based on
207 * Magmeter Offset Cancellation: Theory and Implementation,
208 * revisited William Premerlani, October 14, 2011
210 void magOffsetEstimation(struct data
*this, float mag
[3])
213 // Constants, to possibly go into a UAVO
214 static const float MIN_NORM_DIFFERENCE
= 50;
216 static float B2
[3] = { 0, 0, 0 };
219 MagBiasGet(&magBias
);
221 // Remove the current estimate of the bias
227 if (B2
[0] == 0 && B2
[1] == 0 && B2
[2] == 0) {
234 float B1
[3] = { mag
->x
, mag
->y
, mag
->z
};
235 float norm_diff
= sqrtf(powf(B2
[0] - B1
[0], 2) + powf(B2
[1] - B1
[1], 2) + powf(B2
[2] - B1
[2], 2));
236 if (norm_diff
> MIN_NORM_DIFFERENCE
) {
237 float norm_b1
= sqrtf(B1
[0] * B1
[0] + B1
[1] * B1
[1] + B1
[2] * B1
[2]);
238 float norm_b2
= sqrtf(B2
[0] * B2
[0] + B2
[1] * B2
[1] + B2
[2] * B2
[2]);
239 float scale
= cal
.MagBiasNullingRate
* (norm_b2
- norm_b1
) / norm_diff
;
240 float b_error
[3] = { (B2
[0] - B1
[0]) * scale
, (B2
[1] - B1
[1]) * scale
, (B2
[2] - B1
[2]) * scale
};
242 magBias
.x
+= b_error
[0];
243 magBias
.y
+= b_error
[1];
244 magBias
.z
+= b_error
[2];
246 MagBiasSet(&magBias
);
248 // Store this value to compare against next update
249 B2
[0] = B1
[0]; B2
[1] = B1
[1]; B2
[2] = B1
[2];
253 const float Rxy
= sqrtf(this->homeLocationBe
[0] * this->homeLocationBe
[0] + this->homeLocationBe
[1] * this->homeLocationBe
[1]);
254 const float Rz
= this->homeLocationBe
[2];
256 const float rate
= this->revoCalibration
.MagBiasNullingRate
;
262 AttitudeStateData attitude
;
263 AttitudeStateGet(&attitude
);
265 // Get the rotation matrix
266 Quaternion2R(&attitude
.q1
, Rot
);
268 // Rotate the mag into the NED frame
269 B_e
[0] = Rot
[0][0] * mag
[0] + Rot
[1][0] * mag
[1] + Rot
[2][0] * mag
[2];
270 B_e
[1] = Rot
[0][1] * mag
[0] + Rot
[1][1] * mag
[1] + Rot
[2][1] * mag
[2];
271 B_e
[2] = Rot
[0][2] * mag
[0] + Rot
[1][2] * mag
[1] + Rot
[2][2] * mag
[2];
273 float cy
= cosf(DEG2RAD(attitude
.Yaw
));
274 float sy
= sinf(DEG2RAD(attitude
.Yaw
));
276 xy
[0] = cy
* B_e
[0] + sy
* B_e
[1];
277 xy
[1] = -sy
* B_e
[0] + cy
* B_e
[1];
279 float xy_norm
= sqrtf(xy
[0] * xy
[0] + xy
[1] * xy
[1]);
281 delta
[0] = -rate
* (xy
[0] / xy_norm
* Rxy
- xy
[0]);
282 delta
[1] = -rate
* (xy
[1] / xy_norm
* Rxy
- xy
[1]);
283 delta
[2] = -rate
* (Rz
- B_e
[2]);
285 if (!isnan(delta
[0]) && !isinf(delta
[0]) &&
286 !isnan(delta
[1]) && !isinf(delta
[1]) &&
287 !isnan(delta
[2]) && !isinf(delta
[2])) {
288 this->magBias
[0] += delta
[0];
289 this->magBias
[1] += delta
[1];
290 this->magBias
[2] += delta
[2];
293 // Add bias to state estimation
294 mag
[0] += this->magBias
[0];
295 mag
[1] += this->magBias
[1];
296 mag
[2] += this->magBias
[2];