2 ******************************************************************************
3 * @addtogroup OpenPilotModules OpenPilot Modules
5 * @addtogroup State Estimation
6 * @brief Acquires sensor data and computes state estimate
9 * @file stateestimation.c
10 * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2013.
11 * @brief Module to handle all comms to the AHRS on a periodic basis.
13 * @see The GNU Public License (GPL) Version 3
15 ******************************************************************************/
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 3 of the License, or
20 * (at your option) any later version.
22 * This program is distributed in the hope that it will be useful, but
23 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
24 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
27 * You should have received a copy of the GNU General Public License along
28 * with this program; if not, write to the Free Software Foundation, Inc.,
29 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
32 #include "inc/stateestimation.h"
34 #include <callbackinfo.h>
36 #include <gyrosensor.h>
37 #include <accelsensor.h>
38 #include <magsensor.h>
39 #include <barosensor.h>
40 #include <airspeedsensor.h>
41 #include <gpspositionsensor.h>
42 #include <gpsvelocitysensor.h>
43 #include <homelocation.h>
44 #include <auxmagsensor.h>
46 #include <gyrostate.h>
47 #include <accelstate.h>
49 #include <airspeedstate.h>
50 #include <attitudestate.h>
51 #include <positionstate.h>
52 #include <velocitystate.h>
54 #include "revosettings.h"
55 #include "flightstatus.h"
57 #include "CoordinateConversions.h"
60 #define STACK_SIZE_BYTES 256
61 #define CALLBACK_PRIORITY CALLBACK_PRIORITY_REGULAR
62 #define TASK_PRIORITY CALLBACK_TASK_FLIGHTCONTROL
65 // Private filter init const
66 #define FILTER_INIT_FORCE -1
67 #define FILTER_INIT_IF_POSSIBLE -2
69 // local macros, ONLY to be used in the middle of StateEstimationCb in section RUNSTATE_LOAD after the update of states updated!
70 #define FETCH_SENSOR_FROM_UAVOBJECT_CHECK_AND_LOAD_TO_STATE_3_DIMENSIONS(sensorname, shortname, a1, a2, a3) \
71 if (IS_SET(states.updated, SENSORUPDATES_##shortname)) { \
73 sensorname##Get(&s); \
74 if (IS_REAL(s.a1) && IS_REAL(s.a2) && IS_REAL(s.a3)) { \
75 states.shortname[0] = s.a1; \
76 states.shortname[1] = s.a2; \
77 states.shortname[2] = s.a3; \
80 UNSET_MASK(states.updated, SENSORUPDATES_##shortname); \
84 #define FETCH_SENSOR_FROM_UAVOBJECT_CHECK_AND_LOAD_TO_STATE_1_DIMENSION_WITH_CUSTOM_EXTRA_CHECK(sensorname, shortname, a1, EXTRACHECK) \
85 if (IS_SET(states.updated, SENSORUPDATES_##shortname)) { \
87 sensorname##Get(&s); \
88 if (IS_REAL(s.a1) && EXTRACHECK) { \
89 states.shortname[0] = s.a1; \
92 UNSET_MASK(states.updated, SENSORUPDATES_##shortname); \
96 #define FETCH_SENSOR_FROM_UAVOBJECT_CHECK_AND_LOAD_TO_STATE_2_DIMENSION_WITH_CUSTOM_EXTRA_CHECK(sensorname, shortname, a1, a2, EXTRACHECK) \
97 if (IS_SET(states.updated, SENSORUPDATES_##shortname)) { \
99 sensorname##Get(&s); \
100 if (IS_REAL(s.a1) && IS_REAL(s.a2) && EXTRACHECK) { \
101 states.shortname[0] = s.a1; \
102 states.shortname[1] = s.a2; \
105 UNSET_MASK(states.updated, SENSORUPDATES_##shortname); \
109 // local macros, ONLY to be used in the middle of StateEstimationCb in section RUNSTATE_SAVE before the check of alarms!
110 #define EXPORT_STATE_TO_UAVOBJECT_IF_UPDATED_3_DIMENSIONS(statename, shortname, a1, a2, a3) \
111 if (IS_SET(states.updated, SENSORUPDATES_##shortname)) { \
113 statename##Get(&s); \
114 s.a1 = states.shortname[0]; \
115 s.a2 = states.shortname[1]; \
116 s.a3 = states.shortname[2]; \
117 statename##Set(&s); \
120 #define EXPORT_STATE_TO_UAVOBJECT_IF_UPDATED_2_DIMENSIONS(statename, shortname, a1, a2) \
121 if (IS_SET(states.updated, SENSORUPDATES_##shortname)) { \
123 statename##Get(&s); \
124 s.a1 = states.shortname[0]; \
125 s.a2 = states.shortname[1]; \
126 statename##Set(&s); \
131 struct filterPipelineStruct
;
133 typedef const struct filterPipelineStruct
{
134 const stateFilter
*filter
;
135 const struct filterPipelineStruct
*next
;
139 static DelayedCallbackInfo
*stateEstimationCallback
;
141 static volatile RevoSettingsData revoSettings
;
142 static volatile sensorUpdates updatedSensors
;
143 static volatile int32_t fusionAlgorithm
= -1;
144 static const filterPipeline
*filterChain
= NULL
;
146 // different filters available to state estimation
147 static stateFilter magFilter
;
148 static stateFilter baroFilter
;
149 static stateFilter baroiFilter
;
150 static stateFilter velocityFilter
;
151 static stateFilter altitudeFilter
;
152 static stateFilter airFilter
;
153 static stateFilter stationaryFilter
;
154 static stateFilter llaFilter
;
155 static stateFilter cfFilter
;
156 static stateFilter cfmFilter
;
157 static stateFilter ekf13iFilter
;
158 static stateFilter ekf13Filter
;
160 // this is a hack to provide a computational shortcut for faster gyro state progression
161 static float gyroRaw
[3];
162 static float gyroDelta
[3];
164 // preconfigured filter chains selectable via revoSettings.FusionAlgorithm
165 static const filterPipeline
*cfQueue
= &(filterPipeline
) {
166 .filter
= &airFilter
,
167 .next
= &(filterPipeline
) {
168 .filter
= &baroiFilter
,
169 .next
= &(filterPipeline
) {
170 .filter
= &altitudeFilter
,
171 .next
= &(filterPipeline
) {
178 static const filterPipeline
*cfmiQueue
= &(filterPipeline
) {
179 .filter
= &magFilter
,
180 .next
= &(filterPipeline
) {
181 .filter
= &airFilter
,
182 .next
= &(filterPipeline
) {
183 .filter
= &baroiFilter
,
184 .next
= &(filterPipeline
) {
185 .filter
= &altitudeFilter
,
186 .next
= &(filterPipeline
) {
187 .filter
= &cfmFilter
,
194 static const filterPipeline
*cfmQueue
= &(filterPipeline
) {
195 .filter
= &magFilter
,
196 .next
= &(filterPipeline
) {
197 .filter
= &airFilter
,
198 .next
= &(filterPipeline
) {
199 .filter
= &llaFilter
,
200 .next
= &(filterPipeline
) {
201 .filter
= &baroFilter
,
202 .next
= &(filterPipeline
) {
203 .filter
= &altitudeFilter
,
204 .next
= &(filterPipeline
) {
205 .filter
= &cfmFilter
,
213 static const filterPipeline
*ekf13iQueue
= &(filterPipeline
) {
214 .filter
= &magFilter
,
215 .next
= &(filterPipeline
) {
216 .filter
= &airFilter
,
217 .next
= &(filterPipeline
) {
218 .filter
= &baroiFilter
,
219 .next
= &(filterPipeline
) {
220 .filter
= &stationaryFilter
,
221 .next
= &(filterPipeline
) {
222 .filter
= &ekf13iFilter
,
223 .next
= &(filterPipeline
) {
224 .filter
= &velocityFilter
,
233 static const filterPipeline
*ekf13Queue
= &(filterPipeline
) {
234 .filter
= &magFilter
,
235 .next
= &(filterPipeline
) {
236 .filter
= &airFilter
,
237 .next
= &(filterPipeline
) {
238 .filter
= &llaFilter
,
239 .next
= &(filterPipeline
) {
240 .filter
= &baroFilter
,
241 .next
= &(filterPipeline
) {
242 .filter
= &ekf13Filter
,
243 .next
= &(filterPipeline
) {
244 .filter
= &velocityFilter
,
255 static void settingsUpdatedCb(UAVObjEvent
*objEv
);
256 static void sensorUpdatedCb(UAVObjEvent
*objEv
);
257 static void homeLocationUpdatedCb(UAVObjEvent
*objEv
);
258 static void StateEstimationCb(void);
260 static inline int32_t maxint32_t(int32_t a
, int32_t b
)
269 * Initialise the module. Called before the start function
270 * \returns 0 on success or -1 if initialisation failed
272 int32_t StateEstimationInitialize(void)
274 RevoSettingsInitialize();
276 GyroSensorInitialize();
277 MagSensorInitialize();
278 AuxMagSensorInitialize();
279 BaroSensorInitialize();
280 AirspeedSensorInitialize();
281 GPSVelocitySensorInitialize();
282 GPSPositionSensorInitialize();
284 HomeLocationInitialize();
286 GyroStateInitialize();
287 AccelStateInitialize();
288 MagStateInitialize();
289 AirspeedStateInitialize();
290 PositionStateInitialize();
291 VelocityStateInitialize();
293 RevoSettingsConnectCallback(&settingsUpdatedCb
);
295 HomeLocationConnectCallback(&homeLocationUpdatedCb
);
297 GyroSensorConnectCallback(&sensorUpdatedCb
);
298 AccelSensorConnectCallback(&sensorUpdatedCb
);
299 MagSensorConnectCallback(&sensorUpdatedCb
);
300 BaroSensorConnectCallback(&sensorUpdatedCb
);
301 AirspeedSensorConnectCallback(&sensorUpdatedCb
);
302 AuxMagSensorConnectCallback(&sensorUpdatedCb
);
303 GPSVelocitySensorConnectCallback(&sensorUpdatedCb
);
304 GPSPositionSensorConnectCallback(&sensorUpdatedCb
);
306 uint32_t stack_required
= STACK_SIZE_BYTES
;
307 // Initialize Filters
308 stack_required
= maxint32_t(stack_required
, filterMagInitialize(&magFilter
));
309 stack_required
= maxint32_t(stack_required
, filterBaroiInitialize(&baroiFilter
));
310 stack_required
= maxint32_t(stack_required
, filterBaroInitialize(&baroFilter
));
311 stack_required
= maxint32_t(stack_required
, filterVelocityInitialize(&velocityFilter
));
312 stack_required
= maxint32_t(stack_required
, filterAltitudeInitialize(&altitudeFilter
));
313 stack_required
= maxint32_t(stack_required
, filterAirInitialize(&airFilter
));
314 stack_required
= maxint32_t(stack_required
, filterStationaryInitialize(&stationaryFilter
));
315 stack_required
= maxint32_t(stack_required
, filterLLAInitialize(&llaFilter
));
316 stack_required
= maxint32_t(stack_required
, filterCFInitialize(&cfFilter
));
317 stack_required
= maxint32_t(stack_required
, filterCFMInitialize(&cfmFilter
));
318 stack_required
= maxint32_t(stack_required
, filterEKF13iInitialize(&ekf13iFilter
));
319 stack_required
= maxint32_t(stack_required
, filterEKF13Initialize(&ekf13Filter
));
321 stateEstimationCallback
= PIOS_CALLBACKSCHEDULER_Create(&StateEstimationCb
, CALLBACK_PRIORITY
, TASK_PRIORITY
, CALLBACKINFO_RUNNING_STATEESTIMATION
, stack_required
);
327 * Start the task. Expects all objects to be initialized by this point.
328 * \returns 0 on success or -1 if initialisation failed
330 int32_t StateEstimationStart(void)
332 RevoSettingsConnectCallback(&settingsUpdatedCb
);
334 // Force settings update to make sure rotation loaded
335 settingsUpdatedCb(NULL
);
340 MODULE_INITCALL(StateEstimationInitialize
, StateEstimationStart
);
346 static void StateEstimationCb(void)
348 static filterResult alarm
= FILTERRESULT_OK
;
349 static filterResult lastAlarm
= FILTERRESULT_UNINITIALISED
;
350 static uint16_t alarmcounter
= 0;
351 static const filterPipeline
*current
;
352 static stateEstimation states
;
353 static uint32_t last_time
;
354 static uint16_t bootDelay
= 64;
356 // after system startup, first few sensor readings might be messed up, delay until everything has settled
359 PIOS_CALLBACKSCHEDULER_Schedule(stateEstimationCallback
, TIMEOUT_MS
, CALLBACK_UPDATEMODE_SOONER
);
363 alarm
= FILTERRESULT_OK
;
365 // set alarm to warning if called through timeout
366 if (updatedSensors
== 0) {
367 if (PIOS_DELAY_DiffuS(last_time
) > 1000 * TIMEOUT_MS
) {
368 alarm
= FILTERRESULT_WARNING
;
371 last_time
= PIOS_DELAY_GetRaw();
374 // check if a new filter chain should be initialized
375 if (fusionAlgorithm
!= revoSettings
.FusionAlgorithm
) {
377 FlightStatusGet(&fs
);
378 if (fs
.Armed
== FLIGHTSTATUS_ARMED_DISARMED
|| fusionAlgorithm
== FILTER_INIT_FORCE
) {
379 const filterPipeline
*newFilterChain
;
380 switch ((RevoSettingsFusionAlgorithmOptions
)revoSettings
.FusionAlgorithm
) {
381 case REVOSETTINGS_FUSIONALGORITHM_BASICCOMPLEMENTARY
:
382 newFilterChain
= cfQueue
;
384 case REVOSETTINGS_FUSIONALGORITHM_COMPLEMENTARYMAG
:
385 newFilterChain
= cfmiQueue
;
387 case REVOSETTINGS_FUSIONALGORITHM_COMPLEMENTARYMAGGPSOUTDOOR
:
388 newFilterChain
= cfmQueue
;
390 case REVOSETTINGS_FUSIONALGORITHM_INS13INDOOR
:
391 newFilterChain
= ekf13iQueue
;
393 case REVOSETTINGS_FUSIONALGORITHM_GPSNAVIGATIONINS13
:
394 newFilterChain
= ekf13Queue
;
397 newFilterChain
= NULL
;
399 // initialize filters in chain
400 current
= newFilterChain
;
402 while (current
!= NULL
) {
403 int32_t result
= current
->filter
->init((stateFilter
*)current
->filter
);
408 current
= current
->next
;
411 AlarmsSet(SYSTEMALARMS_ALARM_ATTITUDE
, SYSTEMALARMS_ALARM_ERROR
);
414 // set new fusion algortithm
415 filterChain
= newFilterChain
;
416 fusionAlgorithm
= revoSettings
.FusionAlgorithm
;
421 // read updated sensor UAVObjects and set initial state
422 states
.updated
= updatedSensors
;
425 // fetch sensors, check values, and load into state struct
426 FETCH_SENSOR_FROM_UAVOBJECT_CHECK_AND_LOAD_TO_STATE_3_DIMENSIONS(GyroSensor
, gyro
, x
, y
, z
);
427 if (IS_SET(states
.updated
, SENSORUPDATES_gyro
)) {
428 gyroRaw
[0] = states
.gyro
[0];
429 gyroRaw
[1] = states
.gyro
[1];
430 gyroRaw
[2] = states
.gyro
[2];
432 FETCH_SENSOR_FROM_UAVOBJECT_CHECK_AND_LOAD_TO_STATE_3_DIMENSIONS(AccelSensor
, accel
, x
, y
, z
);
433 FETCH_SENSOR_FROM_UAVOBJECT_CHECK_AND_LOAD_TO_STATE_3_DIMENSIONS(MagSensor
, boardMag
, x
, y
, z
);
434 FETCH_SENSOR_FROM_UAVOBJECT_CHECK_AND_LOAD_TO_STATE_3_DIMENSIONS(AuxMagSensor
, auxMag
, x
, y
, z
);
435 FETCH_SENSOR_FROM_UAVOBJECT_CHECK_AND_LOAD_TO_STATE_3_DIMENSIONS(GPSVelocitySensor
, vel
, North
, East
, Down
);
436 FETCH_SENSOR_FROM_UAVOBJECT_CHECK_AND_LOAD_TO_STATE_1_DIMENSION_WITH_CUSTOM_EXTRA_CHECK(BaroSensor
, baro
, Altitude
, true);
437 FETCH_SENSOR_FROM_UAVOBJECT_CHECK_AND_LOAD_TO_STATE_2_DIMENSION_WITH_CUSTOM_EXTRA_CHECK(AirspeedSensor
, airspeed
, CalibratedAirspeed
, TrueAirspeed
, s
.SensorConnected
== AIRSPEEDSENSOR_SENSORCONNECTED_TRUE
);
439 // GPS position data (LLA) is not fetched here since it does not contain floats. The filter must do all checks itself
441 // at this point sensor state is stored in "states" with some rudimentary filtering applied
443 // apply all filters in the current filter chain
444 current
= filterChain
;
446 // we are not done, re-dispatch self execution
449 filterResult result
= current
->filter
->filter((stateFilter
*)current
->filter
, &states
);
450 if (result
> alarm
) {
453 current
= current
->next
;
456 // the final output of filters is saved in state variables
457 // EXPORT_STATE_TO_UAVOBJECT_IF_UPDATED_3_DIMENSIONS(GyroState, gyro, x, y, z) // replaced by performance shortcut
458 if (IS_SET(states
.updated
, SENSORUPDATES_gyro
)) {
459 gyroDelta
[0] = states
.gyro
[0] - gyroRaw
[0];
460 gyroDelta
[1] = states
.gyro
[1] - gyroRaw
[1];
461 gyroDelta
[2] = states
.gyro
[2] - gyroRaw
[2];
463 EXPORT_STATE_TO_UAVOBJECT_IF_UPDATED_3_DIMENSIONS(AccelState
, accel
, x
, y
, z
);
464 if (IS_SET(states
.updated
, SENSORUPDATES_mag
)) {
471 switch (states
.magStatus
) {
473 s
.Source
= MAGSTATE_SOURCE_ONBOARD
;
476 s
.Source
= MAGSTATE_SOURCE_AUX
;
479 s
.Source
= MAGSTATE_SOURCE_INVALID
;
484 EXPORT_STATE_TO_UAVOBJECT_IF_UPDATED_3_DIMENSIONS(PositionState
, pos
, North
, East
, Down
);
485 EXPORT_STATE_TO_UAVOBJECT_IF_UPDATED_3_DIMENSIONS(VelocityState
, vel
, North
, East
, Down
);
486 EXPORT_STATE_TO_UAVOBJECT_IF_UPDATED_2_DIMENSIONS(AirspeedState
, airspeed
, CalibratedAirspeed
, TrueAirspeed
);
487 // attitude nees manual conversion from quaternion to euler
488 if (IS_SET(states
.updated
, SENSORUPDATES_attitude
)) { \
490 AttitudeStateGet(&s
);
491 s
.q1
= states
.attitude
[0];
492 s
.q2
= states
.attitude
[1];
493 s
.q3
= states
.attitude
[2];
494 s
.q4
= states
.attitude
[3];
495 Quaternion2RPY(&s
.q1
, &s
.Roll
);
496 AttitudeStateSet(&s
);
498 // throttle alarms, raise alarm flags immediately
499 // but require system to run for a while before decreasing
500 // to prevent alarm flapping
501 if (alarm
>= lastAlarm
) {
505 if (alarmcounter
< 100) {
513 // clear alarms if everything is alright, then schedule callback execution after timeout
514 if (lastAlarm
== FILTERRESULT_WARNING
) {
515 AlarmsSet(SYSTEMALARMS_ALARM_ATTITUDE
, SYSTEMALARMS_ALARM_WARNING
);
516 } else if (lastAlarm
== FILTERRESULT_CRITICAL
) {
517 AlarmsSet(SYSTEMALARMS_ALARM_ATTITUDE
, SYSTEMALARMS_ALARM_CRITICAL
);
518 } else if (lastAlarm
>= FILTERRESULT_ERROR
) {
519 AlarmsSet(SYSTEMALARMS_ALARM_ATTITUDE
, SYSTEMALARMS_ALARM_ERROR
);
521 AlarmsClear(SYSTEMALARMS_ALARM_ATTITUDE
);
524 if (updatedSensors
) {
525 PIOS_CALLBACKSCHEDULER_Dispatch(stateEstimationCallback
);
527 PIOS_CALLBACKSCHEDULER_Schedule(stateEstimationCallback
, TIMEOUT_MS
, CALLBACK_UPDATEMODE_SOONER
);
533 * Callback for eventdispatcher when RevoSettings has been updated
535 static void settingsUpdatedCb(__attribute__((unused
)) UAVObjEvent
*ev
)
537 RevoSettingsGet((RevoSettingsData
*)&revoSettings
);
541 * Callback for eventdispatcher when HomeLocation has been updated
543 static void homeLocationUpdatedCb(__attribute__((unused
)) UAVObjEvent
*ev
)
545 // Ask for a filter init (necessary for LLA filter)
546 // Only possible if disarmed
547 fusionAlgorithm
= FILTER_INIT_IF_POSSIBLE
;
552 * Callback for eventdispatcher when any sensor UAVObject has been updated
553 * updates the list of "recently updated UAVObjects" and dispatches the state estimator callback
555 static void sensorUpdatedCb(UAVObjEvent
*ev
)
561 if (ev
->obj
== GyroSensorHandle()) {
562 updatedSensors
|= SENSORUPDATES_gyro
;
563 // shortcut - update GyroState right away
567 t
.x
= s
.x
+ gyroDelta
[0];
568 t
.y
= s
.y
+ gyroDelta
[1];
569 t
.z
= s
.z
+ gyroDelta
[2];
573 if (ev
->obj
== AccelSensorHandle()) {
574 updatedSensors
|= SENSORUPDATES_accel
;
577 if (ev
->obj
== MagSensorHandle()) {
578 updatedSensors
|= SENSORUPDATES_boardMag
;
581 if (ev
->obj
== AuxMagSensorHandle()) {
582 updatedSensors
|= SENSORUPDATES_auxMag
;
585 if (ev
->obj
== GPSPositionSensorHandle()) {
586 updatedSensors
|= SENSORUPDATES_lla
;
589 if (ev
->obj
== GPSVelocitySensorHandle()) {
590 updatedSensors
|= SENSORUPDATES_vel
;
593 if (ev
->obj
== BaroSensorHandle()) {
594 updatedSensors
|= SENSORUPDATES_baro
;
597 if (ev
->obj
== AirspeedSensorHandle()) {
598 updatedSensors
|= SENSORUPDATES_airspeed
;
601 PIOS_CALLBACKSCHEDULER_Dispatch(stateEstimationCallback
);