I am trying to calculate HRV (Heart rate variability) from ANT+ of my Garmin Vivosmart 5 watch on Android. Garmin does not output HRV directly, and instead uses proprietary stress level which is seemingly related, but not the same number as HRV. I tried EliteHRV app, however it told me that my garmin is inaccurate, and produced a lot of mis-detections, so I decided to write my own, trying to address issues with the way the data is collected. Is code below the correct way of deriving HRV from ANT+ data?
override fun subscribeToEvents(pcc: AntPlusHeartRatePcc) {
pcc.subscribeHeartRateDataEvent { estTimestamp, _, computedHeartRate, heartBeatCount , sBeatTime , _ ->
val device = getDevice(pcc)
device.hr = computedHeartRate
device.hrTimestamp = estTimestamp
device.rr = -1.0;
device.hrv = -1.0;
var tdiff = device.hrTimestamp - previousTime; // detect connection loss
if (tdiff < 500 && (heartBeatCount - previousBeat) > 0L) {
//Section 5.3.6.3 https://err.no/tmp/ANT_Device_Profile_Heart_Rate_Monitor.pdf
var rr = (sBeatTime - sPreviousBeatTime).toDouble() / (heartBeatCount - previousBeat) * 1000.0;
var maxrrvals = 40; //stat window size
while (rrVals.size >= maxrrvals){
rrVals.removeFirst()
}
rrVals.add(rr)
device.rr = rr;
if (rrVals.size == maxrrvals) {
// From https://downloads.hindawi.com/journals/mse/2012/931943.pdf
// https://stackoverflow.com/questions/24624039/how-to-get-hrv-heart-rate-variability-from-hr-heart-rate
var rmssdTotal : Double = 0.0;
var rmssdCount : Int = 0
for (i in 1 until rrVals.size) {
var diff = rrVals[i] - rrVals[i-1]
//remove train of consecutive identical values
if (diff == 0.0) {
continue
}
rmssdCount += 1
rmssdTotal += (diff).pow(2.0)
}
if (rmssdCount >= maxrrvals * 0.25) { //ensure we have some stats
var rmssd = kotlin.math.sqrt(rmssdTotal / rmssdCount)
// from https://help.elitehrv.com/article/54-how-do-you-calculate-the-hrv-score
device.hrv = kotlin.math.ln(rmssd) / 6.5 * 100.0
}
}
}
previousTime = device.hrTimestamp;
previousBeat = heartBeatCount
sPreviousBeatTime = sBeatTime
listener.onDataUpdated(device)
}
}