F3

Dependencies:   mbed LSM6DS33_GR1

Files at this revision

API Documentation at this revision

Comitter:
gr66
Date:
Thu Sep 30 09:39:45 2021 +0000
Parent:
6:b51a20c39f79
Commit message:
F3

Changed in this revision

Fusion/Fusion.h Show annotated file Show diff for this revision Revisions of this file
Fusion/FusionAhrs.c Show annotated file Show diff for this revision Revisions of this file
Fusion/FusionAhrs.h Show annotated file Show diff for this revision Revisions of this file
Fusion/FusionBias.c Show annotated file Show diff for this revision Revisions of this file
Fusion/FusionBias.h Show annotated file Show diff for this revision Revisions of this file
Fusion/FusionCalibration.h Show annotated file Show diff for this revision Revisions of this file
Fusion/FusionTypes.h Show annotated file Show diff for this revision Revisions of this file
Fusion3.cpp Show annotated file Show diff for this revision Revisions of this file
LSM6DS33_GR1.lib Show annotated file Show diff for this revision Revisions of this file
TB5649.lib Show diff for this revision Revisions of this file
main_centrale_50.cpp Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Fusion/Fusion.h	Thu Sep 30 09:39:45 2021 +0000
@@ -0,0 +1,37 @@
+/**
+ * @file Fusion.h
+ * @author Seb Madgwick
+ * @brief Main header file for the library.  This is the only file that needs to
+ * be included when using the library.
+ *
+ * Fusion is an ANSI C99 compliment sensor fusion library for sensor arrays of
+ * gyroscopes, accelerometers, and magnetometers.  Fusion was specifically
+ * developed for use with embedded systems and has been optimised for execution
+ * speed.  The library includes modules for: attitude and heading reference
+ * system (AHRS) sensor fusion, gyroscope bias correction, and a tilt-
+ * compensated compass.
+ */
+
+#ifndef FUSION_H
+#define FUSION_H
+
+//------------------------------------------------------------------------------
+// Includes
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "FusionAhrs.h"
+#include "FusionBias.h"
+#include "FusionCalibration.h"
+//#include "FusionCompass.h"
+#include "FusionTypes.h"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+//------------------------------------------------------------------------------
+// End of file
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Fusion/FusionAhrs.c	Thu Sep 30 09:39:45 2021 +0000
@@ -0,0 +1,292 @@
+/**
+ * @file FusionAhrs.c
+ * @author Seb Madgwick
+ * @brief The AHRS sensor fusion algorithm to combines gyroscope, accelerometer,
+ * and magnetometer measurements into a single measurement of orientation
+ * relative to the Earth (NWU convention).
+ *
+ * The algorithm behaviour is governed by a gain.  A low gain will decrease the
+ * influence of the accelerometer and magnetometer so that the algorithm will
+ * better reject disturbances causes by translational motion and temporary
+ * magnetic distortions.  However, a low gain will also increase the risk of
+ * drift due to gyroscope calibration errors.  A typical gain value suitable for
+ * most applications is 0.5.
+ *
+ * The algorithm allows the application to define a minimum and maximum valid
+ * magnetic field magnitude.  The algorithm will ignore magnetic measurements
+ * that fall outside of this range.  This allows the algorithm to reject
+ * magnetic measurements that do not represent the direction of magnetic North.
+ * The typical magnitude of the Earth's magnetic field is between 20 uT and
+ * 70 uT.
+ *
+ * The algorithm can be used without a magnetometer.  Measurements of
+ * orientation obtained using only gyroscope and accelerometer measurements
+ * can be expected to drift in the yaw component of orientation only.  The
+ * application can reset the drift in yaw by setting the yaw to a specified
+ * angle at any time.
+ *
+ * The algorithm provides the measurement of orientation as a quaternion.  The
+ * library includes functions for converting this quaternion to a rotation
+ * matrix and Euler angles.
+ *
+ * The algorithm also provides a measurement of linear acceleration and Earth
+ * acceleration.  Linear acceleration is equal to the accelerometer  measurement
+ * with the 1 g of gravity removed.  Earth acceleration is a measurement of
+ * linear acceleration in the Earth coordinate frame.
+ */
+
+//------------------------------------------------------------------------------
+// Includes
+
+#include "FusionAhrs.h"
+#include <float.h> // FLT_MAX
+#include <math.h> // atan2f, cosf, sinf
+
+//------------------------------------------------------------------------------
+// Definitions
+
+/**
+ * @brief Initial gain used during the initialisation period.  The gain used by
+ * each algorithm iteration will ramp down from this initial gain to the
+ * specified algorithm gain over the initialisation period.
+ */
+#define INITIAL_GAIN (10.0f)
+
+/**
+ * @brief Initialisation period (in seconds).
+ */
+#define INITIALISATION_PERIOD (3.0f)
+
+//------------------------------------------------------------------------------
+// Functions
+
+/**
+ * @brief Initialises the AHRS algorithm structure.
+ * @param fusionAhrs AHRS algorithm structure.
+ * @param gain AHRS algorithm gain.
+ */
+void FusionAhrsInitialise(FusionAhrs * const fusionAhrs, const float gain) {
+    fusionAhrs->gain = gain;
+    fusionAhrs->minimumMagneticFieldSquared = 0.0f;
+    fusionAhrs->maximumMagneticFieldSquared = FLT_MAX;
+    fusionAhrs->quaternion = FUSION_QUATERNION_IDENTITY;
+    fusionAhrs->linearAcceleration = FUSION_VECTOR3_ZERO;
+    fusionAhrs->rampedGain = INITIAL_GAIN;
+    fusionAhrs->zeroYawPending = false;
+}
+
+/**
+ * @brief Sets the AHRS algorithm gain.  The gain must be equal or greater than
+ * zero.
+ * @param gain AHRS algorithm gain.
+ */
+void FusionAhrsSetGain(FusionAhrs * const fusionAhrs, const float gain) {
+    fusionAhrs->gain = gain;
+}
+
+/**
+ * @brief Sets the minimum and maximum valid magnetic field magnitudes in uT.
+ * @param fusionAhrs AHRS algorithm structure.
+ * @param minimumMagneticField Minimum valid magnetic field magnitude.
+ * @param maximumMagneticField Maximum valid magnetic field magnitude.
+ */
+void FusionAhrsSetMagneticField(FusionAhrs * const fusionAhrs, const float minimumMagneticField, const float maximumMagneticField) {
+    fusionAhrs->minimumMagneticFieldSquared = minimumMagneticField * minimumMagneticField;
+    fusionAhrs->maximumMagneticFieldSquared = maximumMagneticField * maximumMagneticField;
+}
+
+/**
+ * @brief Updates the AHRS algorithm.  This function should be called for each
+ * new gyroscope measurement.
+ * @param fusionAhrs AHRS algorithm structure.
+ * @param gyroscope Gyroscope measurement in degrees per second.
+ * @param accelerometer Accelerometer measurement in g.
+ * @param magnetometer Magnetometer measurement in uT.
+ * @param samplePeriod Sample period in seconds.  This is the difference in time
+ * between the current and previous gyroscope measurements.
+ */
+void FusionAhrsUpdate(FusionAhrs * const fusionAhrs, const FusionVector3 gyroscope, const FusionVector3 accelerometer, const FusionVector3 magnetometer, const float samplePeriod) {
+#define Q fusionAhrs->quaternion.element // define shorthand label for more readable code
+
+    // Calculate feedback error
+    FusionVector3 halfFeedbackError = FUSION_VECTOR3_ZERO; // scaled by 0.5 to avoid repeated multiplications by 2
+    do {
+        // Abandon feedback calculation if accelerometer measurement invalid
+        if ((accelerometer.axis.x == 0.0f) && (accelerometer.axis.y == 0.0f) && (accelerometer.axis.z == 0.0f)) {
+            break;
+        }
+
+        // Calculate direction of gravity assumed by quaternion
+        const FusionVector3 halfGravity = {
+            .axis.x = Q.x * Q.z - Q.w * Q.y,
+            .axis.y = Q.w * Q.x + Q.y * Q.z,
+            .axis.z = Q.w * Q.w - 0.5f + Q.z * Q.z,
+        }; // equal to 3rd column of rotation matrix representation scaled by 0.5
+
+        // Calculate accelerometer feedback error
+        halfFeedbackError = FusionVectorCrossProduct(FusionVectorFastNormalise(accelerometer), halfGravity);
+
+        // Abandon magnetometer feedback calculation if magnetometer measurement invalid
+        const float magnetometerMagnitudeSquared = FusionVectorMagnitudeSquared(magnetometer);
+        if ((magnetometerMagnitudeSquared < fusionAhrs->minimumMagneticFieldSquared) || (magnetometerMagnitudeSquared > fusionAhrs->maximumMagneticFieldSquared)) {
+            break;
+        }
+
+        // Compute direction of 'magnetic west' assumed by quaternion
+        const FusionVector3 halfWest = {
+            .axis.x = Q.x * Q.y + Q.w * Q.z,
+            .axis.y = Q.w * Q.w - 0.5f + Q.y * Q.y,
+            .axis.z = Q.y * Q.z - Q.w * Q.x
+        }; // equal to 2nd column of rotation matrix representation scaled by 0.5
+
+        // Calculate magnetometer feedback error
+        halfFeedbackError = FusionVectorAdd(halfFeedbackError, FusionVectorCrossProduct(FusionVectorFastNormalise(FusionVectorCrossProduct(accelerometer, magnetometer)), halfWest));
+
+    } while (false);
+
+    // Ramp down gain until initialisation complete
+    if (fusionAhrs->gain == 0) {
+        fusionAhrs->rampedGain = 0; // skip initialisation if gain is zero
+    }
+    float feedbackGain = fusionAhrs->gain;
+    if (fusionAhrs->rampedGain > fusionAhrs->gain) {
+        fusionAhrs->rampedGain -= (INITIAL_GAIN - fusionAhrs->gain) * samplePeriod / INITIALISATION_PERIOD;
+        feedbackGain = fusionAhrs->rampedGain;
+    }
+
+    // Convert gyroscope to radians per second scaled by 0.5
+    FusionVector3 halfGyroscope = FusionVectorMultiplyScalar(gyroscope, 0.5f * FusionDegreesToRadians(1.0f));
+
+    // Apply feedback to gyroscope
+    halfGyroscope = FusionVectorAdd(halfGyroscope, FusionVectorMultiplyScalar(halfFeedbackError, feedbackGain));
+
+    // Integrate rate of change of quaternion
+    fusionAhrs->quaternion = FusionQuaternionAdd(fusionAhrs->quaternion, FusionQuaternionMultiplyVector(fusionAhrs->quaternion, FusionVectorMultiplyScalar(halfGyroscope, samplePeriod)));
+
+    // Normalise quaternion
+    fusionAhrs->quaternion = FusionQuaternionFastNormalise(fusionAhrs->quaternion);
+
+    // Calculate linear acceleration
+    const FusionVector3 gravity = {
+        .axis.x = 2.0f * (Q.x * Q.z - Q.w * Q.y),
+        .axis.y = 2.0f * (Q.w * Q.x + Q.y * Q.z),
+        .axis.z = 2.0f * (Q.w * Q.w - 0.5f + Q.z * Q.z),
+    }; // equal to 3rd column of rotation matrix representation
+    fusionAhrs->linearAcceleration = FusionVectorSubtract(accelerometer, gravity);
+
+#undef Q // undefine shorthand label
+}
+
+/**
+ * @brief Updates the AHRS algorithm.  This function should be called for each
+ * new gyroscope measurement.
+ * @param fusionAhrs AHRS algorithm structure.
+ * @param gyroscope Gyroscope measurement in degrees per second.
+ * @param accelerometer Accelerometer measurement in g.
+ * @param samplePeriod Sample period in seconds.  This is the difference in time
+ * between the current and previous gyroscope measurements.
+ */
+void FusionAhrsUpdateWithoutMagnetometer(FusionAhrs * const fusionAhrs, const FusionVector3 gyroscope, const FusionVector3 accelerometer, const float samplePeriod) {
+
+    // Update AHRS algorithm
+    FusionAhrsUpdate(fusionAhrs, gyroscope, accelerometer, FUSION_VECTOR3_ZERO, samplePeriod);
+
+    // Zero yaw once initialisation complete
+    if (FusionAhrsIsInitialising(fusionAhrs) == true) {
+        fusionAhrs->zeroYawPending = true;
+    } else {
+        if (fusionAhrs->zeroYawPending == true) {
+            FusionAhrsSetYaw(fusionAhrs, 0.0f);
+            fusionAhrs->zeroYawPending = false;
+        }
+    }
+}
+
+/**
+ * @brief Gets the quaternion describing the sensor relative to the Earth.
+ * @param fusionAhrs AHRS algorithm structure.
+ * @return Quaternion describing the sensor relative to the Earth.
+ */
+FusionQuaternion FusionAhrsGetQuaternion(const FusionAhrs * const fusionAhrs) {
+    return FusionQuaternionConjugate(fusionAhrs->quaternion);
+}
+
+/**
+ * @brief Gets the linear acceleration measurement equal to the accelerometer
+ * measurement with the 1 g of gravity removed.
+ * @param fusionAhrs AHRS algorithm structure.
+ * @return Linear acceleration measurement.
+ */
+FusionVector3 FusionAhrsGetLinearAcceleration(const FusionAhrs * const fusionAhrs) {
+    return fusionAhrs->linearAcceleration;
+}
+
+/**
+ * @brief Gets the Earth acceleration measurement equal to linear acceleration
+ * in the Earth coordinate frame.
+ * @param fusionAhrs AHRS algorithm structure.
+ * @return Earth acceleration measurement.
+ */
+FusionVector3 FusionAhrsGetEarthAcceleration(const FusionAhrs * const fusionAhrs) {
+#define Q fusionAhrs->quaternion.element // define shorthand labels for more readable code
+#define A fusionAhrs->linearAcceleration.axis
+    const float qwqw = Q.w * Q.w; // calculate common terms to avoid repeated operations
+    const float qwqx = Q.w * Q.x;
+    const float qwqy = Q.w * Q.y;
+    const float qwqz = Q.w * Q.z;
+    const float qxqy = Q.x * Q.y;
+    const float qxqz = Q.x * Q.z;
+    const float qyqz = Q.y * Q.z;
+    const FusionVector3 earthAcceleration = {
+        .axis.x = 2.0f * ((qwqw - 0.5f + Q.x * Q.x) * A.x + (qxqy - qwqz) * A.y + (qxqz + qwqy) * A.z),
+        .axis.y = 2.0f * ((qxqy + qwqz) * A.x + (qwqw - 0.5f + Q.y * Q.y) * A.y + (qyqz - qwqx) * A.z),
+        .axis.z = 2.0f * ((qxqz - qwqy) * A.x + (qyqz + qwqx) * A.y + (qwqw - 0.5f + Q.z * Q.z) * A.z),
+    }; // transpose of a rotation matrix representation of the quaternion multiplied with the linear acceleration
+    return earthAcceleration;
+#undef Q // undefine shorthand label
+#undef A
+}
+
+/**
+ * @brief Reinitialise the AHRS algorithm.
+ * @param fusionAhrs AHRS algorithm structure.
+ */
+void FusionAhrsReinitialise(FusionAhrs * const fusionAhrs) {
+    fusionAhrs->quaternion = FUSION_QUATERNION_IDENTITY;
+    fusionAhrs->linearAcceleration = FUSION_VECTOR3_ZERO;
+    fusionAhrs->rampedGain = INITIAL_GAIN;
+}
+
+/**
+ * @brief Returns true while the AHRS algorithm is initialising.
+ * @param fusionAhrs AHRS algorithm structure.
+ * @return True while the AHRS algorithm is initialising.
+ */
+bool FusionAhrsIsInitialising(const FusionAhrs * const fusionAhrs) {
+    return fusionAhrs->rampedGain > fusionAhrs->gain;
+}
+
+/**
+ * @brief Sets the yaw component of the orientation measurement provided by the
+ * AHRS algorithm.  This function can be used to reset drift in yaw when the
+ * AHRS algorithm is being used without a magnetometer.
+ * @param fusionAhrs AHRS algorithm structure.
+ * @param yaw Yaw angle in degrees.
+ */
+void FusionAhrsSetYaw(FusionAhrs * const fusionAhrs, const float yaw) {
+#define Q fusionAhrs->quaternion.element // define shorthand label for more readable code
+    fusionAhrs->quaternion = FusionQuaternionNormalise(fusionAhrs->quaternion); // quaternion must be normalised accurately (approximation not sufficient)
+    const float inverseYaw = atan2f(Q.x * Q.y + Q.w * Q.z, Q.w * Q.w - 0.5f + Q.x * Q.x); // Euler angle of conjugate
+    const float halfInverseYawMinusOffset = 0.5f * (inverseYaw - FusionDegreesToRadians(yaw));
+    const FusionQuaternion inverseYawQuaternion = {
+        .element.w = cosf(halfInverseYawMinusOffset),
+        .element.x = 0.0f,
+        .element.y = 0.0f,
+        .element.z = -1.0f * sinf(halfInverseYawMinusOffset),
+    };
+    fusionAhrs->quaternion = FusionQuaternionMultiply(inverseYawQuaternion, fusionAhrs->quaternion);
+#undef Q // undefine shorthand label
+}
+
+//------------------------------------------------------------------------------
+// End of file
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Fusion/FusionAhrs.h	Thu Sep 30 09:39:45 2021 +0000
@@ -0,0 +1,82 @@
+/**
+ * @file FusionAhrs.h
+ * @author Seb Madgwick
+ * @brief The AHRS sensor fusion algorithm to combines gyroscope, accelerometer,
+ * and magnetometer measurements into a single measurement of orientation
+ * relative to the Earth (NWU convention).
+ *
+ * The algorithm behaviour is governed by a gain.  A low gain will decrease the
+ * influence of the accelerometer and magnetometer so that the algorithm will
+ * better reject disturbances causes by translational motion and temporary
+ * magnetic distortions.  However, a low gain will also increase the risk of
+ * drift due to gyroscope calibration errors.  A typical gain value suitable for
+ * most applications is 0.5.
+ *
+ * The algorithm allows the application to define a minimum and maximum valid
+ * magnetic field magnitude.  The algorithm will ignore magnetic measurements
+ * that fall outside of this range.  This allows the algorithm to reject
+ * magnetic measurements that do not represent the direction of magnetic North.
+ * The typical magnitude of the Earth's magnetic field is between 20 uT and
+ * 70 uT.
+ *
+ * The algorithm can be used without a magnetometer.  Measurements of
+ * orientation obtained using only gyroscope and accelerometer measurements
+ * can be expected to drift in the yaw component of orientation only.  The
+ * application can reset the drift in yaw by setting the yaw to a specified
+ * angle at any time.
+ *
+ * The algorithm provides the measurement of orientation as a quaternion.  The
+ * library includes functions for converting this quaternion to a rotation
+ * matrix and Euler angles.
+ *
+ * The algorithm also provides a measurement of linear acceleration and Earth
+ * acceleration.  Linear acceleration is equal to the accelerometer  measurement
+ * with the 1 g of gravity removed.  Earth acceleration is a measurement of
+ * linear acceleration in the Earth coordinate frame.
+ */
+
+#ifndef FUSION_AHRS_H
+#define FUSION_AHRS_H
+
+//------------------------------------------------------------------------------
+// Includes
+
+#include "FusionTypes.h"
+#include <stdbool.h>
+
+//------------------------------------------------------------------------------
+// Definitions
+
+/**
+ * @brief AHRS algorithm structure.  Structure members are used internally and
+ * should not be used by the user application.
+ */
+typedef struct {
+    float gain;
+    float minimumMagneticFieldSquared;
+    float maximumMagneticFieldSquared;
+    FusionQuaternion quaternion; // describes the Earth relative to the sensor
+    FusionVector3 linearAcceleration;
+    float rampedGain;
+    bool zeroYawPending;
+} FusionAhrs;
+
+//------------------------------------------------------------------------------
+// Function prototypes
+
+void FusionAhrsInitialise(FusionAhrs * const fusionAhrs, const float gain);
+void FusionAhrsSetGain(FusionAhrs * const fusionAhrs, const float gain);
+void FusionAhrsSetMagneticField(FusionAhrs * const fusionAhrs, const float minimumMagneticField, const float maximumMagneticField);
+void FusionAhrsUpdate(FusionAhrs * const fusionAhrs, const FusionVector3 gyroscope, const FusionVector3 accelerometer, const FusionVector3 magnetometer, const float samplePeriod);
+void FusionAhrsUpdateWithoutMagnetometer(FusionAhrs * const fusionAhrs, const FusionVector3 gyroscope, const FusionVector3 accelerometer, const float samplePeriod);
+FusionQuaternion FusionAhrsGetQuaternion(const FusionAhrs * const fusionAhrs);
+FusionVector3 FusionAhrsGetLinearAcceleration(const FusionAhrs * const fusionAhrs);
+FusionVector3 FusionAhrsGetEarthAcceleration(const FusionAhrs * const fusionAhrs);
+void FusionAhrsReinitialise(FusionAhrs * const fusionAhrs);
+bool FusionAhrsIsInitialising(const FusionAhrs * const fusionAhrs);
+void FusionAhrsSetYaw(FusionAhrs * const fusionAhrs, const float yaw);
+
+#endif
+
+//------------------------------------------------------------------------------
+// End of file
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Fusion/FusionBias.c	Thu Sep 30 09:39:45 2021 +0000
@@ -0,0 +1,89 @@
+/**
+ * @file FusionBias.c
+ * @author Seb Madgwick
+ * @brief The gyroscope bias correction algorithm achieves run-time calibration
+ * of the gyroscope bias.  The algorithm will detect when the gyroscope is
+ * stationary for a set period of time and then begin to sample gyroscope
+ * measurements to calculate the bias as an average.
+ */
+
+//------------------------------------------------------------------------------
+// Includes
+
+#include "FusionBias.h"
+#include "math.h" // fabs
+
+//------------------------------------------------------------------------------
+// Definitions
+
+/**
+ * @brief Minimum stationary period (in seconds) after which the the algorithm
+ * becomes active and begins sampling gyroscope measurements.
+ */
+#define STATIONARY_PERIOD (5.0f)   // 5.0
+
+/**
+ * @brief Corner frequency (in Hz) of the high-pass filter used to sample the
+ * gyroscope bias.
+ */
+#define CORNER_FREQUENCY (0.02f)  //0.02
+
+//------------------------------------------------------------------------------
+// Functions
+
+/**
+ * @brief Initialises the gyroscope bias correction algorithm.
+ * @param fusionBias FusionBias structure.
+ * @param threshold Gyroscope threshold (in degrees per second) below which the
+ * gyroscope is detected stationary.
+ * @param samplePeriod Nominal sample period (in seconds) corresponding the rate
+ * at which the application will update the algorithm.
+ */
+void FusionBiasInitialise(FusionBias * const fusionBias, const float threshold, const float samplePeriod) {
+    fusionBias->threshold = threshold;
+    fusionBias->samplePeriod = samplePeriod;
+    fusionBias->filterCoefficient = (2.0f * M_PI * CORNER_FREQUENCY) * fusionBias->samplePeriod;
+    fusionBias->stationaryTimer = 0.0f;
+    fusionBias->gyroscopeBias = FUSION_VECTOR3_ZERO;
+}
+
+/**
+ * @brief Updates the gyroscope bias correction algorithm and returns the
+ * corrected gyroscope measurement.
+ * @param fusionBias FusionBias structure.
+ * @param gyroscope Gyroscope measurement in degrees per second.
+ * @return Corrected gyroscope measurement in degrees per second.
+ */
+FusionVector3 FusionBiasUpdate(FusionBias * const fusionBias, FusionVector3 gyroscope) {
+
+    // Subtract bias from gyroscope measurement
+    gyroscope = FusionVectorSubtract(gyroscope, fusionBias->gyroscopeBias);
+
+    // Reset stationary timer if gyroscope not stationary
+    if ((fabs(gyroscope.axis.x) > fusionBias->threshold) || (fabs(gyroscope.axis.y) > fusionBias->threshold) || (fabs(gyroscope.axis.z) > fusionBias->threshold)) {
+        fusionBias->stationaryTimer = 0.0f;
+        return gyroscope;
+    }
+
+    // Increment stationary timer while gyroscope stationary
+    if (fusionBias->stationaryTimer < STATIONARY_PERIOD) {
+        fusionBias->stationaryTimer += fusionBias->samplePeriod;
+        return gyroscope;
+    }
+
+    // Adjust bias if stationary timer has elapsed
+    fusionBias->gyroscopeBias = FusionVectorAdd(fusionBias->gyroscopeBias, FusionVectorMultiplyScalar(gyroscope, fusionBias->filterCoefficient));
+    return gyroscope;
+}
+
+/**
+ * @brief Returns true if the gyroscope bias correction algorithm is active.
+ * @param fusionBias FusionBias structure.
+ * @return True if the gyroscope bias correction algorithm is active.
+ */
+bool FusionBiasIsActive(FusionBias * const fusionBias) {
+    return fusionBias->stationaryTimer >= STATIONARY_PERIOD;
+}
+
+//------------------------------------------------------------------------------
+// End of file
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Fusion/FusionBias.h	Thu Sep 30 09:39:45 2021 +0000
@@ -0,0 +1,44 @@
+/**
+ * @file FusionBias.h
+ * @author Seb Madgwick
+ * @brief The gyroscope bias correction algorithm achieves run-time calibration
+ * of the gyroscope bias.  The algorithm will detect when the gyroscope is
+ * stationary for a set period of time and then begin to sample gyroscope
+ * measurements to calculate the bias as an average.
+ */
+
+#ifndef FUSION_BIAS_H
+#define FUSION_BIAS_H
+
+//------------------------------------------------------------------------------
+// Includes
+
+#include "FusionTypes.h"
+#include <stdbool.h>
+
+//------------------------------------------------------------------------------
+// Definitions
+
+/**
+ * @brief Gyroscope bias correction algorithm structure.  Structure members are
+ * used internally and should not be used by the user application.
+ */
+typedef struct {
+    float threshold;
+    float samplePeriod;
+    float filterCoefficient;
+    float stationaryTimer;
+    FusionVector3 gyroscopeBias;
+} FusionBias;
+
+//------------------------------------------------------------------------------
+// Function prototypes
+
+void FusionBiasInitialise(FusionBias * const fusionBias, const float threshold, const float samplePeriod);
+FusionVector3 FusionBiasUpdate(FusionBias * const fusionBias, FusionVector3 gyroscope);
+bool FusionBiasIsActive(FusionBias * const fusionBias);
+
+#endif
+
+//------------------------------------------------------------------------------
+// End of file
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Fusion/FusionCalibration.h	Thu Sep 30 09:39:45 2021 +0000
@@ -0,0 +1,49 @@
+/**
+ * @file FusionCalibration.h
+ * @author Seb Madgwick
+ * @brief Gyroscope, accelerometer, and magnetometer calibration model.
+ *
+ * Static inline implementations are used to optimise for increased execution
+ * speed.
+ */
+
+#ifndef FUSION_CALIBRATION_H
+#define FUSION_CALIBRATION_H
+
+//------------------------------------------------------------------------------
+// Includes
+
+#include "FusionTypes.h"
+
+//------------------------------------------------------------------------------
+// Inline functions
+
+/**
+ * @brief Gyroscope and accelerometer calibration model.
+ * @param uncalibrated Uncalibrated gyroscope or accelerometer measurement in
+ * lsb.
+ * @param misalignment Misalignment matrix (may not be a true rotation matrix).
+ * @param sensitivity Sensitivity in g per lsb for an accelerometer and degrees
+ * per second per lsb for a gyroscope.
+ * @param bias Bias in lsb.
+ * @return Calibrated gyroscope or accelerometer measurement.
+ */
+static inline __attribute__((always_inline)) FusionVector3 FusionCalibrationInertial(const FusionVector3 uncalibrated, const FusionRotationMatrix misalignment, const FusionVector3 sensitivity, const FusionVector3 bias) {
+    return FusionRotationMatrixMultiplyVector(misalignment, FusionVectorHadamardProduct(FusionVectorSubtract(uncalibrated, bias), sensitivity));
+}
+
+/**
+ * @brief Magnetometer calibration model.
+ * @param magnetometer Uncalibrated magnetometer measurement in uT.
+ * @param softIronMatrix Soft-iron matrix (may not be a true rotation matrix).
+ * @param hardIronBias Hard-iron bias in uT.
+ * @return Calibrated magnetometer measurement.
+ */
+static inline __attribute__((always_inline)) FusionVector3 FusionCalibrationMagnetic(const FusionVector3 uncalibrated, const FusionRotationMatrix softIronMatrix, const FusionVector3 hardIronBias) {
+    return FusionVectorSubtract(FusionRotationMatrixMultiplyVector(softIronMatrix, uncalibrated), hardIronBias);
+}
+
+#endif
+
+//------------------------------------------------------------------------------
+// End of file
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Fusion/FusionTypes.h	Thu Sep 30 09:39:45 2021 +0000
@@ -0,0 +1,473 @@
+/**
+ * @file FusionTypes.h
+ * @author Seb Madgwick
+ * @brief Common types and their associated operations.
+ *
+ * Static inline implementations are used to optimise for increased execution
+ * speed.
+ */
+
+#ifndef FUSION_TYPES_H
+#define FUSION_TYPES_H
+
+//------------------------------------------------------------------------------
+// Includes
+
+#include <math.h> // M_PI, sqrtf, atan2f, asinf
+#include <stdint.h> // int32_t
+
+//------------------------------------------------------------------------------
+// Definitions
+
+/**
+ * @brief Three-dimensional spacial vector.
+ */
+typedef union {
+    float array[3];
+
+    struct {
+        float x;
+        float y;
+        float z;
+    } axis;
+} FusionVector3;
+
+/**
+ * @brief Quaternion.  This library uses the conversion of placing the 'w'
+ * element as the first element.  Other implementations may place the 'w'
+ * element as the last element.
+ */
+typedef union {
+    float array[4];
+
+    struct {
+        float w;
+        float x;
+        float y;
+        float z;
+    } element;
+} FusionQuaternion;
+
+/**
+ * @brief Rotation matrix in row-major order.
+ * See http://en.wikipedia.org/wiki/Row-major_order
+ */
+typedef union {
+    float array[9];
+
+    struct {
+        float xx;
+        float xy;
+        float xz;
+        float yx;
+        float yy;
+        float yz;
+        float zx;
+        float zy;
+        float zz;
+    } element;
+} FusionRotationMatrix;
+
+/**
+ * @brief Euler angles union.  The Euler angles are in the Aerospace sequence
+ * also known as the ZYX sequence.
+ */
+typedef union {
+    float array[3];
+
+    struct {
+        float roll;
+        float pitch;
+        float yaw;
+    } angle;
+} FusionEulerAngles;
+
+/**
+ * @brief Zero-length vector definition.
+ */
+#define FUSION_VECTOR3_ZERO ((FusionVector3){ .array = {0.0f, 0.0f, 0.0f} })
+
+/**
+ * @brief Quaternion identity definition to represent an aligned of
+ * orientation.
+ */
+#define FUSION_QUATERNION_IDENTITY ((FusionQuaternion){ .array = {1.0f, 0.0f, 0.0f, 0.0f} })
+
+/**
+ * @brief Rotation matrix identity definition to represent an aligned of
+ * orientation.
+ */
+#define FUSION_ROTATION_MATRIX_IDENTITY ((FusionRotationMatrix){ .array = {1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f} })
+
+/**
+ * @brief Euler angles zero definition to represent an aligned of orientation.
+ */
+#define FUSION_EULER_ANGLES_ZERO ((FusionEulerAngles){ .array = {0.0f, 0.0f, 0.0f} })
+
+/**
+ * @brief Definition of M_PI.  Some compilers may not define this in math.h.
+ */
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+//------------------------------------------------------------------------------
+// Inline functions - Degrees and radians conversion
+
+/**
+ * @brief Converts degrees to radians.
+ * @param degrees Degrees.
+ * @return Radians.
+ */
+static inline __attribute__((always_inline)) float FusionDegreesToRadians(const float degrees) {
+    return degrees * ((float) M_PI / 180.0f);
+}
+
+/**
+ * @brief Converts radians to degrees.
+ * @param radians Radians.
+ * @return Degrees.
+ */
+static inline __attribute__((always_inline)) float FusionRadiansToDegrees(const float radians) {
+    return radians * (180.0f / (float) M_PI);
+}
+
+//------------------------------------------------------------------------------
+// Inline functions - Fast inverse square root
+
+/**
+ * @brief Calculates the reciprocal of the square root.
+ * See http://en.wikipedia.org/wiki/Fast_inverse_square_root
+ * @param x Operand.
+ * @return Reciprocal of the square root of x.
+ */
+static inline __attribute__((always_inline)) float FusionFastInverseSqrt(const float x) {
+    float halfx = 0.5f * x;
+    float y = x;
+    int32_t i = *(int32_t*) & y;
+    i = 0x5f3759df - (i >> 1);
+    y = *(float*) &i;
+    y = y * (1.5f - (halfx * y * y));
+    return y;
+}
+
+//------------------------------------------------------------------------------
+// Inline functions - Vector operations
+
+/**
+ * @brief Adds two vectors.
+ * @param vectorA First vector of the operation.
+ * @param vectorB Second vector of the operation.
+ * @return Sum of vectorA and vectorB.
+ */
+static inline __attribute__((always_inline)) FusionVector3 FusionVectorAdd(const FusionVector3 vectorA, const FusionVector3 vectorB) {
+    FusionVector3 result;
+    result.axis.x = vectorA.axis.x + vectorB.axis.x;
+    result.axis.y = vectorA.axis.y + vectorB.axis.y;
+    result.axis.z = vectorA.axis.z + vectorB.axis.z;
+    return result;
+}
+
+/**
+ * @brief Subtracts two vectors.
+ * @param vectorA First vector of the operation.
+ * @param vectorB Second vector of the operation.
+ * @return vectorB subtracted from vectorA.
+ */
+static inline __attribute__((always_inline)) FusionVector3 FusionVectorSubtract(const FusionVector3 vectorA, const FusionVector3 vectorB) {
+    FusionVector3 result;
+    result.axis.x = vectorA.axis.x - vectorB.axis.x;
+    result.axis.y = vectorA.axis.y - vectorB.axis.y;
+    result.axis.z = vectorA.axis.z - vectorB.axis.z;
+    return result;
+}
+
+/**
+ * @brief Multiplies vector by a scalar.
+ * @param vector Vector to be multiplied.
+ * @param scalar Scalar to be multiplied.
+ * @return Vector multiplied by scalar.
+ */
+static inline __attribute__((always_inline)) FusionVector3 FusionVectorMultiplyScalar(const FusionVector3 vector, const float scalar) {
+    FusionVector3 result;
+    result.axis.x = vector.axis.x * scalar;
+    result.axis.y = vector.axis.y * scalar;
+    result.axis.z = vector.axis.z * scalar;
+    return result;
+}
+
+/**
+ * @brief Calculates the Hadamard product (element-wise multiplication) of two
+ * vectors.
+ * @param vectorA First vector of the operation.
+ * @param vectorB Second vector of the operation.
+ * @return Hadamard product of vectorA and vectorB.
+ */
+static inline __attribute__((always_inline)) FusionVector3 FusionVectorHadamardProduct(const FusionVector3 vectorA, const FusionVector3 vectorB) {
+    FusionVector3 result;
+    result.axis.x = vectorA.axis.x * vectorB.axis.x;
+    result.axis.y = vectorA.axis.y * vectorB.axis.y;
+    result.axis.z = vectorA.axis.z * vectorB.axis.z;
+    return result;
+}
+
+/**
+ * @brief Calculates the cross-product of two vectors.
+ * @param vectorA First vector of the operation.
+ * @param vectorB Second vector of the operation.
+ * @return Cross-product of vectorA and vectorB.
+ */
+static inline __attribute__((always_inline)) FusionVector3 FusionVectorCrossProduct(const FusionVector3 vectorA, const FusionVector3 vectorB) {
+#define A vectorA.axis // define shorthand labels for more readable code
+#define B vectorB.axis
+    FusionVector3 result;
+    result.axis.x = A.y * B.z - A.z * B.y;
+    result.axis.y = A.z * B.x - A.x * B.z;
+    result.axis.z = A.x * B.y - A.y * B.x;
+    return result;
+#undef A // undefine shorthand labels
+#undef B
+}
+
+/**
+ * @brief Calculates the vector magnitude squared.
+ * @param vector Vector of the operation.
+ * @return Vector magnitude squared.
+ */
+static inline __attribute__((always_inline)) float FusionVectorMagnitudeSquared(const FusionVector3 vector) {
+#define V vector.axis // define shorthand label for more readable code
+    return V.x * V.x + V.y * V.y + V.z * V.z;
+#undef V // undefine shorthand label
+}
+
+/**
+ * @brief Calculates the magnitude of a vector.
+ * @param vector Vector to be used in calculation.
+ * @return Vector magnitude.
+ */
+static inline __attribute__((always_inline)) float FusionVectorMagnitude(const FusionVector3 vector) {
+    return sqrtf(FusionVectorMagnitudeSquared(vector));
+}
+
+/**
+ * @brief Normalises a vector to be of unit magnitude.
+ * @param vector Vector to be normalised.
+ * @return Normalised vector.
+ */
+static inline __attribute__((always_inline)) FusionVector3 FusionVectorNormalise(const FusionVector3 vector) {
+    const float magnitudeReciprocal = 1.0f / sqrtf(FusionVectorMagnitudeSquared(vector));
+    return FusionVectorMultiplyScalar(vector, magnitudeReciprocal);
+}
+
+/**
+ * @brief Normalises a vector to be of unit magnitude using the fast inverse
+ * square root approximation.
+ * @param vector Vector to be normalised.
+ * @return Normalised vector.
+ */
+static inline __attribute__((always_inline)) FusionVector3 FusionVectorFastNormalise(const FusionVector3 vector) {
+    const float magnitudeReciprocal = FusionFastInverseSqrt(FusionVectorMagnitudeSquared(vector));
+    return FusionVectorMultiplyScalar(vector, magnitudeReciprocal);
+}
+
+//------------------------------------------------------------------------------
+// Inline functions - Quaternion operations
+
+/**
+ * @brief Adds two quaternions.
+ * @param quaternionA First quaternion of the operation.
+ * @param quaternionB Second quaternion of the operation.
+ * @return Sum of quaternionA and quaternionB.
+ */
+static inline __attribute__((always_inline)) FusionQuaternion FusionQuaternionAdd(const FusionQuaternion quaternionA, const FusionQuaternion quaternionB) {
+    FusionQuaternion result;
+    result.element.w = quaternionA.element.w + quaternionB.element.w;
+    result.element.x = quaternionA.element.x + quaternionB.element.x;
+    result.element.y = quaternionA.element.y + quaternionB.element.y;
+    result.element.z = quaternionA.element.z + quaternionB.element.z;
+    return result;
+}
+
+/**
+ * @brief Multiplies two quaternions.
+ * @param quaternionA First quaternion of the operation.
+ * @param quaternionB Second quaternion of the operation.
+ * @return quaternionA multiplied by quaternionB.
+ */
+static inline __attribute__((always_inline)) FusionQuaternion FusionQuaternionMultiply(const FusionQuaternion quaternionA, const FusionQuaternion quaternionB) {
+#define A quaternionA.element // define shorthand labels for more readable code
+#define B quaternionB.element
+    FusionQuaternion result;
+    result.element.w = A.w * B.w - A.x * B.x - A.y * B.y - A.z * B.z;
+    result.element.x = A.w * B.x + A.x * B.w + A.y * B.z - A.z * B.y;
+    result.element.y = A.w * B.y - A.x * B.z + A.y * B.w + A.z * B.x;
+    result.element.z = A.w * B.z + A.x * B.y - A.y * B.x + A.z * B.w;
+    return result;
+#undef A // undefine shorthand labels
+#undef B
+}
+
+/**
+ * @brief Multiplies quaternion by a vector.  This is a normal quaternion
+ * multiplication where the vector is treated a quaternion with a 'w' element
+ * value of 0.  The quaternion is post multiplied by the vector.
+ * @param quaternion Quaternion to be multiplied.
+ * @param vector Vector to be multiplied.
+ * @return Quaternion multiplied by vector.
+ */
+static inline __attribute__((always_inline)) FusionQuaternion FusionQuaternionMultiplyVector(const FusionQuaternion quaternion, const FusionVector3 vector) {
+#define Q quaternion.element // define shorthand labels for more readable code
+#define V vector.axis
+    FusionQuaternion result;
+    result.element.w = -Q.x * V.x - Q.y * V.y - Q.z * V.z;
+    result.element.x = Q.w * V.x + Q.y * V.z - Q.z * V.y;
+    result.element.y = Q.w * V.y - Q.x * V.z + Q.z * V.x;
+    result.element.z = Q.w * V.z + Q.x * V.y - Q.y * V.x;
+    return result;
+#undef Q // undefine shorthand labels
+#undef V
+}
+
+/**
+ * @brief Returns the quaternion conjugate.
+ * @param quaternion Quaternion to be conjugated.
+ * @return Conjugated quaternion.
+ */
+static inline __attribute__((always_inline)) FusionQuaternion FusionQuaternionConjugate(const FusionQuaternion quaternion) {
+    FusionQuaternion conjugate;
+    conjugate.element.w = quaternion.element.w;
+    conjugate.element.x = -1.0f * quaternion.element.x;
+    conjugate.element.y = -1.0f * quaternion.element.y;
+    conjugate.element.z = -1.0f * quaternion.element.z;
+    return conjugate;
+}
+
+/**
+ * @brief Normalises a quaternion to be of unit magnitude.
+ * @param quaternion Quaternion to be normalised.
+ * @return Normalised quaternion.
+ */
+static inline __attribute__((always_inline)) FusionQuaternion FusionQuaternionNormalise(const FusionQuaternion quaternion) {
+#define Q quaternion.element // define shorthand label for more readable code
+    const float magnitudeReciprocal = 1.0f / sqrtf(Q.w * Q.w + Q.x * Q.x + Q.y * Q.y + Q.z * Q.z);
+    FusionQuaternion normalisedQuaternion;
+    normalisedQuaternion.element.w = Q.w * magnitudeReciprocal;
+    normalisedQuaternion.element.x = Q.x * magnitudeReciprocal;
+    normalisedQuaternion.element.y = Q.y * magnitudeReciprocal;
+    normalisedQuaternion.element.z = Q.z * magnitudeReciprocal;
+    return normalisedQuaternion;
+#undef Q // undefine shorthand label
+}
+
+/**
+ * @brief Normalises a quaternion to be of unit magnitude using the fast inverse
+ * square root approximation.
+ * @param quaternion Quaternion to be normalised.
+ * @return Normalised quaternion.
+ */
+static inline __attribute__((always_inline)) FusionQuaternion FusionQuaternionFastNormalise(const FusionQuaternion quaternion) {
+#define Q quaternion.element // define shorthand label for more readable code
+    const float magnitudeReciprocal = FusionFastInverseSqrt(Q.w * Q.w + Q.x * Q.x + Q.y * Q.y + Q.z * Q.z);
+    FusionQuaternion normalisedQuaternion;
+    normalisedQuaternion.element.w = Q.w * magnitudeReciprocal;
+    normalisedQuaternion.element.x = Q.x * magnitudeReciprocal;
+    normalisedQuaternion.element.y = Q.y * magnitudeReciprocal;
+    normalisedQuaternion.element.z = Q.z * magnitudeReciprocal;
+    return normalisedQuaternion;
+#undef Q // undefine shorthand label
+}
+
+//------------------------------------------------------------------------------
+// Inline functions - Rotation matrix operations
+
+/**
+ * @brief Multiplies two rotation matrices.
+ * @param rotationMatrixA First rotation matrix of the operation.
+ * @param rotationMatrixB Second rotation matrix of the operation.
+ * @return rotationMatrixA with rotationMatrixB.
+ */
+static inline __attribute__((always_inline)) FusionRotationMatrix FusionRotationMatrixMultiply(const FusionRotationMatrix rotationMatrixA, const FusionRotationMatrix rotationMatrixB) {
+#define A rotationMatrixA.element // define shorthand label for more readable code
+#define B rotationMatrixB.element
+    FusionRotationMatrix result;
+    result.element.xx = A.xx * B.xx + A.xy * B.yx + A.xz * B.zx;
+    result.element.xy = A.xx * B.xy + A.xy * B.yy + A.xz * B.zy;
+    result.element.xz = A.xx * B.xz + A.xy * B.yz + A.xz * B.zz;
+    result.element.yx = A.yx * B.xx + A.yy * B.yx + A.yz * B.zx;
+    result.element.yy = A.yx * B.xy + A.yy * B.yy + A.yz * B.zy;
+    result.element.yz = A.yx * B.xz + A.yy * B.yz + A.yz * B.zz;
+    result.element.zx = A.zx * B.xx + A.zy * B.yx + A.zz * B.zx;
+    result.element.zy = A.zx * B.xy + A.zy * B.yy + A.zz * B.zy;
+    result.element.zz = A.zx * B.xz + A.zy * B.yz + A.zz * B.zz;
+    return result;
+#undef A // undefine shorthand label
+#undef B
+}
+
+/**
+ * @brief Multiplies rotation matrix with scalar.
+ * @param rotationMatrix Rotation matrix to be multiplied.
+ * @param vector Vector to be multiplied.
+ * @return Rotation matrix multiplied with scalar.
+ */
+static inline __attribute__((always_inline)) FusionVector3 FusionRotationMatrixMultiplyVector(const FusionRotationMatrix rotationMatrix, const FusionVector3 vector) {
+#define R rotationMatrix.element // define shorthand label for more readable code
+    FusionVector3 result;
+    result.axis.x = R.xx * vector.axis.x + R.xy * vector.axis.y + R.xz * vector.axis.z;
+    result.axis.y = R.yx * vector.axis.x + R.yy * vector.axis.y + R.yz * vector.axis.z;
+    result.axis.z = R.zx * vector.axis.x + R.zy * vector.axis.y + R.zz * vector.axis.z;
+    return result;
+#undef R // undefine shorthand label
+}
+
+//------------------------------------------------------------------------------
+// Inline functions - Conversion operations
+
+/**
+ * @brief Converts a quaternion to a rotation matrix.
+ * @param quaternion Quaternion to be converted.
+ * @return Rotation matrix.
+ */
+static inline __attribute__((always_inline)) FusionRotationMatrix FusionQuaternionToRotationMatrix(const FusionQuaternion quaternion) {
+#define Q quaternion.element // define shorthand label for more readable code
+    const float qwqw = Q.w * Q.w; // calculate common terms to avoid repeated operations
+    const float qwqx = Q.w * Q.x;
+    const float qwqy = Q.w * Q.y;
+    const float qwqz = Q.w * Q.z;
+    const float qxqy = Q.x * Q.y;
+    const float qxqz = Q.x * Q.z;
+    const float qyqz = Q.y * Q.z;
+    FusionRotationMatrix rotationMatrix;
+    rotationMatrix.element.xx = 2.0f * (qwqw - 0.5f + Q.x * Q.x);
+    rotationMatrix.element.xy = 2.0f * (qxqy + qwqz);
+    rotationMatrix.element.xz = 2.0f * (qxqz - qwqy);
+    rotationMatrix.element.yx = 2.0f * (qxqy - qwqz);
+    rotationMatrix.element.yy = 2.0f * (qwqw - 0.5f + Q.y * Q.y);
+    rotationMatrix.element.yz = 2.0f * (qyqz + qwqx);
+    rotationMatrix.element.zx = 2.0f * (qxqz + qwqy);
+    rotationMatrix.element.zy = 2.0f * (qyqz - qwqx);
+    rotationMatrix.element.zz = 2.0f * (qwqw - 0.5f + Q.z * Q.z);
+    return rotationMatrix;
+#undef Q // undefine shorthand label
+}
+
+/**
+ * @brief Converts a quaternion to Euler angles in degrees.
+ * @param quaternion Quaternion to be converted.
+ * @return Euler angles in degrees.
+ */
+static inline __attribute__((always_inline)) FusionEulerAngles FusionQuaternionToEulerAngles(const FusionQuaternion quaternion) {
+#define Q quaternion.element // define shorthand label for more readable code
+    const float qwqwMinusHalf = Q.w * Q.w - 0.5f; // calculate common terms to avoid repeated operations
+    FusionEulerAngles eulerAngles;
+    eulerAngles.angle.roll = FusionRadiansToDegrees(atan2f(Q.y * Q.z - Q.w * Q.x, qwqwMinusHalf + Q.z * Q.z));
+    eulerAngles.angle.pitch = FusionRadiansToDegrees(-1.0f * asinf(2.0f * (Q.x * Q.z + Q.w * Q.y)));
+    eulerAngles.angle.yaw = FusionRadiansToDegrees(atan2f(Q.x * Q.y - Q.w * Q.z, qwqwMinusHalf + Q.x * Q.x));
+    return eulerAngles;
+#undef Q // undefine shorthand label
+}
+
+#endif
+
+//------------------------------------------------------------------------------
+// End of file
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Fusion3.cpp	Thu Sep 30 09:39:45 2021 +0000
@@ -0,0 +1,186 @@
+//************************************
+// test fusion
+// ***********************************
+//
+#include "Fusion.h"
+#include "LSM6DS33_GR1.h"
+//
+float samplePeriod = 0.01f; // replace this value with actual sample period in seconds/
+// tracteur
+FusionBias TBias;
+FusionAhrs TAhrs;
+FusionVector3 gTSensitivity = {
+    .axis.x = 1.0f,
+    .axis.y = 1.0f,
+    .axis.z = 1.0f,
+}; // replace these values with actual sensitivity in degrees per second per lsb as specified in gyroscope datasheet
+FusionVector3 aTSensitivity = {
+    .axis.x = 1.0f,
+    .axis.y = 1.0f,
+    .axis.z = 1.0f,
+}; // replace these values with actual sensitivity in g per lsb as specified in accelerometer datasheet
+//
+// outil
+FusionBias OBias;
+FusionAhrs OAhrs;
+FusionVector3 gOSensitivity = {
+    .axis.x = 1.0f,
+    .axis.y = 1.0f,
+    .axis.z = 1.0f,
+}; // replace these values with actual sensitivity in degrees per second per lsb as specified in gyroscope datasheet
+FusionVector3 aOSensitivity = {
+    .axis.x = 1.0f,
+    .axis.y = 1.0f,
+    .axis.z = 1.0f,
+}; // replace these values with actual sensitivity in g per lsb as specified in accelerometer datasheet
+
+
+
+int flag_fusion=0;
+Serial pc(SERIAL_TX, SERIAL_RX);  // I/O terminal PC
+DigitalOut led(LED1);  // led
+LSM6DS33 AGT(PB_9, PB_8);  // accelero gyro tracteur
+
+
+
+float gg[3];
+float aRes,gRes;
+Ticker affich;
+//
+
+//
+signed long round(double value)
+{
+    return((long)floor(value+0.5));
+}
+
+//
+void aff(void)
+{
+    flag_fusion=1;
+    // tracteur
+    // Calibrate gyroscope
+
+}
+//
+int main()
+{
+    wait(1);
+    pc.baud(115200);
+    pc.printf("Hello Fusion \r\n");
+    //aRes = 2.0 / 32768.0;
+    //gRes = 250.0 / 32768.0;
+    //pi=4*atan(1.0);
+    //init IMU
+    AGT.begin(LSM6DS33::G_SCALE_500DPS,LSM6DS33::A_SCALE_8G,LSM6DS33::G_ODR_104,LSM6DS33::A_ODR_104);
+    //wait(0.5);
+    AGT.calibration(1000);
+    //
+    char rr= AGT.readReg(0x15);
+    pc.printf(" reg 0x15 : %x \r\n",rr);
+    rr= AGT.readReg(0x10);
+    pc.printf(" reg 0x10 : %x \r\n",rr);
+    AGT.writeReg(0x10,rr|0x03);  // 50HZ
+    rr= AGT.readReg(0x10);
+    pc.printf(" reg 0x10 : %x \r\n",rr);
+    rr= AGT.readReg(0x15);
+    pc.printf(" reg 0x15 : %x \r\n",rr);
+    rr= AGT.readReg(0x11);
+    pc.printf(" reg 0x11 : %x \r\n",rr);
+    
+AGT.writeReg(0x13,0x80);  // BW SCAL
+rr= AGT.readReg(0x13);
+    pc.printf(" reg 0x13 : %x \r\n",rr);
+    //
+    // Initialise gyroscope bias correction algorithm
+    FusionBiasInitialise(&TBias, 20.0f, samplePeriod); // stationary threshold = 0.5 degrees per second
+    FusionBiasInitialise(&OBias, 20.0f, samplePeriod); // stationary threshold = 0.5 degrees per second
+
+    // Initialise AHRS algorithm
+    FusionAhrsInitialise(&TAhrs, 0.5f); // gain = 0.5
+    FusionAhrsInitialise(&OAhrs, 0.5f); // gain = 0.5
+
+    // The contents of this do while loop should be called for each time new sensor measurements are available
+    //
+    int cpt=0;
+    affich.attach(&aff,0.01);
+    while(1) {
+        // lecture accelero / gyro
+
+        if(flag_fusion==1) {
+            cpt++;
+            AGT.readAccel();
+            AGT.readGyro();
+            AGT.readTemp();
+            FusionVector3 uncalTGyroscope = {
+                .axis.x = AGT.gx-AGT.gx_off, /* replace this value with actual gyroscope x axis measurement in lsb */
+                .axis.y = AGT.gy-AGT.gy_off, /* replace this value with actual gyroscope y axis measurement in lsb */
+                .axis.z = AGT.gz-AGT.gz_off, /* replace this value with actual gyroscope z axis measurement in lsb */
+                //.axis.z = 0, /* replace this value with actual gyroscope z axis measurement in lsb */
+            };
+            FusionVector3 calTGyroscope = FusionCalibrationInertial(uncalTGyroscope, FUSION_ROTATION_MATRIX_IDENTITY, gTSensitivity, FUSION_VECTOR3_ZERO);
+            // Calibrate accelerometer
+            FusionVector3 uncalTAccelerometer = {
+                .axis.x = AGT.ax, /* replace this value with actual accelerometer x axis measurement in lsb */
+                .axis.y = AGT.ay, /* replace this value with actual accelerometer y axis measurement in lsb */
+                .axis.z = AGT.az, /* replace this value with actual accelerometer z axis measurement in lsb */
+            };
+            FusionVector3 calTAccelerometer = FusionCalibrationInertial(uncalTAccelerometer, FUSION_ROTATION_MATRIX_IDENTITY, aTSensitivity, FUSION_VECTOR3_ZERO);
+            // Update gyroscope bias correction algorithm
+            calTGyroscope = FusionBiasUpdate(&TBias, calTGyroscope);
+            led= FusionBiasIsActive(&TBias);
+            // Update AHRS algorithm
+            FusionAhrsUpdateWithoutMagnetometer(&TAhrs, calTGyroscope, calTAccelerometer, samplePeriod);
+//
+            // Outil
+            // Calibrate gyroscope
+            FusionVector3 uncalOGyroscope = {
+                .axis.x = -(AGT.gz-AGT.gz_off), /* replace this value with actual gyroscope x axis measurement in lsb */
+                .axis.y = -(AGT.gy-AGT.gy_off), /* replace this value with actual gyroscope y axis measurement in lsb */
+                .axis.z = -(AGT.gx-AGT.gx_off), /* replace this value with actual gyroscope z axis measurement in lsb */
+            };
+            FusionVector3 calOGyroscope = FusionCalibrationInertial(uncalOGyroscope, FUSION_ROTATION_MATRIX_IDENTITY, gOSensitivity, FUSION_VECTOR3_ZERO);
+            // Calibrate accelerometer
+            FusionVector3 uncalOAccelerometer = {
+                .axis.x = -AGT.az, /* replace this value with actual accelerometer x axis measurement in lsb */
+                .axis.y = -AGT.ay, /* replace this value with actual accelerometer y axis measurement in lsb */
+                .axis.z = -AGT.ax, /* replace this value with actual accelerometer z axis measurement in lsb */
+            };
+            FusionVector3 calOAccelerometer = FusionCalibrationInertial(uncalOAccelerometer, FUSION_ROTATION_MATRIX_IDENTITY, aOSensitivity, FUSION_VECTOR3_ZERO);
+            // Update gyroscope bias correction algorithm
+            calOGyroscope = FusionBiasUpdate(&OBias, calOGyroscope);
+            // Update AHRS algorithm
+            FusionAhrsUpdateWithoutMagnetometer(&OAhrs, calOGyroscope, calOAccelerometer, samplePeriod);
+FusionEulerAngles eulerTAngles = FusionQuaternionToEulerAngles(FusionAhrsGetQuaternion(&TAhrs));
+
+        FusionEulerAngles eulerOAngles = FusionQuaternionToEulerAngles(FusionAhrsGetQuaternion(&OAhrs));
+            flag_fusion=0;
+            if(cpt%1==0){
+                pc.printf("$%f %f;", eulerTAngles.angle.roll, eulerTAngles.angle.pitch);
+                 //pc.printf("$%f %f %f %f;", uncalTGyroscope.axis.x,calTGyroscope.axis.x,uncalTGyroscope.axis.y,calTGyroscope.axis.y);
+                
+                }
+        }
+        // Print Euler angles
+        
+        //printf("Roll = %0.1f, Pitch = %0.1f, Yaw = %0.1f\r\n", eulerAngles.angle.roll, eulerAngles.angle.pitch, eulerAngles.angle.yaw);
+        // pc.printf("$%f %f %f;", eulerAngles.angle.roll, eulerAngles.angle.pitch, eulerAngles.angle.yaw);
+        // pc.printf("$%f %f %f %f;", eulerAngles.angle.roll, eulerAngles.angle.pitch,angle_trac,cc);
+        //pc.printf("$%f %f %f %f ;", eulerTAngles.angle.roll, eulerTAngles.angle.pitch,eulerOAngles.angle.roll, eulerOAngles.angle.pitch);
+        //////////// pc.printf("$%f %f %f;", eulerOAngles.angle.roll, eulerOAngles.angle.pitch,AGT.temperature_c);
+        ////////////pc.printf("$%f %f;", eulerTAngles.angle.roll, eulerTAngles.angle.pitch);
+        //pc.printf("$%f %f %f;", gyroT_out[0],accT_out[0],angle_trac);
+        //pc.printf("$%f %f %f;", AGT.gx-AGT.gx_off,AGT.gy-AGT.gy_off,AGT.gz-AGT.gz_off);
+        //pc.printf("$%f %f %f;", gg[0],gg[1],gg[2]);
+        //
+        //wait(0.1);
+        // pc.printf(">>> %f %f %f  \r\n",GXOT,GYOT,GZOT);
+        ////pc.printf(">>> %f %f %f  \r\n",AGT.gx_off,AGT.gy_off,AGT.gz_off);
+        // pc.printf("> %f %f %f %f %f %f \r\n",AXT,AYT,AZT,GXT,GYT,GZT);
+        ////pc.printf("< %f %f %f %f %f %f \r\n\r\n",AGT.ax,AGT.ay,AGT.az,AGT.gx-AGT.gx_off,AGT.gy,AGT.gz);
+        // pc.printf("> %f %f %f %f %f %f \r\n",AXM,AYM,AZM,GXM,GYM,GZM);
+        // pc.printf(">>> %f %f %f  \r\n",GXOM,GYOM,GZOM);
+        // allichage des differents ecrans
+
+    }
+}
\ No newline at end of file
--- a/LSM6DS33_GR1.lib	Tue Jul 06 07:20:20 2021 +0000
+++ b/LSM6DS33_GR1.lib	Thu Sep 30 09:39:45 2021 +0000
@@ -1,1 +1,1 @@
-https://os.mbed.com/users/gr66/code/LSM6DS33_GR1/#132a63741359
+https://os.mbed.com/teams/ER-S3-prime/code/LSM6DS33_GR1/#704790280bbe
--- a/TB5649.lib	Tue Jul 06 07:20:20 2021 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-https://os.mbed.com/users/gr66/code/TB5649xx/#d77dee9a8b4a
--- a/main_centrale_50.cpp	Tue Jul 06 07:20:20 2021 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,904 +0,0 @@
-//************************************
-// VITIACCESS2
-// HUBLEX
-// G. Raynaud
-// Centrale 40
-// 30 juin 2021
-// format message de 8 octets en flash
-// refonte automate
-// mise en place du mode expert
-// modif lecture IMU calcul angle central
-// ***********************************
-//
-//
-#define elec 0    // 1 verin hydraulique  0 verin electrique
-#define mat 0  // 1 : horizontal  0 vertical
-#include "LSM6DS33_GR1.h"
-#include "TB6549.h"
-
-//
-#define VERSION 50
-
-//#define ZM 2         // zone morte +/- zm
-#define INCLINAISON 45  // inclinaison du boitier
-#define TEMPOCMD 1000
-//
-Serial pc(PA_2, PA_3);  // I/O terminal PC
-DigitalOut led(PA_15);  // led
-#if elec
-DigitalOut VerinG(PF_1) ;  // Verin Gauche
-DigitalOut VerinD(PB_3);   // Verin Droit
-DigitalOut Verin(PB_5);   // Verin
-#else
-unsigned char VerinG=0,VerinD=0,Verin=0;
-Motor motor(PB_5,PA_0,PA_4,PF_1);  // commande moteur vérin  pwm in1 in2 sb
-#endif
-I2C i2c_lcd(PB_7, PB_6);   // SDA, SCL
-LSM6DS33 DOF6(PB_7, PB_6);
-Ticker automate;
-Ticker affich;
-Timer T;
-CAN can(PA_11,PA_12 );      //canRX canTX
-//
-CANMessage msg;            // message recu can
-//
-signed char butee_gauche=-21,butee_droite=21;
-signed char flash_s[8]= {0,-20,20,1,10,10,0,0}; //
-signed char zmm=2; // zone morte
-signed char f_fusion=11; // 10 log frequence de coupure angle tract
-signed char beta=11; // coef de compensation de vitesse
-char ack=0;
-
-unsigned char ecran=6;
-unsigned char ihmnbr=0;
-unsigned char ihmcmd=0;
-//double *angle_acc;
-void automat(void);
-unsigned char etat_automate;
-//tableau pour filtrage
-double coefT_acc[2],coefT_gyro[2];
-double accT_in[2],gyroT_in[2];
-double accT_out[2],gyroT_out[2];
-//
-double coefM_acc[2],coefM_gyro[2];
-double accM_in[2],gyroM_in[2];
-double accM_out[2],gyroM_out[2];
-//
-int f_aff=0;
-//correction inclinaison
-double cos_inclT,sin_inclT;
-double cos_inclM,sin_inclM;
-double pi;
-//attitude trac
-double aRes;
-double gRes ;
-double AXT,AYT,AZT,GXT,GYT,GZT;   // T pour tracteur
-double GXOT,GYOT,GZOT;
-double AXM,AYM,AZM,GXM,GYM,GZM;   // M pour mat
-double GXOM,GYOM,GZOM;
-//
-void coef_filtre_PB(double H0,double f0,double Te,double* coef)
-{
-    double PI=4*atan(1.0);
-    double tau=1/(2*PI*f0);
-    coef[0]=H0/(1+(2*tau/Te));
-    coef[1]=(Te-2*tau)/(Te+2*tau);
-}
-//
-void filtre_PB(double* xn,double* yn, double* coef)
-{
-    yn[0]=coef[0]*(xn[0]+xn[1])-coef[1]*yn[1];
-    xn[1]=xn[0];
-    yn[1]=yn[0];
-}
-//
-signed long round(double value)
-{
-    return((long)floor(value+0.5));
-}
-//
-signed char consigne=0,consigne_eff=0;
-signed char angle_trac;
-signed char angle_mat;
-double angleM,angleMCT;  // angle mat , aggle mat correction tachimetrique
-signed char angle_verin;
-
-void complement()
-{
-    double psia=0;
-    //mat
-#if mat
-    psia=atan2(AYM,AZM)*180/pi;
-    gyroM_in[0]=GXM; // mat H
-#else
-    psia=atan2(AYM,-AXM)*180/pi;
-    gyroM_in[0]=GZM; // mat V
-#endif
-    accM_in[0]=psia;
-    filtre_PB(accM_in,accM_out,coefM_acc);  // filtre accelero X
-    //angle_trac=-(char)round(accM_out[0]); ;
-    filtre_PB(gyroM_in,gyroM_out,coefM_gyro);  // filtre accelero X
-    angleM=-(gyroM_out[0]+accM_out[0]);
-    angleMCT=angleM-(gyroM_in[0]*beta*0.01);   ////BETA 0.11
-    angle_mat=(char)round(angleM);
-    //trac
-    // corection inclinaison
-    double gxx=(GZT)*sin_inclT +(GXT)*cos_inclT;
-    double gyy=GYT;
-    double gzz=(GZT)*cos_inclT -(GXT)*sin_inclT;
-    double axx=(AZT)*sin_inclT +(AXT)*cos_inclT;
-    double ayy=AYT;
-    double azz=(AZT)*cos_inclT -(AXT)*sin_inclT;
-    //
-    ////psia=atan2(AYT,AZT)*180/pi;
-    ////accT_in[0]=psia;
-    ////filtre_PB(accT_in,accT_out,coefT_acc);  // filtre accelero X
-    ////gyroT_in[0]=GXT;
-    ////filtre_PB(gyroT_in,gyroT_out,coefT_gyro);  // filtre accelero X
-    ////angle_trac=-(char)round(gyroT_out[0]+accT_out[0]);
-    //
-    psia=atan2(ayy,azz)*180/pi;
-    accT_in[0]=psia;
-    filtre_PB(accT_in,accT_out,coefT_acc);  // filtre accelero X
-    gyroT_in[0]=gxx;
-    filtre_PB(gyroT_in,gyroT_out,coefT_gyro);  // filtre accelero X
-    angle_trac=-(char)round(gyroT_out[0]+accT_out[0]);
-
-}
-
-//char tab[5]= {0x00,0xcc,0,0,0};   //
-char voyant[4]= {0x00,0x00,0x00,0x00};   //
-char affi[8]= {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06};   //
-void send(void);
-//
-void autom(void)
-{
-    complement();
-}
-void aff(void)
-{
-    send();
-    f_aff=1;
-    automat();
-}
-// fonction can en reception sur ISR
-void rxcan()
-{
-    //pc.printf("receive\n");
-    can.read(msg);
-    if(msg.id==0x22) {
-        led=1;
-        ihmnbr=msg.data[0];
-        ihmcmd=msg.data[1];
-    }
-    if(msg.id==0x48) {   // stocke les donnees transmises depuis la flash
-        butee_gauche=msg.data[1];
-        butee_droite=msg.data[2];
-        zmm=msg.data[3];
-        f_fusion=msg.data[4];
-        beta=msg.data[5];
-    }
-    if(msg.id==0x41) {   // accéléro depuis outil
-        //pc.printf(">>> can id 41 \r\n");
-        AXM=(signed short)(msg.data[1] | (msg.data[0] << 8))*aRes ;
-        AYM=(signed short)(msg.data[3] | (msg.data[2]<< 8))*aRes ;
-        AZM=(signed short)(msg.data[5] | (msg.data[4] << 8))*aRes ;
-        //pc.printf(">>>  ayt  azt axm  %f %f %f  \r\n",AYM,AZM,AXM);
-    }
-    if(msg.id==0x42) {   // gyro depuis outil
-        GXM=((signed short)(msg.data[1] | (msg.data[0] << 8))*gRes )-GXOM;
-        GYM=((signed short)(msg.data[3] | (msg.data[2]<< 8))*gRes )-GYOM;
-        GZM=((signed short)(msg.data[5] | (msg.data[4] << 8))*gRes )-GZOM;
-    }
-    if(msg.id==0x43) {   // offset depuis outil
-        //pc.printf(">>> can id 43 \r\n");
-        GXOM=(signed short)(msg.data[1] | (msg.data[0] << 8))*gRes ;
-        GYOM=(signed short)(msg.data[3] | (msg.data[2]<< 8))*gRes ;
-        GZOM=(signed short)(msg.data[5] | (msg.data[4] << 8))*gRes ;
-        //pc.printf(">>> GXOM %f GYOM %f GZOM %f \r\n",GXOM,GYOM,GZOM);
-    }
-    if(msg.id==0x49) {   // envoi depuis outil
-        ack=1;
-    }
-}
-// fonction d'envoi trame de test can
-void sendx(char msg,char* data,char size)
-{
-    //pc.printf("send()\n");
-    while(!can.write(CANMessage(msg, data, size)));
-}
-// fonction d'envoi trame de test can
-void send()
-{
-    while(!can.write(CANMessage(0x11, voyant, 4)));   // envoi de l'etat des voyants
-    while(!can.write(CANMessage(0x12, affi, 8)));   // envoi de l'etat des voyants
-}
-//
-void automat(void)
-{
-    typedef enum {IDLE=0,RAZ=1, ERR=2,
-                  D=3,DT=4, DB=5,DF=6,
-                  G=7,GT=8, GB=9,GF=10,
-                  AA=11,GA=12,DA=13,INV=14,
-                  ButGAtt=15,ButG=16,
-                  ButDAtt=17,ButD=18,
-                  ParkAtt=19,Park=20,
-                  ButRAZAtt=21,ButRAZ=22,
-                  EXPATT=23,EXP1=24,EXP0=25,
-                  P0=26,P0X=27,P0Z=28,
-                  P1=29,P1X=30,P1Z=31,
-                  P2=32,P2X=33,P2Z=34,
-                  P3=35,P3X=36,P3Z=37,
-                  P4=38,P4X=39,P4Z=40,
-                  Param=41
-                 } type_etat;
-    typedef enum {n=0x00, dm=0x01, gm=0x02, da=0x05,
-                  ga=0x06, a=0x04,bd=0x08,bg=0x10,p=0x20,in=0x24
-                 } type_cmd;
-    type_etat etat_actuel = RAZ ;  // déclaration état actuel init. à RAZ etat 3
-    static type_etat etat_futur = RAZ;  // déclaration état futur
-    type_cmd bouton=n;  // init bouton N : tout relaché
-    // gestion des entrees
-    angle_verin=angle_mat-angle_trac;
-    bouton=(type_cmd)ihmcmd; // lecture des boutons
-    // gestion du diagramme de transition
-    //
-    etat_actuel = etat_futur;   // mise à jour de l'état
-    etat_automate=(unsigned char)etat_actuel;
-    // calul de l'éat futur en fonction de l'état actuel et des entrées
-    // ihmcmd = 02:gauche,01:droite,04:auto,10 :  but.G, 20 : park, 08 : but.D
-    switch(etat_actuel) {
-        case RAZ :
-            T.reset();
-            if(bouton==a) etat_futur = ERR; // si auto activé à la mise en marche etat d'erreur
-            else {
-                etat_futur=Param;
-                T.start();
-            }
-            break;
-        case Param :
-            ecran=6;
-            if(T.read_ms()>5000) etat_futur=IDLE;
-            break;
-        case ERR :
-            if(bouton==n) etat_futur = IDLE;  // mise en attente d'une commande
-            break;
-        case IDLE :
-            if (bouton==gm) etat_futur = GT;       // gauche test
-            else if (bouton==dm) etat_futur = DT;  // droite test
-            else if(bouton==a) {
-                etat_futur=AA;
-                consigne=angle_mat;
-            } else if (ihmcmd==0x10) etat_futur=ButGAtt ;    // attente butee gauche
-            else if (ihmcmd==0x08) etat_futur=ButDAtt ;      // attente butee droite
-            else if (ihmcmd==0x18) etat_futur=ButRAZAtt ;      // attente RAZ butée
-            else if (ihmcmd==0x20) etat_futur=ParkAtt ;      // attente butee droite
-            else if (ihmcmd==0x30) etat_futur=EXPATT ;      // attente mode expert
-            break;
-        //---- gauche ----
-        case GT :
-            if(angle_verin<=butee_gauche) etat_futur=GB; // butee gauche atteinte ou dépassée
-            else etat_futur=G ;    // deplacement gauche possible
-            break;
-        case GB :
-            if(bouton==n) etat_futur=IDLE;
-            else if(bouton==a) {
-                etat_futur=AA;
-                //consigne=angle_verin-angle_trac;  ////////grgrgr
-                consigne=angle_trac+butee_gauche;  ////////grgrgr
-            } else if(T.read_ms()>5000) etat_futur=GF;
-            break;
-        case G :
-            if (bouton==n) etat_futur = IDLE;         // fin gauche
-            else if(bouton==a) {
-                etat_futur=AA;
-                consigne=angle_mat;
-            } else if (angle_verin<=butee_gauche) etat_futur=GB; // butee atteinte
-            break;
-        case GF :
-            if (bouton==n) etat_futur = IDLE;         // fin forcage gauche
-            else if(bouton==a) {
-                etat_futur=AA;
-                consigne=angle_mat;
-            }
-            break;
-        // ---- droite ----
-        case DT :
-            if(angle_verin>=butee_droite) etat_futur=DB;   // butee droite atteinte ou dépassée
-            else etat_futur=D ;  // deplacement droite possible
-            break;
-        case DB :
-            if(bouton==n) etat_futur=IDLE;
-            else if(bouton==a) {
-                etat_futur=AA;
-                //consigne=angle_verin-angle_trac;
-                consigne=angle_trac+butee_droite;
-            } else if(T.read_ms()>5000) etat_futur=DF;
-            break;
-        case D :
-            if (bouton==n) etat_futur = IDLE;         // fin droite
-            else if(bouton==a) {
-                etat_futur=AA;
-                consigne=angle_mat;
-            } else if (angle_verin>=butee_droite) etat_futur=DB; // butee atteinte
-            break;
-        case DF :
-            if (bouton==n) etat_futur = IDLE;         // fin forcage droite
-            else if(bouton==a) {
-                etat_futur=AA;
-                consigne=angle_mat;
-            }
-            break;
-        // ---- auto ----
-        case AA :
-            if (bouton==n) etat_futur=IDLE;       // retour etat d'attente
-            else if (bouton==da) etat_futur=DT;    // vers test consigne droite
-            else if (bouton==ga) etat_futur=GT;    // vers test consigne gauche
-            else if (bouton==in) {
-                etat_futur=INV;      // inversion de consigne
-                consigne=-consigne;
-            } else if  (angleMCT >= consigne_eff+zmm) etat_futur=GA;  // mouvement gauche
-            else if (angleMCT <= consigne_eff-zmm) etat_futur= DA;   //mouvement droite
-            break;
-        case GA:
-            if (bouton==n) etat_futur=IDLE;   // retour etat manuel
-            else if(bouton==da) etat_futur=DT;
-            else if (bouton==ga) etat_futur=GT;    // vers test consigne gauche
-            else if (bouton==in) {
-                etat_futur=INV;      // inversion de consigne
-                consigne=-consigne;
-            } else if (angleMCT < consigne_eff+zmm) etat_futur=AA ; // auto  //GRGR +zmm
-            break;
-        case DA :
-            if (bouton==n) etat_futur=IDLE;   // retour manuel
-            else if(bouton==da) etat_futur=DT;
-            else if (bouton==ga) etat_futur=GT;    // vers test consigne gauche
-            else if (bouton==in) {
-                etat_futur=INV;      // inversion de consigne
-                consigne=-consigne;
-            } else if (angleMCT > consigne_eff-zmm) etat_futur=AA ; // auto //GRGR +zmm
-            break;
-        case INV :
-            if(bouton==a) etat_futur=AA;
-            break;
-        // ---- butée ----
-        case ButGAtt :
-            if(bouton==n) etat_futur=IDLE;
-            else if(ihmcmd==0x18)  etat_futur=ButRAZAtt;
-            else if(ihmcmd==0x30)  etat_futur=EXPATT;
-            else if((T.read_ms()>TEMPOCMD)&&(angle_verin <0)) {   // butéé dans le bon secteur
-                etat_futur=ButG;
-                flash_s[1]=angle_verin;   // nouvelle valeur
-                flash_s[2]=butee_droite; // valeur inchangée
-                flash_s[3]=zmm; // valeur inchangée
-                flash_s[4]=f_fusion;
-                flash_s[5]=beta;
-                sendx(0x34,(char*)flash_s,8);
-            }
-            break;
-        case ButG :
-            if(bouton==n) etat_futur=IDLE;
-            break;
-        case ButDAtt :
-            if(bouton==n) etat_futur=IDLE;
-            else if(ihmcmd==0x18)  etat_futur=ButRAZAtt;
-            else if((T.read_ms()>TEMPOCMD)&&(angle_verin >0)) {    // butéé dans le bon secteur
-                etat_futur=ButD;
-                flash_s[1]=butee_gauche;  // valeur inchangée
-                flash_s[2]=angle_verin;   // nouvelle valeur
-                flash_s[3]=zmm; // valeur inchangée
-                flash_s[4]=f_fusion;
-                flash_s[5]=beta;
-                sendx(0x34,(char*)flash_s,8);
-            }
-            break;
-        case ButRAZAtt :
-            if(bouton==n) etat_futur=IDLE;
-            else if(T.read_ms()>TEMPOCMD) {
-                etat_futur=ButRAZ;
-                flash_s[1]=-20;   // valeur RAZ
-                flash_s[2]=20;    // valeur RAE
-                flash_s[3]=zmm; // valeur inchangée
-                flash_s[4]=f_fusion;
-                flash_s[5]=beta;
-                sendx(0x34,(char*)flash_s,8);
-            }
-            break;
-        case ButRAZ :
-            if(bouton==n) etat_futur=IDLE;
-            break;
-        case ButD :
-            if(bouton==n) etat_futur=IDLE;
-            break;
-        case ParkAtt :
-            if(bouton==n) etat_futur=IDLE;
-            else if(ihmcmd==0x30)  etat_futur=EXPATT;
-            else if(T.read_ms()>3000) {
-                etat_futur=Park;
-            }
-            break;
-        case Park :
-            if(bouton==n) etat_futur=IDLE;
-            else if ((angle_verin > +zmm)) {
-                VerinG=1;
-                VerinD=0;
-            }   // correction gauche
-            else if ((angle_verin < -zmm)) {
-                VerinG=0;
-                VerinD=1;
-            }  // correction droite
-            else {
-                VerinG=0;
-                VerinD=0;
-            }
-            break;
-        // ---- expert ----
-        case EXPATT :
-            if(bouton==n) etat_futur=IDLE;
-            //else if(ihmcmd==0x30)  etat_futur=EXPATT;
-            else if(T.read_ms()>TEMPOCMD) {
-                etat_futur=EXP1;
-            }
-            break;
-        case EXP1:
-            ecran=1;  // modif xx
-            if(bouton==n) {   // init message flash valeurs courantes
-                flash_s[1]= butee_gauche;
-                flash_s[2]= butee_droite;
-                flash_s[3]= zmm;
-                flash_s[4]= f_fusion;
-                flash_s[5]= beta;
-                etat_futur=P0;
-            }
-            break;
-        case EXP0 :
-            ecran=0;  // modif xx
-            if(bouton==n) {
-                sendx(0x34,(char*)flash_s,8);
-                etat_futur=IDLE;
-            }
-            break;
-        case P0X :
-            if(bouton==n) etat_futur=P0;
-            else if(ihmcmd==0x30) etat_futur=EXP0 ; // sortie mode expert
-            break;
-        case P0 :
-            ecran=1;
-            if(bouton==p) etat_futur=P1X;
-            else if(bouton==bd) {
-                if(zmm<10)zmm++;
-                flash_s[3]=zmm; // nouvelle valeur
-                //sendx(0x34,(char*)flash_s,8);
-                etat_futur=P0Z;
-            } else if(bouton==bg) {
-                if(zmm>0)zmm--;
-                flash_s[3]=zmm; // nouvelle valeur
-                //sendx(0x34,(char*)flash_s,8);
-                etat_futur=P0Z;
-            }
-            break;
-        case P0Z :
-            if(bouton==n) etat_futur=P0;
-            break;
-        case P1X :
-            if(bouton==n) etat_futur=P1;
-            else if(ihmcmd==0x30) etat_futur=EXP0 ; // sortie mode expert
-            break;
-        case P1 :
-            ecran=2;
-            if(bouton==p) etat_futur=P2X;
-            else if(bouton==bd) {
-                if(butee_gauche<0)butee_gauche++;
-                flash_s[1]=butee_gauche;  // valeur inchangée
-                //sendx(0x34,(char*)flash_s,8);
-                etat_futur=P1Z;
-            } else if(bouton==bg) {
-                if(butee_gauche>-45)butee_gauche--;
-                flash_s[1]=butee_gauche;  // valeur inchangée
-                //sendx(0x34,(char*)flash_s,8);
-                etat_futur=P1Z;
-            }
-            break;
-        case P1Z :
-            if(bouton==n) etat_futur=P1;
-            break;
-        case P2X :
-            if(bouton==n) etat_futur=P2;
-            else if(ihmcmd==0x30) etat_futur=EXP0 ; // sortie mode expert
-            break;
-        case P2 :
-            ecran=3;
-            if(bouton==p) etat_futur=P3X;
-            else if(bouton==bd) {
-                if(butee_droite<45)butee_droite++;
-                flash_s[2]=butee_droite;;   // valeur inchangée
-                //sendx(0x34,(char*)flash_s,8);
-                etat_futur=P2Z;
-            } else if(bouton==bg) {
-                if(butee_droite>0)butee_droite--;
-                flash_s[2]=butee_droite;;   // valeur inchangée
-                //sendx(0x34,(char*)flash_s,8);
-                etat_futur=P2Z;
-            }
-            break;
-        case P2Z :
-            if(bouton==n) etat_futur=P2;
-            break;
-        case P3X :
-            if(bouton==n) etat_futur=P3;
-            else if(ihmcmd==0x30) etat_futur=EXP0 ; // sortie mode expert
-            break;
-        case P3 :
-            ecran=4;  // regmage freq angle
-            if(bouton==p) etat_futur=P4X;
-            else if(bouton==bd) {
-                if(f_fusion<10)f_fusion++;
-                flash_s[4]=f_fusion;;   // valeur inchangée
-                //pc.printf("flog %d \r\n",f_fusion);
-                etat_futur=P3Z;
-            } else if(bouton==bg) {
-                if(f_fusion>-10)f_fusion--;
-                flash_s[4]=f_fusion;;   // valeur inchangée
-                //pc.printf("flog %d \r\n",f_fusion);
-                etat_futur=P3Z;
-            }
-            break;
-        case P3Z :
-            if(bouton==n) etat_futur=P3;
-            break;
-        case P4X :
-            if(bouton==n) etat_futur=P4;
-            else if(ihmcmd==0x30) etat_futur=EXP0 ; // sortie mode expert
-            break;
-        case P4 :
-            ecran=5;  // regmage freq angle
-            if(bouton==p) etat_futur=P0X;
-            else if(bouton==bd) {
-                if(beta<50)beta++;
-                flash_s[5]=beta;;   // valeur inchangée
-                //sendx(0x34,(char*)flash_s,8);
-                //pc.printf("flog %d \r\n",beta);
-                //coef_filtre_PB(1.0,pow(10,f_fusion/10.0),0.01,coef_angle); // paramètres filtre angle Ho,Fo,Te,coef
-                //coef_filtre_PB(1.0,pow(10,f_fusion/10.0),0.01,coef_acc);  //paramètres filtre accelero Ho,Fo,Te,coef
-                etat_futur=P4Z;
-            } else if(bouton==bg) {
-                if(beta>0)beta--;
-                flash_s[5]=beta;;   // valeur inchangée
-                //sendx(0x34,(char*)flash_s,8);
-                //pc.printf("flog %d \r\n",beta);
-                //coef_filtre_PB(1.0,pow(10,f_fusion/10.0),0.01,coef_angle); // paramètres filtre angle Ho,Fo,Te,coef
-                //coef_filtre_PB(1.0,pow(10,f_fusion/10.0),0.01,coef_acc);  //paramètres filtre accelero Ho,Fo,Te,coef
-                etat_futur=P4Z;
-            }
-            break;
-        case P4Z :
-            if(bouton==n) etat_futur=P4;
-            break;
-
-
-            // default :
-            //    etat_futur=IDLE;
-            //   break;
-    }
-// valeurs des  sorties en fonction de l'état actuel
-    switch(etat_actuel) {
-        case RAZ :
-        case Param :
-            VerinG=0;
-            VerinD=0;
-            voyant[0]=1;
-            voyant[1]=1;
-            voyant[2]=1;
-            voyant[3]=1;
-            break;
-        case ERR :
-            VerinG=0;
-            VerinD=0;
-            voyant[0]=2;
-            voyant[1]=2;
-            voyant[2]=2;
-            voyant[3]=2;
-            break;
-        case IDLE :
-            VerinG=0;
-            VerinD=0;
-            voyant[0]=0;
-            voyant[1]=0;
-            voyant[2]=0;
-            voyant[3]=1;
-            T.stop();
-            T.reset();
-            ecran=0;
-            break;
-        case DB :
-            VerinG=0;
-            VerinD=0;
-            voyant[1]=2;
-            T.start();
-            break;
-        case D :  // droite manuel
-            VerinG=0;
-            VerinD=1;
-            voyant[1]=1;
-            break;
-        case GB :
-            VerinG=0;
-            VerinD=0;
-            voyant[0]=2;
-            T.start();
-            break;
-        case G :
-            VerinG=1;
-            VerinD=0;
-            voyant[0]=1;
-            break;
-
-        case AA :
-            VerinG=0;
-            VerinD=0;
-            if(consigne_eff != consigne) voyant[2]=2;
-            else voyant[2]=1;
-            voyant[0]=0;
-            voyant[1]=0;
-            T.stop();
-            T.reset();
-            break;
-        case GA :
-            VerinG=1;
-            VerinD=0;
-            voyant[0]=1;
-            if(consigne_eff != consigne) voyant[2]=2;
-            else voyant[2]=1;
-            //T.start();
-            break;
-        case DA :
-            VerinG=0;
-            VerinD=1;
-            voyant[1]=1;
-            if(consigne_eff != consigne) voyant[2]=2;
-            else voyant[2]=1;
-            break;
-        case ButGAtt :
-            voyant[0]=2;
-            T.start();
-            break;
-        case ButG :
-            voyant[0]=1;
-            break;
-        case ButDAtt :
-            voyant[1]=2;
-            T.start();
-            break;
-        case ButD :
-            voyant[1]=1;
-            break;
-        case ParkAtt :
-            voyant[2]=2;
-            T.start();
-            break;
-        case Park :
-            voyant[2]=1;
-            break;
-        case ButRAZAtt :
-            voyant[1]=2;
-            voyant[0]=2;
-            T.start();
-            break;
-        case EXPATT :
-            voyant[2]=2;
-            voyant[0]=2;
-            T.start();
-            break;
-        case EXP1:
-            voyant[2]=1;
-            voyant[0]=1;
-            break;
-        case ButRAZ :
-            voyant[1]=1;
-            voyant[0]=1;
-            break;
-        case GT :
-        case DT :
-            break;
-        case GF:
-            VerinG=1;
-            VerinD=0;
-            voyant[0]=3;
-            voyant[1]=0;
-            voyant[2]=0;
-            break;
-        case DF:
-            VerinG=0;
-            VerinD=1;
-            voyant[0]=0;
-            voyant[1]=3;
-            voyant[2]=0;
-            break;
-        default :
-            VerinG=0;
-            VerinD=0;
-            //Verin=0;
-            break;
-    }
-    Verin=VerinG||VerinD;
-    if(angle_trac>=consigne-butee_gauche) consigne_eff=butee_gauche+angle_trac;
-    else if(angle_trac<=consigne-butee_droite) consigne_eff=butee_droite+angle_trac;
-    else consigne_eff=consigne;
-#if elec
-#else
-    if(VerinG) {
-        motor.speed(0.5);
-        //pc.printf("verinG \r\n");
-    } else if(VerinD) {
-        motor.speed(-0.5);
-        //pc.printf("verinD \r\n");
-    } else {
-        motor.speed(0);
-        //pc.printf("verinStop \r\n");
-    }
-#endif
-}
-int main()
-{
-    can.frequency(1000000);
-    can.attach(&rxcan);
-    pc.baud(115200);
-    pc.printf("Hello Viti2 \r\n");
-    aRes = 2.0 / 32768.0;
-    gRes = 250.0 / 32768.0;
-    pi=4*atan(1.0);
-    //init IMU
-    DOF6.begin();
-    DOF6.calibration(1000);
-    //pc.printf("flog %d \r\n",f_fusion);
-    
-    // lancement réception can
-    wait(0);
-    //while(1){
-    sendx(0x39,NULL,0); // envoi requéte
-    //wait(1);
-    //}
-    //pc.printf(">>>> att \r\n");
-    char xx=1;
-    while(!ack) {
-        xx=!ack;
-        wait(0.1);
-        //pc.printf(">>>> ist\r\n");
-    } // attente erquéte
-    //pc.printf(">>>> OK\r\n");
-    // valeurs flash
-    //pc.printf("f_fusion %d %f \r\n",f_fusion, pow(10.0,(f_fusion-10.0)/10.0));
-    double fc =pow(10.0,(f_fusion-10.0)/10.0);
-    //calcul des coefs des filtres
-    //pour le gyro
-    coef_filtre_PB(1.0/(fc*2.0*pi),fc,0.01,coefT_gyro);      // paramètres filtre angle Ho,Fo,Te,coef
-    coef_filtre_PB(1.0,fc,0.01,coefT_acc);                    //paramètres filtre accelero Ho,Fo,Te,coef
-    //pour l'accelero
-    coef_filtre_PB(1.0/(fc*2.0*pi),fc,0.01,coefM_gyro);      // paramètres filtre angle Ho,Fo,Te,coef
-    coef_filtre_PB(1.0,fc,0.01,coefM_acc);                 //paramètres filtre accelero Ho,Fo,Te,coef
-//
-    wait(3);  // delay attende envoi depuis acc pour init filtre
-//
-    cos_inclT=cos(INCLINAISON*(pi/180));
-    sin_inclT=sin(INCLINAISON*(pi/180));
-// resolution accel // gyro
-    aRes = 2.0 / 32768.0;
-    gRes = 250.0 / 32768.0;
-    // calcul des offsets gyro tracteur
-    GXOT=(signed short)(DOF6.gxol | (DOF6.gxoh << 8))*gRes ;
-    GYOT=(signed short)(DOF6.gyol | (DOF6.gyoh << 8))*gRes ;
-    GZOT=(signed short)(DOF6.gzol | (DOF6.gzoh << 8))*gRes ;
-
-
-    //init des filtres numeriques
-    double psia=atan2(AYM,-AXM)*180/pi;
-    //pc.printf(">>>  aym  axm psia  %f %f %f  \r\n",AYM,AXM,psia);
-    accM_in[0]=psia;
-    accM_in[1]=psia;
-    accM_out[0]=psia;
-    accM_out[1]=psia;
-    //
-    DOF6.readAccel();
-    AXT=(signed short)(DOF6.axl | (DOF6.axh << 8))*aRes ;
-    AYT=(signed short)(DOF6.ayl | (DOF6.ayh << 8))*aRes ;
-    AZT=(signed short)(DOF6.azl | (DOF6.azh << 8))*aRes ;
-    psia=atan2(-AYT,AZT)*180/pi;
-    //pc.printf(">>>  ayt  azt psia  %f %f %f  \r\n",AYT,AZT,psia);
-    accT_in[0]=psia;
-    accT_in[1]=psia;
-    accT_out[0]=psia;
-    accT_out[1]=psia;
-    //pc.printf(">>>  gxom gyom gzom  %f %f %f  \r\n",GXOM,GYOM,GZOM);
-    //xx=1;
-   // while(xx) {
-    //    if(GXOM+GYOM+GZOM !=0)xx=0;
-    //    wait(0.1);
-   //}
-   // pc.printf(">>>  gxom gyom gzom  %f %f %f  \r\n",GXOM,GYOM,GZOM);
-    // lancement tickers
-    automate.attach(&autom,0.01);
-    affich.attach(&aff,0.1);
-    while(1) {
-        // lecture accelero / gyro
-        DOF6.readAccel();
-        DOF6.readGyro();
-        // mise a l'echelle
-        AXT=(signed short)(DOF6.axl | (DOF6.axh << 8))*aRes ;
-        AYT=(signed short)(DOF6.ayl | (DOF6.ayh << 8))*aRes ;
-        AZT=(signed short)(DOF6.azl | (DOF6.azh << 8))*aRes ;
-        GXT=((signed short)(DOF6.gxl | (DOF6.gxh << 8))*gRes)-GXOT ;
-        GYT=((signed short)(DOF6.gyl | (DOF6.gyh << 8))*gRes)-GYOT ;
-        GZT=((signed short)(DOF6.gzl | (DOF6.gzh << 8))*gRes)-GZOT ;
-        // pc.printf(">>> %f %f %f  \r\n",GXOT,GYOT,GZOT);
-        // pc.printf(">>> %f %f %f  \r\n",DOF6.gx_off,DOF6.gy_off,DOF6.gz_off);
-        // pc.printf("> %f %f %f %f %f %f \r\n",AXT,AYT,AZT,GXT,GYT,GZT);
-        //pc.printf("< %f %f %f %f %f %f \r\n\r\n",DOF6.ax,DOF6.ay,DOF6.az,DOF6.gx,DOF6.gy,DOF6.gz);
-        // pc.printf("> %f %f %f %f %f %f \r\n",AXM,AYM,AZM,GXM,GYM,GZM);
-        // pc.printf(">>> %f %f %f  \r\n",GXOM,GYOM,GZOM);
-        // allichage des differents ecrans
-        if(ecran==0) {
-            affi[0]=etat_automate;
-            affi[1]=angle_mat;
-            affi[2]=angle_trac;
-            affi[3]=consigne;
-            affi[4]=butee_gauche;
-            affi[5]=butee_droite;
-            affi[6]=consigne_eff;
-            affi[7]=ecran;
-        } else if(ecran==1) {
-            affi[0]=etat_automate;
-            affi[1]=zmm;
-            affi[2]=0;
-            affi[3]=0;
-            affi[4]=0;
-            affi[5]=0;
-            affi[6]=0;
-            affi[7]=ecran;
-        } else if(ecran==2) {
-            affi[0]=etat_automate;
-            affi[1]=butee_gauche;
-            affi[2]=0;
-            affi[3]=0;
-            affi[4]=0;
-            affi[5]=0;
-            affi[6]=0;
-            affi[7]=ecran;
-        } else if(ecran==3) {
-            affi[0]=etat_automate;
-            affi[1]=butee_droite;
-            affi[2]=0;
-            affi[3]=0;
-            affi[4]=0;
-            affi[5]=0;
-            affi[6]=0;
-            affi[7]=ecran;
-        } else if(ecran==4) {
-            affi[0]=etat_automate;
-            affi[1]=f_fusion;
-            affi[2]=0;
-            affi[3]=0;
-            affi[4]=0;
-            affi[5]=0;
-            affi[6]=0;
-            affi[7]=ecran;
-        } else if(ecran==5) {
-            affi[0]=etat_automate;
-            affi[1]=beta;
-            affi[2]=0;
-            affi[3]=0;
-            affi[4]=0;
-            affi[5]=0;
-            affi[6]=0;
-            affi[7]=ecran;
-        } else if(ecran==6) {
-            affi[0]=zmm;
-            affi[1]=butee_gauche;
-            affi[2]=butee_droite;
-            affi[3]=f_fusion;
-            affi[4]=beta;
-            affi[5]=0;
-            affi[6]=0;
-            affi[7]=ecran;
-        }
-    }
-}
\ No newline at end of file