I took a look at this and I have to give up now because I'm out of ideas. A few papers have code online, e.g. WPFV . I tried them on data collected on my Bangle JS2, but they all failed to recover the exact heart rate, or even a value close to it.
The following picture shows the frequencies in BPM of a random 8 second sample of motion artifacts in a PPG signal from the IEEE Signal Processing Cup 2015 , dataset 1, downsampled to 25Hz:
We can simply see that the first and second dominant PPG peeks (blue) at about 90 BPM and 176 BPM are caused by motion because we can identify the spikes from the acceleration sensor at the same frequency. That leaves the third spike at 165 BPM as the real BPM which closely matches the ground truth at 163 BPM.
Lets now look at the data I have recorded from my Bangle JS2:
We can see the motion introduced spike on 70BPM. The problem is that at the real heart rate at about 110BPM we do not see any value. Second highest peak is at 123 BPM which is also overlapped by x acceleration.
I think we now get an idea why the algorithms fail. Now lets take a look at the raw signal. First ieee:
Now Bangle Js2, raw signal in blue, FIR filtered in red, both zero averaged:
Even at the no-motion-phase at the beginning the signal appears more noisy compared to the more clean one from the ieee dataset. I'm not a signal processing or PPG sensor export, but at this point I assume the Bangle JS ppg sensor is not of high enough quality.
The BangleJS2 data has been recorded with acc rate=25Hz, hrm rate=50Hz, both sampled at 25Hz. See the attached recordppg.js. Though the sampling rate is not exact (more ~23Hz on mine)
© Espruino, powered by microcosm.
Report a problem