src/sensor-fusion/complementary-filter.js
function ComplementaryFilter(kFilter, isDebug) {
  this.kFilter = kFilter;
  this.isDebug = isDebug;

  // Raw sensor measurements.
  this.currentAccelMeasurement = new SensorSample();
  this.currentGyroMeasurement = new SensorSample();
  this.previousGyroMeasurement = new SensorSample();

  // Set default look direction to be in the correct direction.
  if (Util.isIOS()) {
    this.filterQ = new MathUtil.Quaternion(-1, 0, 0, 1);
  } else {
    this.filterQ = new MathUtil.Quaternion(1, 0, 0, 1);
  }
  this.previousFilterQ = new MathUtil.Quaternion();
  this.previousFilterQ.copy(this.filterQ);

  // Orientation based on the accelerometer.
  this.accelQ = new MathUtil.Quaternion();
  // Whether or not the orientation has been initialized.
  this.isOrientationInitialized = false;
  // Running estimate of gravity based on the current orientation.
  this.estimatedGravity = new MathUtil.Vector3();
  // Measured gravity based on accelerometer.
  this.measuredGravity = new MathUtil.Vector3();

  // Debug only quaternion of gyro-based orientation.
  this.gyroIntegralQ = new MathUtil.Quaternion();
}
function ComplementaryFilter(kFilter, isDebug) {
  this.kFilter = kFilter;
  this.isDebug = isDebug;

  // Raw sensor measurements.
  this.currentAccelMeasurement = new SensorSample();
  this.currentGyroMeasurement = new SensorSample();
  this.previousGyroMeasurement = new SensorSample();

  // Set default look direction to be in the correct direction.
  if (Util.isIOS()) {
    this.filterQ = new MathUtil.Quaternion(-1, 0, 0, 1);
  } else {
    this.filterQ = new MathUtil.Quaternion(1, 0, 0, 1);
  }
  this.previousFilterQ = new MathUtil.Quaternion();
  this.previousFilterQ.copy(this.filterQ);

  // Orientation based on the accelerometer.
  this.accelQ = new MathUtil.Quaternion();
  // Whether or not the orientation has been initialized.
  this.isOrientationInitialized = false;
  // Running estimate of gravity based on the current orientation.
  this.estimatedGravity = new MathUtil.Vector3();
  // Measured gravity based on accelerometer.
  this.measuredGravity = new MathUtil.Vector3();

  // Debug only quaternion of gyro-based orientation.
  this.gyroIntegralQ = new MathUtil.Quaternion();
}
function ComplementaryFilter ( kFilter , isDebug ) { this . kFilter = kFilter ; this . isDebug = isDebug ; // Raw sensor measurements. this . currentAccelMeasurement = new SensorSample ( ) ; this . currentGyroMeasurement = new SensorSample ( ) ; this . previousGyroMeasurement = new SensorSample ( ) ; // Set default look direction to be in the correct direction. if ( Util . isIOS ( ) ) { this . filterQ = new MathUtil . Quaternion ( - 1 , 0 , 0 , 1 ) ; } else { this . filterQ = new MathUtil . Quaternion ( 1 , 0 , 0 , 1 ) ; } this . previousFilterQ = new MathUtil . Quaternion ( ) ; this . previousFilterQ . copy ( this . filterQ ) ; // Orientation based on the accelerometer. this . accelQ = new MathUtil . Quaternion ( ) ; // Whether or not the orientation has been initialized. this . isOrientationInitialized = false ; // Running estimate of gravity based on the current orientation. this . estimatedGravity = new MathUtil . Vector3 ( ) ; // Measured gravity based on accelerometer. this . measuredGravity = new MathUtil . Vector3 ( ) ; // Debug only quaternion of gyro-based orientation. this . gyroIntegralQ = new MathUtil . Quaternion ( ) ; }
An implementation of a simple complementary filter, which fuses gyroscope and
accelerometer data from the 'devicemotion' event.

Accelerometer data is very noisy, but stable over the long term.
Gyroscope data is smooth, but tends to drift over the long term.

This fusion is relatively simple:
1. Get orientation estimates from accelerometer by applying a low-pass filter
on that data.
2. Get orientation estimates from gyroscope by integrating over time.
3. Combine the two estimates, weighing (1) in the long term, but (2) for the
short term.
An implementation of a simple complementary filter which fuses gyroscope and accelerometer data from the devicemotion event .