Added external magnetometer functionality

Dependencies:   HMC58X31 MODI2C MPU6050 MS561101BA

Dependents:   Quadcopter_mk2

Fork of FreeIMU by Yifei Teng

Revision:
3:f9b100a9aa65
Parent:
2:5c419926dcd7
Child:
6:6b1185b32814
--- a/FreeIMU.cpp	Tue Nov 05 13:38:07 2013 +0000
+++ b/FreeIMU.cpp	Sat Nov 09 08:53:25 2013 +0000
@@ -25,169 +25,201 @@
 //#include <inttypes.h>
 //#include <stdint.h>
 //#define DEBUG
+#include "MODI2C.h"
 #include "FreeIMU.h"
+#include "rtos.h"
+
 #define     M_PI 3.1415926535897932384626433832795
 
 #ifdef DEBUG
- #define DEBUG_PRINT(x) Serial.println(x)
- #else
- #define DEBUG_PRINT(x)
- #endif
+#define DEBUG_PRINT(x) Serial.println(x)
+#else
+#define DEBUG_PRINT(x)
+#endif
 // #include "WireUtils.h"
 //#include "DebugUtils.h"
 
 //#include "vector_math.h"
 
-I2C i2c(I2C_SDA,I2C_SCL);
+FreeIMU::FreeIMU()
+{
 
-FreeIMU::FreeIMU() : accgyro(MPU6050(i2c)), magn(HMC58X3(i2c)), baro(MS561101BA(i2c)){
-
-   i2c.frequency(400000);
+    // initialize quaternion
+    q0 = 1.0f;
+    q1 = 0.0f;
+    q2 = 0.0f;
+    q3 = 0.0f;
+    exInt = 0.0;
+    eyInt = 0.0;
+    ezInt = 0.0;
+    twoKp = twoKpDef;
+    twoKi = twoKiDef;
+    integralFBx = 0.0f,  integralFBy = 0.0f, integralFBz = 0.0f;
 
-  // initialize quaternion
-  q0 = 1.0f;
-  q1 = 0.0f;
-  q2 = 0.0f;
-  q3 = 0.0f;
-  exInt = 0.0;
-  eyInt = 0.0;
-  ezInt = 0.0;
-  twoKp = twoKpDef;
-  twoKi = twoKiDef;
-  integralFBx = 0.0f,  integralFBy = 0.0f, integralFBz = 0.0f;
-  
-  update.start();
-  dt_us=0;
-  /*
-  lastUpdate = 0;
-  now = 0;
-  */
-  #ifndef CALIBRATION_H
-  // initialize scale factors to neutral values
-  acc_scale_x = 1;
-  acc_scale_y = 1;
-  acc_scale_z = 1;
-  magn_scale_x = 1;
-  magn_scale_y = 1;
-  magn_scale_z = 1;
-  #else
-  // get values from global variables of same name defined in calibration.h
-  acc_off_x = ::acc_off_x;
-  acc_off_y = ::acc_off_y;
-  acc_off_z = ::acc_off_z;
-  acc_scale_x = ::acc_scale_x;
-  acc_scale_y = ::acc_scale_y;
-  acc_scale_z = ::acc_scale_z;
-  magn_off_x = ::magn_off_x;
-  magn_off_y = ::magn_off_y;
-  magn_off_z = ::magn_off_z;
-  magn_scale_x = ::magn_scale_x;
-  magn_scale_y = ::magn_scale_y;
-  magn_scale_z = ::magn_scale_z;
-  #endif
+    update.start();
+    dt_us=0;
+    /*
+    lastUpdate = 0;
+    now = 0;
+    */
+#ifndef CALIBRATION_H
+    // initialize scale factors to neutral values
+    acc_scale_x = 1;
+    acc_scale_y = 1;
+    acc_scale_z = 1;
+    magn_scale_x = 1;
+    magn_scale_y = 1;
+    magn_scale_z = 1;
+#else
+    // get values from global variables of same name defined in calibration.h
+    acc_off_x = ::acc_off_x;
+    acc_off_y = ::acc_off_y;
+    acc_off_z = ::acc_off_z;
+    acc_scale_x = ::acc_scale_x;
+    acc_scale_y = ::acc_scale_y;
+    acc_scale_z = ::acc_scale_z;
+    magn_off_x = ::magn_off_x;
+    magn_off_y = ::magn_off_y;
+    magn_off_z = ::magn_off_z;
+    magn_scale_x = ::magn_scale_x;
+    magn_scale_y = ::magn_scale_y;
+    magn_scale_z = ::magn_scale_z;
+#endif
 }
 
-void FreeIMU::init() {
+void FreeIMU::init()
+{
 
-  init(FIMU_ACCGYRO_ADDR, false);
+    init(FIMU_ACCGYRO_ADDR, false);
 
 }
 
-void FreeIMU::init(bool fastmode) {
- 
-  init(FIMU_ACCGYRO_ADDR, fastmode);
- 
+void FreeIMU::init(bool fastmode)
+{
+
+    init(FIMU_ACCGYRO_ADDR, fastmode);
+
 }
 
-
 /**
  * Initialize the FreeIMU I2C bus, sensors and performs gyro offsets calibration
 */
 
-void FreeIMU::init(int accgyro_addr, bool fastmode) {
+void FreeIMU::init(int accgyro_addr, bool fastmode)
+{
+    accgyro = new MPU6050();
+    Thread::wait(10);
+    baro = new MS561101BA();
+    magn = new HMC58X3();
 
-  wait_ms(10);
+    Thread::wait(10);
+
+    printf("AccGyro init start\r\n");
+
+    printf("DeviceId = %d\r\n",accgyro->getDeviceID());
 
-  accgyro = MPU6050(0x68);
-  accgyro.initialize();
-  accgyro.setI2CMasterModeEnabled(0);
-  accgyro.setI2CBypassEnabled(1);
-  accgyro.setFullScaleGyroRange(MPU6050_GYRO_FS_1000);
-  accgyro.setDLPFMode(1);
-  accgyro.setRate(0);
-  wait_ms(10);
+    accgyro->initialize();
+    printf("AccGyro setting\r\n");
+    accgyro->setI2CMasterModeEnabled(0);
+    accgyro->setI2CBypassEnabled(1);
+    accgyro->setFullScaleGyroRange(MPU6050_GYRO_FS_1000);
+    accgyro->setDLPFMode(0);
+    accgyro->setRate(0);
+    Thread::wait(20);
 
+    accgyro->start_sampling();
+
+    printf("AccGyro init fin\r\n");
+    Thread::wait(10);
 
-  // init HMC5843
-  magn.init(false); // Don't set mode yet, we'll do that later on.
-  magn.setGain(0);
-  // Calibrate HMC using self test, not recommended to change the gain after calibration.
-  magn.calibrate(0, 8); // Use gain 1=default, valid 0-7, 7 not recommended.
-  // Single mode conversion was used in calibration, now set continuous mode
-  magn.setMode(0);
-  wait_ms(20);
-  magn.setDOR(6);
-  
-  baro.init(FIMU_BARO_ADDR);
-  
-  // zero gyro
-  zeroGyro();
-  
-  #ifndef CALIBRATION_H
-  // load calibration from eeprom
-  calLoad();
-  #endif
-  getQ_simple(NULL);
+    // init HMC5843
+    magn->init(false); // Don't set mode yet, we'll do that later on.
+    magn->setGain(0);
+    // Calibrate HMC using self test, not recommended to change the gain after calibration.
+    magn->calibrate(0, 8); // Use gain 1=default, valid 0-7, 7 not recommended.
+    // Single mode conversion was used in calibration, now set continuous mode
+    //magn.setMode(0);
+
+    Thread::wait(30);
+
+    magn->setDOR(6);
+
+    Thread::wait(30);
+
+    printf("Magn init fin\r\n");
+
+    magn->start_sampling();
+
+    Thread::wait(30);
+
+    baro->init(FIMU_BARO_ADDR);
+
+    printf("Baro init fin\r\n");
+
+    // zero gyro
+    zeroGyro();
+
+#ifndef CALIBRATION_H
+    // load calibration from eeprom
+    calLoad();
+#endif
+
+    Thread::wait(30);
+
+    getQ_simple(NULL);
+
+    baro->start_sampling(MS561101BA_OSR_4096);
 }
 
 void FreeIMU::getQ_simple(float* q)
 {
-  float values[9];
-  getValues(values);
-  
-  float pitch = atan2(values[0], sqrt(values[1]*values[1]+values[2]*values[2]));
-  float roll = -atan2(values[1], sqrt(values[0]*values[0]+values[2]*values[2]));
-  
-  float xh = values[6]*cos(pitch)+values[7]*sin(roll)*sin(pitch)-values[8]*cos(roll)*sin(pitch);
-  float yh = values[7]*cos(roll)+values[8]*sin(roll);
-  float yaw = -atan2(yh, xh);
-  
-  float rollOver2 = (roll + M_PI) * 0.5f;
-  float sinRollOver2 = (float)sin(rollOver2);
-  float cosRollOver2 = (float)cos(rollOver2);
-  float pitchOver2 = pitch * 0.5f;
-  float sinPitchOver2 = (float)sin(pitchOver2);
-  float cosPitchOver2 = (float)cos(pitchOver2);
-  float yawOver2 = yaw * 0.5f;
-  float sinYawOver2 = (float)sin(yawOver2);
-  float cosYawOver2 = (float)cos(yawOver2);
+    float values[9];
+    getValues(values);
+
+    float pitch = atan2(values[0], sqrt(values[1]*values[1]+values[2]*values[2]));
+    float roll = -atan2(values[1], sqrt(values[0]*values[0]+values[2]*values[2]));
+
+    float xh = values[6]*cos(pitch)+values[7]*sin(roll)*sin(pitch)-values[8]*cos(roll)*sin(pitch);
+    float yh = values[7]*cos(roll)+values[8]*sin(roll);
+    float yaw = -atan2(yh, xh);
 
-  q0 = cosYawOver2 * cosPitchOver2 * sinRollOver2 - sinYawOver2 * sinPitchOver2 * cosRollOver2;
-  q1 = cosYawOver2 * cosPitchOver2 * cosRollOver2 + sinYawOver2 * sinPitchOver2 * sinRollOver2;
-  q2 = sinYawOver2 * cosPitchOver2 * cosRollOver2 - cosYawOver2 * sinPitchOver2 * sinRollOver2;
-  q3 = cosYawOver2 * sinPitchOver2 * cosRollOver2 + sinYawOver2 * cosPitchOver2 * sinRollOver2;
-  
-  if (q!=NULL){
-          q[0] = q0;
-          q[1] = q1;
-          q[2] = q2;
-          q[3] = q3;
-  }
+    float rollOver2 = (roll + M_PI) * 0.5f;
+    float sinRollOver2 = (float)sin(rollOver2);
+    float cosRollOver2 = (float)cos(rollOver2);
+    float pitchOver2 = pitch * 0.5f;
+    float sinPitchOver2 = (float)sin(pitchOver2);
+    float cosPitchOver2 = (float)cos(pitchOver2);
+    float yawOver2 = yaw * 0.5f;
+    float sinYawOver2 = (float)sin(yawOver2);
+    float cosYawOver2 = (float)cos(yawOver2);
+
+    q0 = cosYawOver2 * cosPitchOver2 * sinRollOver2 - sinYawOver2 * sinPitchOver2 * cosRollOver2;
+    q1 = cosYawOver2 * cosPitchOver2 * cosRollOver2 + sinYawOver2 * sinPitchOver2 * sinRollOver2;
+    q2 = sinYawOver2 * cosPitchOver2 * cosRollOver2 - cosYawOver2 * sinPitchOver2 * sinRollOver2;
+    q3 = cosYawOver2 * sinPitchOver2 * cosRollOver2 + sinYawOver2 * cosPitchOver2 * sinRollOver2;
+
+    if (q!=NULL) {
+        q[0] = q0;
+        q[1] = q1;
+        q[2] = q2;
+        q[3] = q3;
+    }
 }
 
 /**
  * Populates raw_values with the raw_values from the sensors
 */
-void FreeIMU::getRawValues(int16_t * raw_values) {
+void FreeIMU::getRawValues(int16_t * raw_values)
+{
 
-    accgyro.getMotion6(&raw_values[0], &raw_values[1], &raw_values[2], &raw_values[3], &raw_values[4], &raw_values[5]);
-    magn.getValues(&raw_values[6], &raw_values[7], &raw_values[8]);
-    
+    accgyro->getMotion6(&raw_values[0], &raw_values[1], &raw_values[2], &raw_values[3], &raw_values[4], &raw_values[5]);
+    magn->getValues(&raw_values[6], &raw_values[7], &raw_values[8]);
+
     int temp, press;
     //TODO: possible loss of precision
-    temp = baro.rawTemperature(MS561101BA_OSR_4096);
+    temp = baro->rawTemperature();
     raw_values[9] = temp;
-    press = baro.rawPressure(MS561101BA_OSR_4096);
+    press = baro->rawPressure();
     raw_values[10] = press;
 }
 
@@ -195,220 +227,216 @@
 /**
  * Populates values with calibrated readings from the sensors
 */
-void FreeIMU::getValues(float * values) {  
+void FreeIMU::getValues(float * values)
+{
 
 // MPU6050
     int16_t accgyroval[6];
-    accgyro.getMotion6(&accgyroval[0], &accgyroval[1], &accgyroval[2], &accgyroval[3], &accgyroval[4], &accgyroval[5]);
-    
+    accgyro->getMotion6(&accgyroval[0], &accgyroval[1], &accgyroval[2], &accgyroval[3], &accgyroval[4], &accgyroval[5]);
+
     // remove offsets from the gyroscope
     accgyroval[3] = accgyroval[3] - gyro_off_x;
     accgyroval[4] = accgyroval[4] - gyro_off_y;
     accgyroval[5] = accgyroval[5] - gyro_off_z;
 
     for(int i = 0; i<6; i++) {
-      if(i < 3) {
-        values[i] = (float) accgyroval[i];
-      }
-      else {
-        values[i] = ((float) accgyroval[i]) / 32.8f; // NOTE: this depends on the sensitivity chosen
-      }
+        if(i < 3) {
+            values[i] = (float) accgyroval[i];
+        } else {
+            values[i] = ((float) accgyroval[i]) / 32.8f; // NOTE: this depends on the sensitivity chosen
+        }
     }
 
-  
-  
-  #warning Accelerometer calibration active: have you calibrated your device?
-  // remove offsets and scale accelerometer (calibration)
-  values[0] = (values[0] - acc_off_x) / acc_scale_x;
-  values[1] = (values[1] - acc_off_y) / acc_scale_y;
-  values[2] = (values[2] - acc_off_z) / acc_scale_z;
-  
-  magn.getValues(&values[6]);
-    // calibration 
-    #warning Magnetometer calibration active: have you calibrated your device?
+
+
+#warning Accelerometer calibration active: have you calibrated your device?
+    // remove offsets and scale accelerometer (calibration)
+    values[0] = (values[0] - acc_off_x) / acc_scale_x;
+    values[1] = (values[1] - acc_off_y) / acc_scale_y;
+    values[2] = (values[2] - acc_off_z) / acc_scale_z;
+
+    magn->getValues(&values[6]);
+    // calibration
+#warning Magnetometer calibration active: have you calibrated your device?
     values[6] = (values[6] - magn_off_x) / magn_scale_x;
     values[7] = (values[7] - magn_off_y) / magn_scale_y;
     values[8] = (values[8] - magn_off_z) / magn_scale_z;
- 
+
 }
 
 
 /**
  * Computes gyro offsets
 */
-void FreeIMU::zeroGyro() {
-  const int totSamples = 3;
-  int16_t raw[11];
-  float tmpOffsets[] = {0,0,0};
-  
-  for (int i = 0; i < totSamples; i++){
-    getRawValues(raw);
-    tmpOffsets[0] += raw[3];
-    tmpOffsets[1] += raw[4];
-    tmpOffsets[2] += raw[5];
-  }
-  
-  gyro_off_x = tmpOffsets[0] / totSamples;
-  gyro_off_y = tmpOffsets[1] / totSamples;
-  gyro_off_z = tmpOffsets[2] / totSamples;
+void FreeIMU::zeroGyro()
+{
+    const int totSamples = 8;
+    int16_t raw[11];
+    float tmpOffsets[] = {0,0,0};
+
+    for (int i = 0; i < totSamples; i++) {
+        getRawValues(raw);
+        tmpOffsets[0] += raw[3];
+        tmpOffsets[1] += raw[4];
+        tmpOffsets[2] += raw[5];
+        Thread::wait(2);
+    }
+
+    gyro_off_x = tmpOffsets[0] / totSamples;
+    gyro_off_y = tmpOffsets[1] / totSamples;
+    gyro_off_z = tmpOffsets[2] / totSamples;
 }
 
+extern bool magn_valid;
 
 /**
  * Quaternion implementation of the 'DCM filter' [Mayhony et al].  Incorporates the magnetic distortion
  * compensation algorithms from Sebastian Madgwick's filter which eliminates the need for a reference
  * direction of flux (bx bz) to be predefined and limits the effect of magnetic distortions to yaw
  * axis only.
- * 
+ *
  * @see: http://www.x-io.co.uk/node/8#open_source_ahrs_and_imu_algorithms
 */
 
-void  FreeIMU::AHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz) {
+void  FreeIMU::AHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz, bool _magn_valid)
+{
+
+    float recipNorm;
+    float q0q0, q0q1, q0q2, q0q3, q1q1, q1q2, q1q3, q2q2, q2q3, q3q3;
+    float halfex = 0.0f, halfey = 0.0f, halfez = 0.0f;
+    float qa, qb, qc;
 
-  float recipNorm;
-  float q0q0, q0q1, q0q2, q0q3, q1q1, q1q2, q1q3, q2q2, q2q3, q3q3;
-  float halfex = 0.0f, halfey = 0.0f, halfez = 0.0f;
-  float qa, qb, qc;
+    // Auxiliary variables to avoid repeated arithmetic
+    q0q0 = q0 * q0;
+    q0q1 = q0 * q1;
+    q0q2 = q0 * q2;
+    q0q3 = q0 * q3;
+    q1q1 = q1 * q1;
+    q1q2 = q1 * q2;
+    q1q3 = q1 * q3;
+    q2q2 = q2 * q2;
+    q2q3 = q2 * q3;
+    q3q3 = q3 * q3;
 
-  // Auxiliary variables to avoid repeated arithmetic
-  q0q0 = q0 * q0;
-  q0q1 = q0 * q1;
-  q0q2 = q0 * q2;
-  q0q3 = q0 * q3;
-  q1q1 = q1 * q1;
-  q1q2 = q1 * q2;
-  q1q3 = q1 * q3;
-  q2q2 = q2 * q2;
-  q2q3 = q2 * q3;
-  q3q3 = q3 * q3;
-  
-  // Use magnetometer measurement only when valid (avoids NaN in magnetometer normalisation)
-  if((mx != 0.0f) && (my != 0.0f) && (mz != 0.0f)) {
-    float hx, hy, bx, bz;
-    float halfwx, halfwy, halfwz;
-    
-    // Normalise magnetometer measurement
-    recipNorm = invSqrt(mx * mx + my * my + mz * mz);
-    mx *= recipNorm;
-    my *= recipNorm;
-    mz *= recipNorm;
-    
-    // Reference direction of Earth's magnetic field
-    hx = 2.0f * (mx * (0.5f - q2q2 - q3q3) + my * (q1q2 - q0q3) + mz * (q1q3 + q0q2));
-    hy = 2.0f * (mx * (q1q2 + q0q3) + my * (0.5f - q1q1 - q3q3) + mz * (q2q3 - q0q1));
-    bx = sqrt(hx * hx + hy * hy);
-    bz = 2.0f * (mx * (q1q3 - q0q2) + my * (q2q3 + q0q1) + mz * (0.5f - q1q1 - q2q2));
-    
-    // Estimated direction of magnetic field
-    halfwx = bx * (0.5f - q2q2 - q3q3) + bz * (q1q3 - q0q2);
-    halfwy = bx * (q1q2 - q0q3) + bz * (q0q1 + q2q3);
-    halfwz = bx * (q0q2 + q1q3) + bz * (0.5f - q1q1 - q2q2);
-    
-    // Error is sum of cross product between estimated direction and measured direction of field vectors
-    halfex = (my * halfwz - mz * halfwy);
-    halfey = (mz * halfwx - mx * halfwz);
-    halfez = (mx * halfwy - my * halfwx);
-  }
+    // Use magnetometer measurement only when valid (avoids NaN in magnetometer normalisation)
+    if((mx != 0.0f) && (my != 0.0f) && (mz != 0.0f) && _magn_valid) {
+        float hx, hy, bx, bz;
+        float halfwx, halfwy, halfwz;
+
+        // Normalise magnetometer measurement
+        recipNorm = invSqrt(mx * mx + my * my + mz * mz);
+        mx *= recipNorm;
+        my *= recipNorm;
+        mz *= recipNorm;
 
-  // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation)
-  if((ax != 0.0f) && (ay != 0.0f) && (az != 0.0f)) {
-    float halfvx, halfvy, halfvz;
-    
-    // Normalise accelerometer measurement
-    recipNorm = invSqrt(ax * ax + ay * ay + az * az);
-    ax *= recipNorm;
-    ay *= recipNorm;
-    az *= recipNorm;
-    
-    // Estimated direction of gravity
-    halfvx = q1q3 - q0q2;
-    halfvy = q0q1 + q2q3;
-    halfvz = q0q0 - 0.5f + q3q3;
-  
-    // Error is sum of cross product between estimated direction and measured direction of field vectors
-    halfex += (ay * halfvz - az * halfvy);
-    halfey += (az * halfvx - ax * halfvz);
-    halfez += (ax * halfvy - ay * halfvx);
-  }
+        // Reference direction of Earth's magnetic field
+        hx = 2.0f * (mx * (0.5f - q2q2 - q3q3) + my * (q1q2 - q0q3) + mz * (q1q3 + q0q2));
+        hy = 2.0f * (mx * (q1q2 + q0q3) + my * (0.5f - q1q1 - q3q3) + mz * (q2q3 - q0q1));
+        bx = sqrt(hx * hx + hy * hy);
+        bz = 2.0f * (mx * (q1q3 - q0q2) + my * (q2q3 + q0q1) + mz * (0.5f - q1q1 - q2q2));
 
-  // Apply feedback only when valid data has been gathered from the accelerometer or magnetometer
-  if(halfex != 0.0f && halfey != 0.0f && halfez != 0.0f) {
-    // Compute and apply integral feedback if enabled
-    if(twoKi > 0.0f) {
-      integralFBx += twoKi * halfex * (1.0f / sampleFreq);  // integral error scaled by Ki
-      integralFBy += twoKi * halfey * (1.0f / sampleFreq);
-      integralFBz += twoKi * halfez * (1.0f / sampleFreq);
-      gx += integralFBx;  // apply integral feedback
-      gy += integralFBy;
-      gz += integralFBz;
-    }
-    else {
-      integralFBx = 0.0f; // prevent integral windup
-      integralFBy = 0.0f;
-      integralFBz = 0.0f;
+        // Estimated direction of magnetic field
+        halfwx = bx * (0.5f - q2q2 - q3q3) + bz * (q1q3 - q0q2);
+        halfwy = bx * (q1q2 - q0q3) + bz * (q0q1 + q2q3);
+        halfwz = bx * (q0q2 + q1q3) + bz * (0.5f - q1q1 - q2q2);
+
+        // Error is sum of cross product between estimated direction and measured direction of field vectors
+        halfex = (my * halfwz - mz * halfwy);
+        halfey = (mz * halfwx - mx * halfwz);
+        halfez = (mx * halfwy - my * halfwx);
+
+        magn_valid = false;
     }
 
-    // Apply proportional feedback
-    gx += twoKp * halfex;
-    gy += twoKp * halfey;
-    gz += twoKp * halfez;
-  }
-  
-  // Integrate rate of change of quaternion
-  gx *= (0.5f * (1.0f / sampleFreq));   // pre-multiply common factors
-  gy *= (0.5f * (1.0f / sampleFreq));
-  gz *= (0.5f * (1.0f / sampleFreq));
-  qa = q0;
-  qb = q1;
-  qc = q2;
-  q0 += (-qb * gx - qc * gy - q3 * gz);
-  q1 += (qa * gx + qc * gz - q3 * gy);
-  q2 += (qa * gy - qb * gz + q3 * gx);
-  q3 += (qa * gz + qb * gy - qc * gx);
-  
-  // Normalise quaternion
-  recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3);
-  q0 *= recipNorm;
-  q1 *= recipNorm;
-  q2 *= recipNorm;
-  q3 *= recipNorm;
+    // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation)
+    if((ax != 0.0f) && (ay != 0.0f) && (az != 0.0f)) {
+        float halfvx, halfvy, halfvz;
+
+        // Normalise accelerometer measurement
+        recipNorm = invSqrt(ax * ax + ay * ay + az * az);
+        ax *= recipNorm;
+        ay *= recipNorm;
+        az *= recipNorm;
+
+        // Estimated direction of gravity
+        halfvx = q1q3 - q0q2;
+        halfvy = q0q1 + q2q3;
+        halfvz = q0q0 - 0.5f + q3q3;
+
+        // Error is sum of cross product between estimated direction and measured direction of field vectors
+        halfex += (ay * halfvz - az * halfvy);
+        halfey += (az * halfvx - ax * halfvz);
+        halfez += (ax * halfvy - ay * halfvx);
+    }
+
+    // Apply feedback only when valid data has been gathered from the accelerometer or magnetometer
+    if(halfex != 0.0f && halfey != 0.0f && halfez != 0.0f) {
+        // Compute and apply integral feedback if enabled
+        if(twoKi > 0.0f) {
+            integralFBx += twoKi * halfex * (1.0f / sampleFreq);  // integral error scaled by Ki
+            integralFBy += twoKi * halfey * (1.0f / sampleFreq);
+            integralFBz += twoKi * halfez * (1.0f / sampleFreq);
+            gx += integralFBx;  // apply integral feedback
+            gy += integralFBy;
+            gz += integralFBz;
+        } else {
+            integralFBx = 0.0f; // prevent integral windup
+            integralFBy = 0.0f;
+            integralFBz = 0.0f;
+        }
+
+        // Apply proportional feedback
+        gx += twoKp * halfex;
+        gy += twoKp * halfey;
+        gz += twoKp * halfez;
+    }
+
+    // Integrate rate of change of quaternion
+    gx *= (0.5f * (1.0f / sampleFreq));   // pre-multiply common factors
+    gy *= (0.5f * (1.0f / sampleFreq));
+    gz *= (0.5f * (1.0f / sampleFreq));
+    qa = q0;
+    qb = q1;
+    qc = q2;
+    q0 += (-qb * gx - qc * gy - q3 * gz);
+    q1 += (qa * gx + qc * gz - q3 * gy);
+    q2 += (qa * gy - qb * gz + q3 * gx);
+    q3 += (qa * gz + qb * gy - qc * gx);
+
+    // Normalise quaternion
+    recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3);
+    q0 *= recipNorm;
+    q1 *= recipNorm;
+    q2 *= recipNorm;
+    q3 *= recipNorm;
 }
 
 
 /**
  * Populates array q with a quaternion representing the IMU orientation with respect to the Earth
- * 
+ *
  * @param q the quaternion to populate
 */
-void FreeIMU::getQ(float * q) {
-  float val[9];
-  getValues(val);
-  
-  DEBUG_PRINT(val[3] * M_PI/180);
-  DEBUG_PRINT(val[4] * M_PI/180);
-  DEBUG_PRINT(val[5] * M_PI/180);
-  DEBUG_PRINT(val[0]);
-  DEBUG_PRINT(val[1]);
-  DEBUG_PRINT(val[2]);
-  DEBUG_PRINT(val[6]);
-  DEBUG_PRINT(val[7]);
-  DEBUG_PRINT(val[8]);
-  
-  //now = micros();
-  dt_us=update.read_us();
-  sampleFreq = 1.0 / ((dt_us) / 1000000.0);
-  update.reset();
- // lastUpdate = now;
-  // gyro values are expressed in deg/sec, the * M_PI/180 will convert it to radians/sec
+void FreeIMU::getQ(float * q)
+{
+    float val[9];
+    getValues(val);
 
-    AHRSupdate(val[3] * M_PI/180, val[4] * M_PI/180, val[5] * M_PI/180, val[0], val[1], val[2], val[6], val[7], val[8]);
+    //now = micros();
+    dt_us=update.read_us();
+    sampleFreq = 1.0 / ((dt_us) / 1000000.0);
+    update.reset();
+// lastUpdate = now;
+    // gyro values are expressed in deg/sec, the * M_PI/180 will convert it to radians/sec
 
-  if (q!=NULL){
-      q[0] = q0;
-      q[1] = q1;
-      q[2] = q2;
-      q[3] = q3;
-  }
+    AHRSupdate(val[3] * M_PI/180.0, val[4] * M_PI/180.0, val[5] * M_PI/180.0, val[0], val[1], val[2], val[6], val[7], val[8], magn_valid);
+
+    if (q!=NULL) {
+        q[0] = q0;
+        q[1] = q1;
+        q[2] = q2;
+        q[3] = q3;
+    }
 }
 
 
@@ -417,21 +445,24 @@
 /**
  * Returns an altitude estimate from baromether readings only using sea_press as current sea level pressure
 */
-float FreeIMU::getBaroAlt(float sea_press) {
-  float temp = baro.getTemperature(MS561101BA_OSR_4096);
-  float press = baro.getPressure(MS561101BA_OSR_4096);
-  return ((pow((float)(sea_press / press), 1.0f/5.257f) - 1.0f) * (temp + 273.15f)) / 0.0065f;
+float FreeIMU::getBaroAlt(float sea_press)
+{
+    float temp = baro->getTemperature();
+    float press = baro->getPressure();
+    return ((pow((float)(sea_press / press), 1.0f/5.257f) - 1.0f) * (temp + 273.15f)) / 0.0065f;
 }
 
 /**
  * Returns an altitude estimate from baromether readings only using a default sea level pressure
 */
-float FreeIMU::getBaroAlt() {
-  return getBaroAlt(def_sea_press);
+float FreeIMU::getBaroAlt()
+{
+    return getBaroAlt(def_sea_press);
 }
 
-float FreeIMU::getRawPressure() {
-  return baro.getPressure(MS561101BA_OSR_4096);
+float FreeIMU::getRawPressure()
+{
+    return baro->getPressure();
 }
 
 
@@ -440,47 +471,50 @@
  * @param acc the accelerometer readings to compensate for gravity
  * @param q the quaternion orientation of the sensor board with respect to the world
 */
-void FreeIMU::gravityCompensateAcc(float * acc, float * q) {
-  float g[3];
-  
-  // get expected direction of gravity in the sensor frame
-  g[0] = 2 * (q[1] * q[3] - q[0] * q[2]);
-  g[1] = 2 * (q[0] * q[1] + q[2] * q[3]);
-  g[2] = q[0] * q[0] - q[1] * q[1] - q[2] * q[2] + q[3] * q[3];
-  
-  // compensate accelerometer readings with the expected direction of gravity
-  acc[0] = acc[0] - g[0];
-  acc[1] = acc[1] - g[1];
-  acc[2] = acc[2] - g[2];
+void FreeIMU::gravityCompensateAcc(float * acc, float * q)
+{
+    float g[3];
+
+    // get expected direction of gravity in the sensor frame
+    g[0] = 2 * (q[1] * q[3] - q[0] * q[2]);
+    g[1] = 2 * (q[0] * q[1] + q[2] * q[3]);
+    g[2] = q[0] * q[0] - q[1] * q[1] - q[2] * q[2] + q[3] * q[3];
+
+    // compensate accelerometer readings with the expected direction of gravity
+    acc[0] = acc[0] - g[0];
+    acc[1] = acc[1] - g[1];
+    acc[2] = acc[2] - g[2];
 }
 
 
 /**
  * Returns the Euler angles in radians defined in the Aerospace sequence.
- * See Sebastian O.H. Madwick report "An efficient orientation filter for 
+ * See Sebastian O.H. Madwick report "An efficient orientation filter for
  * inertial and intertial/magnetic sensor arrays" Chapter 2 Quaternion representation
- * 
+ *
  * @param angles three floats array which will be populated by the Euler angles in radians
 */
-void FreeIMU::getEulerRad(float * angles) {
-  float q[4]; // quaternion
-  getQ(q);
-  angles[0] = atan2(2 * q[1] * q[2] - 2 * q[0] * q[3], 2 * q[0]*q[0] + 2 * q[1] * q[1] - 1); // psi
-  angles[1] = -asin(2 * q[1] * q[3] + 2 * q[0] * q[2]); // theta
-  angles[2] = atan2(2 * q[2] * q[3] - 2 * q[0] * q[1], 2 * q[0] * q[0] + 2 * q[3] * q[3] - 1); // phi
+void FreeIMU::getEulerRad(float * angles)
+{
+    float q[4]; // quaternion
+    getQ(q);
+    angles[0] = atan2(2 * q[1] * q[2] - 2 * q[0] * q[3], 2 * q[0]*q[0] + 2 * q[1] * q[1] - 1); // psi
+    angles[1] = -asin(2 * q[1] * q[3] + 2 * q[0] * q[2]); // theta
+    angles[2] = atan2(2 * q[2] * q[3] - 2 * q[0] * q[1], 2 * q[0] * q[0] + 2 * q[3] * q[3] - 1); // phi
 }
 
 
 /**
  * Returns the Euler angles in degrees defined with the Aerospace sequence.
- * See Sebastian O.H. Madwick report "An efficient orientation filter for 
+ * See Sebastian O.H. Madwick report "An efficient orientation filter for
  * inertial and intertial/magnetic sensor arrays" Chapter 2 Quaternion representation
- * 
+ *
  * @param angles three floats array which will be populated by the Euler angles in degrees
 */
-void FreeIMU::getEuler(float * angles) {
-  getEulerRad(angles);
-  arr3_rad_to_deg(angles);
+void FreeIMU::getEuler(float * angles)
+{
+    getEulerRad(angles);
+    arr3_rad_to_deg(angles);
 }
 
 
@@ -488,24 +522,25 @@
  * Returns the yaw pitch and roll angles, respectively defined as the angles in radians between
  * the Earth North and the IMU X axis (yaw), the Earth ground plane and the IMU X axis (pitch)
  * and the Earth ground plane and the IMU Y axis.
- * 
+ *
  * @note This is not an Euler representation: the rotations aren't consecutive rotations but only
  * angles from Earth and the IMU. For Euler representation Yaw, Pitch and Roll see FreeIMU::getEuler
- * 
+ *
  * @param ypr three floats array which will be populated by Yaw, Pitch and Roll angles in radians
 */
-void FreeIMU::getYawPitchRollRad(float * ypr) {
-  float q[4]; // quaternion
-  float gx, gy, gz; // estimated gravity direction
-  getQ(q);
-  
-  gx = 2 * (q[1]*q[3] - q[0]*q[2]);
-  gy = 2 * (q[0]*q[1] + q[2]*q[3]);
-  gz = q[0]*q[0] - q[1]*q[1] - q[2]*q[2] + q[3]*q[3];
-  
-  ypr[0] = atan2(2 * q[1] * q[2] - 2 * q[0] * q[3], 2 * q[0]*q[0] + 2 * q[1] * q[1] - 1);
-  ypr[1] = atan(gx / sqrt(gy*gy + gz*gz));
-  ypr[2] = atan(gy / sqrt(gx*gx + gz*gz));
+void FreeIMU::getYawPitchRollRad(float * ypr)
+{
+    float q[4]; // quaternion
+    float gx, gy, gz; // estimated gravity direction
+    getQ(q);
+
+    gx = 2 * (q[1]*q[3] - q[0]*q[2]);
+    gy = 2 * (q[0]*q[1] + q[2]*q[3]);
+    gz = q[0]*q[0] - q[1]*q[1] - q[2]*q[2] + q[3]*q[3];
+
+    ypr[0] = atan2(2 * q[1] * q[2] - 2 * q[0] * q[3], 2 * q[0]*q[0] + 2 * q[1] * q[1] - 1);
+    ypr[1] = atan(gx / sqrt(gy*gy + gz*gz));
+    ypr[2] = atan(gy / sqrt(gx*gx + gz*gz));
 }
 
 
@@ -513,25 +548,27 @@
  * Returns the yaw pitch and roll angles, respectively defined as the angles in degrees between
  * the Earth North and the IMU X axis (yaw), the Earth ground plane and the IMU X axis (pitch)
  * and the Earth ground plane and the IMU Y axis.
- * 
+ *
  * @note This is not an Euler representation: the rotations aren't consecutive rotations but only
  * angles from Earth and the IMU. For Euler representation Yaw, Pitch and Roll see FreeIMU::getEuler
- * 
+ *
  * @param ypr three floats array which will be populated by Yaw, Pitch and Roll angles in degrees
 */
-void FreeIMU::getYawPitchRoll(float * ypr) {
-  getYawPitchRollRad(ypr);
-  arr3_rad_to_deg(ypr);
+void FreeIMU::getYawPitchRoll(float * ypr)
+{
+    getYawPitchRollRad(ypr);
+    arr3_rad_to_deg(ypr);
 }
 
 
 /**
  * Converts a 3 elements array arr of angles expressed in radians into degrees
 */
-void arr3_rad_to_deg(float * arr) {
-  arr[0] *= 180/M_PI;
-  arr[1] *= 180/M_PI;
-  arr[2] *= 180/M_PI;
+void arr3_rad_to_deg(float * arr)
+{
+    arr[0] *= 180/M_PI;
+    arr[1] *= 180/M_PI;
+    arr[2] *= 180/M_PI;
 }
 
 
@@ -539,18 +576,19 @@
  * Fast inverse square root implementation
  * @see http://en.wikipedia.org/wiki/Fast_inverse_square_root
 */
-float invSqrt(float number) {
-  volatile long i;
-  volatile float x, y;
-  volatile const float f = 1.5F;
+float invSqrt(float number)
+{
+    volatile long i;
+    volatile float x, y;
+    volatile const float f = 1.5F;
 
-  x = number * 0.5F;
-  y = number;
-  i = * ( long * ) &y;
-  i = 0x5f375a86 - ( i >> 1 );
-  y = * ( float * ) &i;
-  y = y * ( f - ( x * y * y ) );
-  return y;
+    x = number * 0.5F;
+    y = number;
+    i = * ( long * ) &y;
+    i = 0x5f375a86 - ( i >> 1 );
+    y = * ( float * ) &i;
+    y = y * ( f - ( x * y * y ) );
+    return y;
 }