You are reading a single comment by @Gordon and its replies. Click here to read the full conversation.
  • It's worth looking at the actual C code as it's documented a lot better there (and also it's slightly different now), but here are my comments on the JS:

    // values for debug plotting
    var lastAccel = 120;      // BLUE
    var lastAccelFilt = 120;  // YELLOW
    var lastThresh = 120;     // RED
    
    // question: where do these values come from ? what does tap mean ?
    // question: is this a low pass 3Hz filter ?
    // GW If you look at the actual C code there's a link to where the filter comes from and what it is: https://github.com/espruino/Espruino/blo­b/master/libs/misc/stepcount.c
    // GW Basically it keeps 1.3-2.5Hz which is what stuff online seems to think is a normal step rate.
    var filter_taps = new Int8Array([ -2, 4, 4, 1, -1, 0, 2, -3, -12, -13, 2, 24, 29, 6, -25, -33, -13, 10, 11, -1, 3, 29, 41, 4, -62, -89, -34, 62, 110, 62, -34, -89, -62, 4, 41, 29, 3, -1, 11, 10, -13, -33, -25, 6, 29, 24, 2, -13, -12, -3, 2, 0, -1, 1, 4, 4, -2 ]);
    // create a history buffer and populate it with the tap values ?
    // GW The history just needs to be the same length as the filter. It's not pre-populated
    var history = new Int8Array(filter_taps.length);
    
    // what units is this in ? 
    // GW: It's not in any in particular - it's what comes out the filter
    const stepCounterThresholdMin = 1500;
    const stepCounterAvr = 1;
    /// Theshold in filtered acceleration for detecting a step
    var stepCounterThreshold = stepCounterThresholdMin;
    /// has filtered acceleration passed stepCounterThresholdLow?
    var stepWasLow = false;
    function onAccel(a) {
      // question - onAccel is event driven, so events could be irregular, papers seem to suggest 20Hz sampling frequency
      // will this event driven approach provide regular sampling of approx 20Hz or will it be subject to CPU performance etc
    //GW it's 12.5Hz as that's what comes out the accelerometer - it will be regular, yes.
      // scale to fit and clip
      var v = ((a.mag-1)*8192)>>5;
      // create a new Int8Array from the existing but starting from pos 1 of the existing history
      // this drops off index 0 and moves everything up, leaving the last entry to be filled
      // with the new value
      history.set(new Int8Array(history.buffer,1));
      // set last value to the clipped value from the accel
      history[history.length-1] = E.clip(v, -128, 127);
      console.log("history");
      console.log(history);
      
      // do filtering
      //  what is convolution ??
      //https://homepages.inf.ed.ac.uk/rbf/HIP­R2/convolve.htm#:~:text=Convolution%20pr­ovides%20a%20way%20of,numbers%20of%20the­%20same%20dimensionality.&text=In%20an%2­0image%20processing%20context,normally%2­0just%20a%20graylevel%20image.
      // question: - is this a low pass filter at 3Hz to get rid of noise ?
    // GW: answered this above
      // question: - why is accFiltered much larger than v , why >>2 to make it bigger ? 
    // GW: The filter amplifies it because we're doing everything in integer arithmetic not floats (for speed in C code)
      var accFiltered = E.convolve(filter_taps, history, 0) >> 2;
      console.log("accFiltered");
      console.log(accFiltered);
      
      /*
      // Simple average-based threshold
      if (stepCounterAvr) {
        var a = accFiltered;
        if (a<0) a=-a;
        stepCounterThreshold = (stepCounterThreshold*(32-stepCounterAvr­) + a*stepCounterAvr) >> 5;
        if (stepCounterThreshold < stepCounterThresholdMin)
          stepCounterThreshold = stepCounterThresholdMin;
      }*/
      
      // Set threshold based on the 'middle' history item - the one making the big spikes.
      // Try and scale it appropriately
      /*
        question:  why would middle history item represent a big spike ?
        the history starts out blank and samples shuffle along in a FIFO manner
        initially history[32] will be 0 until the 32nd accel event has been captured ?
        question: having filtered why are we using the raw data again and not a scalled form of accFiltered ?
       */
    // GW: Check out that filter design website. It might make a bit more sense when you see the shape of the filter.
    // GW: Personally I'd ignore this code - adjusting the threshold never actually worked that well - a fixed threshold seemed to be better
      
      var a = history[32] * 55;
      if (a<0) a=-a;
      if (a > stepCounterThreshold) 
        stepCounterThreshold = a;//(stepCounterThreshold+a) >> 1;
      
      stepCounterThreshold -= 48;  // question: why -= 48 ??
      
      if (stepCounterThreshold < stepCounterThresholdMin)
        stepCounterThreshold = stepCounterThresholdMin;  
      
      // check for steps, a bottom, followed by top threshold crossing = a step
      var hadStep = false;
      if (accFiltered < -stepCounterThreshold) 
        stepWasLow = true;
      else if ((accFiltered > stepCounterThreshold) && stepWasLow) {
        stepWasLow = false;
        hadStep = true;
      }
      // output data
      g.scroll(0,1);
      var n;
      // question: cyan markers on the edge of the screen mean that
      // we crossed the threshold ??
      // GW:  yes
      if (accFiltered < -stepCounterThreshold)
        g.setColor("#0ff").fillRect(0,0,8,0);
      if (accFiltered > stepCounterThreshold)
        g.setColor("#0ff").fillRect(232,0,240,0)­;
      
      n = 120+v;
      g.setColor("#00f").fillRect(lastAccel,0,­n,0);
      lastAccel = n;
      n = 120+(accFiltered>>6);
      g.setColor("#ff0").fillRect(lastAccelFil­t,0,n,0);
      lastAccelFilt = n;
      n = 120+(stepCounterThreshold>>6);
      g.setColor("#f00").fillRect(lastThresh,0­,n,0);
      lastThresh = n;
      if (hadStep) {
        g.setColor(-1).drawString("STEP",60,0);
      }
    }
    Bangle.on('accel',onAccel);
    Bangle.setLCDTimeout(0);
    

    It's a shame the new step counting hasn't worked out - is it substantially worse than it was before, or only marginally better?

    I guess making a widget is a good idea, although it won't be great for battery. You could actually run multiple widgets at once I guess and see which ones gave better values.

    I wouldn't add your widget to the main app store though - the last thing we need is another slightly different pedometer widget to confuse people. If you can get the step counting substantially better then we can look at pulling those changes back into the Bangle.js firmware

About

Avatar for Gordon @Gordon started