wouterbulten/slacjs

View on GitHub
src/app/device/pedometer.js

Summary

Maintainability
A
0 mins
Test Coverage
import KalmanFilter from '../util/kalman';

/**
 * Accelerometer based pedometer
 *
 * Based on a FirefoxOS ES5 implementation.
 *
 * @see http://sebastien.menigot.free.fr/index.php?view=article&id=93
 * @see http://www.analog.com/library/analogdialogue/archives/44-06/pedometer.html
 */
class Pedometer {

    constructor(updateRate) {

        const windowSize = Math.round(2 / (updateRate / 1000));

        this.accNorm = new Array(windowSize); // amplitude of the acceleration

        this.varAcc   = 0.0; // variance of the acceleration on the window L
        this.minAcc   = 1.0;  // minimum of the acceleration on the window L
        this.maxAcc   = -Infinity; // maximum of the acceleration on the window L
        this.threshold = -Infinity; // threshold to detect a step
        this.sensitivity = 1.0 / 30.0;  // sensitivity to detect a step

        this.stepCount = 0;           // number of steps
        this.stepArr   = new Array(windowSize); // steps in 2 seconds

        this.updateRate = updateRate; //Update rate in ms

        this.filter = new KalmanFilter();

        //Callback to run after a new step
        this.callbackOnStep = undefined;
    }

    /**
     * Process a new accelerometer measurement
     * @param  {Number} x
     * @param  {Number} y
     * @param  {Number} z
     * @return {void}
     */
    processMeasurement(x, y, z) {

        const norm = this._computeNorm(x, y, z);

        this.accNorm.push(norm);
        this.accNorm.shift();

        this._stepDetection();
    }

    /**
     * Register a callback function to run on a new step
     * @param  {Function} callback
     * @return {void}
     */
    onStep(callback) {
        this.callbackOnStep = callback;
    }

    /**
     * Detect whether the user has done a step
     * @return {void}
     */
    _stepDetection() {

        this._computeAccelerationVariance();
        this.minAcc = Math.min.apply(null, this.accNorm);
        this.maxAcc = Math.max.apply(null, this.accNorm);

        this.threshold = (this.minAcc + this.maxAcc) / 2;

        const diff = this.maxAcc - this.minAcc;

        if (

            //Sensiblity, the difference must increase the sensitivity
            Math.abs(diff) >= this.sensitivity &&

            //Acceleration must be above the threshold, and the previous one below (i.e. a new step)
            (this.accNorm[this.accNorm.length - 1] >= this.threshold) &&
            (this.accNorm[this.accNorm.length - 2] < this.threshold) &&

            (this.stepArr[this.stepArr.length - 1] === 0)
        ) {
            this.stepCount++;
            this.stepArr.push(1);
            this.stepArr.shift();

            if (this.callbackOnStep !== undefined) {
                this.callbackOnStep();
            }
        }
        else {
            this.stepArr.push(0);
            this.stepArr.shift();
        }
    }

    /**
     * Compute the norm of the acceleration vector
     * @param  {Number} x
     * @param  {Number} y
     * @param  {Number} z
     * @return {Number} norm of the vector
     */
    _computeNorm(x, y, z) {
        const norm = Math.sqrt((x * x) + (y * y) + (z * z));
        const filteredNorm = this.filter.filter(norm);

        return filteredNorm / 9.80665;
    }

    /**
     * Compute the variance of the acceleration norm vector
     * @return {void}
     */
    _computeAccelerationVariance() {
        let mean  = 0.0;
        let mean2 = 0.0;

        for (var k = 0; k < this.accNorm.length - 1; k++) {
            mean += this.accNorm[k];
            mean2 += this.accNorm[k] * this.accNorm[k];
        }

        this.varAcc = ((mean * mean) - mean2) / this.accNorm.length;

        if ((this.varAcc - 0.5) > 0.0) {
            this.varAcc -= 0.5;
        }

        if (!isNaN(this.varAcc)) {
            this.filter.setMeasurementNoise(this.varAcc);
            this.sensitivity = 2.0 * (Math.sqrt(this.varAcc) / (9.80665 * 9.80665));
        }
        else {
            this.sensitivity = 1.0 / 30.0;
        }
    }
}

export default Pedometer;