PPG analysis beyond HR at rest

Posted on
of 4
First Prev
/ 4
  • I modified the test code a bit, and it seems that the HRM event always gets a higher BPM than the manual counting:

    var M = g.getWidth() / 2;
    var yr, yg, yc, yd;
    let beats = [];
    Bangle.on("HRM-raw", (hrm) => {
      g.scroll(0, 1);
      g.setPixel(M, 0, "#ff0");
      g.setColor(hrm.adjusted ? "#000" : "#0F0").fillRect(
        (yg = hrm.raw / 32),
      g.setColor("#F00").fillRect(yr, 0, (yr = M + hrm.filt / 256 - 30), 0);
      g.setColor("#F0F").fillRect(yc, 0, (yc = M + (hrm.raw - hrm.avg) / 128 + 30), 0);
      if (hrm.isBeat) {
        g.fillRect(yr - 4, 0, yr + 4, 0);
    setInterval(() => {
      beats = beats.filter(b => b > Date.now() - 60 * 1000);
      console.log(beats.length, "BPM");
    }, 1000);
    Bangle.on("HRM", (hrm) => console.log("OFFICIAL:", hrm.bpm, "BPM", hrm.confidence));

    Any idea why?

  • How much higher?

    I guess it's possible that it is missing an HRM-raw event every so often? And which one do you think is correct?

    But the HRM code works by taking all the times between the beats and median-filtering them, so while there could be something wrong with that calculation, the filtering will end up changing the value a bit.

    Code is here: https://github.com/espruino/Espruino/blo­b/master/libs/misc/heartrate.c#L290-L347­

  • HRM: 87 BPM (87% confidence)
    Counting beats: 65 BPM
    Google Fit (finger on camera): 95 BPM
    I suppose the HRM event is more correct, and doing filtering. I still don't know why the Health app claims my heart rate is 40 though.

  • Looking at the code there is a possibility that if it detected a second beat very close to the first one, it would drop it and then measure the time period between the second and third beat (rather than 1st and 3rd) - but in that case, counting beats should have given you an even higher value so I don't think that's it

    I still don't know why the Health app claims my heart rate is 40 though.

    No, that is very strange. The Health app should store what it considered was the latest, most accurate reading from the HRM. You could check what it thought the reading was for the current 10 minute block in the IDE with Bangle.getHealthStatus() or Bangle.getHealthStatus("last") for the last 10 minutes.

    It might help you figure out whether it was the health app that wasn't recording the data correctly, or the Bangle firmware that was deciding that the 'best' HRM value was 40

  • In another test it genuinely believed my heart rate was 51 bpm, with 100% confidence. Anyway, after looking at the source code of the Health app, I made a PR to update the HRM logic.

  • Had a look at the PR and will test if you post a link to your personal loader.

    I noticed that the key bit of code appears to be:

       if (h.confidence > 80 && Math.abs(Bangle.getHealthStatus().bpm - h.bpm) < 1) Bangle.setHRMPower(0, "health");

    IE turn off the HRM power if confidence is greater than 80 we are not getting heart rate values different to the recorded value.

    I wonder if the same change needs to be put into the clockInfo module ?

    The reason I say this is that I did not realise that the heart rate clock info will only update once when it is displayed for the first time. Only if you tap it, will it update. OR it will get updated periodically without inter-action if you have the health app installed as that will wake up and measure your heart rate more often.

    The confidence threshold level in the clock_info is greater than 60 will stop measuring. So this would make the very first reading displayed by the clock_info less accurate than subsequent events generated by the health app waking up the hrm. I appreciate in practice nobody will notice or care. This is just me trying to understand how the heart rate stuff works.

    Even with this PR though - its not tackling the HRT accuracy beyond resting heart rate. There is still the issue of the the heart rate slowing down on movement. I did try the Artifact Removal app but not sure if I could see it working or not. The guy that wrote that app is non contactable.


    I'd be interested to know if anyone has a description of how to show that the artifact removal app works or not.

  • That change wasn't to make sure it stayed stable. It was to make sure that the health bpm had updated, since it only updates when it gets a higher confidence than the last stored one.
    In order to test I just used the Espruino IDE.

  • On the watch itself a confidence=0 indicates hrmmar is active. Also in the console you can subscribe to the HRM event:

    Bangle.on('HRM', print)

    If you see a value in bpm_orig and confidence_orig hrmmar has replaced the firmware value with a value it thinks is not motion affected. For a confidence value of >= 90 the firmware value is displayed.

    Please note that the fft elimination needs 8 seconds of sensor data to compute its value on so it might not be suited for the health app. Maybe it is a good idea to disable hrmmar when health is requesting the hrm data, I did only run short tests with health but have not seen an issue here.

    Update: Because of alphabetically order health.boot.js should load before hrmmar.boot.js so health should ignore any modification of hrmmar.

  • so health should ignore any modification of hrmmar.

    But surely the point is to modify the heart rate detected by patching the code by installing the hrmmar app ?

    I dont see much point of installing an app that is meant to improve the heart rate detection only to find that it only works under experimental situations. The whole heart rate measurement thing is a bit confusing right now with different apps all tweaking things differently.

    I tried out the app and logged to the console as you suggested whilst sat on the sofa. I had been up and down the stairs a few times so my heart rate was around 80bpm. All the time I could see confidence = 0 the corrected bpm recorded in the logging was around 95-110, wheras the original value was actually closer to what it was ( I also used an AmazFit bip on the same wrist to check it).. So not improved but made worse. I could understand it not working well if I was actaully moving about a lot but I think the app is basically not working. I have attached the log.

    1 Attachment

  • I can't say for sure for your case but usually when sitting still the noise from the accelerometer is so high that the result of the fft is invalid. Personally I don't see this as a big issue because the firmware value is used when confidence is high. Which also only happens when the wrist is not moved.

    I've also seen cases in the same situation (sitting still after movement) where firmware reports about 40BPM, hrmmar says 100 and the real value was 80.

    However yes there is lots of room for improvement.

    But surely the point is to modify the heart rate detected by patching the code by installing the hrmmar app ?

    The usecase this targets are apps like Heart Rate Monitor , not Health.

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview

PPG analysis beyond HR at rest

Posted by Avatar for Mi @Mi