tool idea: calculate/inject respiration rate into FIT when using HRV on older watches

For some reason Garmin has chosen not to back-port the respiration rate feature into watches like the 935/Fenix 5 series, even when you use their (expensive) supported HRM strap like HRM-TRI

They even own firstbeat now so licensing cost isn't the issue, they just don't want to bother even with porting the widget.

But the data of course is still there if you have HRV turned on and the appropriate FIT field has to be there since Garmin uses it.

I was thinking if clever developers can make Garmin apps like "HRV Test" then it should be possible to look at HRV data in a FIT file and find the R-to-R peaks to calculate respiration rate?

If it helps anyone, this is Garmin's explainer on respiration and supported devices/straps:

support.garmin.com/.../

  • Check this out, Oura was nice enough to map out how respiratory rate is calculated from HRV, I was searching various white-papers but they've got it down exactly:

    https://d1a0efioav7lro.cloudfront.net/wp-content/uploads/2020/05/12195157/How-Oura-calculates-respiratory-rate.jpg

    Now to figure out how to turn the series of HRV numbers in FIT files into interbeat intervals (IBIs) and then if they are increasing or decreasing.

    This might take awhile.

    Looks like Garmin is putting respiration rate into all their devices now, even the Lily has it but not backporting like the Vivosmart4 does not (but VS4 has HRV internally even with wrist HR but you have to be still like sleeping)

    I figured it can't be too incredibly hard to do this when my $15 pulse oximeter can do it.

  • I need help finding a publicly shared FIT file with a Fenix6/945 plus HRM-PRO/TRI/RUN so I can examine its respiratory rate for comparison.

    I looked at DC Rainmaker's HRM-PRO review

    https://www.dcrainmaker.com/2020/09/garmin-hrm-pro-chest-strap-depth-review.html

    and while he shows the garmin-connect page with respiration rate https://media.dcrainmaker.com/images/2020/09/image-62.png

    none of his data sets posted use the Fenix6+HRM-Pro so there is no respiration data in there?

    https://analyze.dcrainmaker.com/#/public/5b83de04-ab22-4552-5c90-a76613cb62bc

    So if anyone has a FIT with such data they are willing to share that would help me a lot, can't afford a fenix6/945 to dev

    adding: hmm, well I found some FIT files on his site with fenix6-hrm-dual BUT as far as I can tell, despite HRV data in the file, respiration rate is not saved into the FIT ? No mysterious fields that match.

    Is the dirty secret that Garmin doesn't save it into the file and calculates it afterwards when uploaded to garmin-connect from the HRV data? But it's shown on the watch during activity as a datafield option right? So it must be calculated there too by firstbeat.

    This means they could offer it on ANY upload from ANY watch model with HRV data, including Fenix5 or going even further back.

    But they choose not to. It's a marketing decision. Wow, not cool.

  • quick and dirty first attempt. can it really be this simple? I am not so sure, but the rates seem to match dcrainmaker somewhat

    this is not fine-grained rolling seconds average yet, just breaking off the count every time the IBI total goes over a minute, there has to be a sequential "breathe in" + "breathe out" for it to count as a total breath and there has to be at least two increasing or decreasing in a row or I throw it away as noise or incomplete data since the HRM belt seems to be imperfect in the data, there are spikes and dropouts in the IBI (no way a breath is over 2 seconds running)

    1: 33
    2: 30
    3: 18  (dropout from poor belt contact in cold dry winter initially?)
    4: 25
    5: 14  (too low to be right?)
    6: 23
    7: 13  (too low to be right?)
    8: 20
    9: 19
    10: 24
    11: 30    (consistent after 10 minutes)
    12: 31
    13: 39
    14: 48
    15: 38
    16: 39
    17: 37
    18: 40
    19: 40
    20: 37  (etc.)

    		$ibi0=reset($hrv); $minutes=1; $minutetimer=0; $breathein=0; $breatheout=0; $breaths=0;
    		foreach ($hrv as $ibi) {
    			if ($ibi>$ibi0) { $breatheout++; if ($breathein>1) { $breaths++; }  $breathein=0; }
    			elseif ($ibi<$ibi0) { $breathein++;  if ($breatheout>1) { $breaths++; } $breatheout=0; }
    			$ibi0=$ibi;
    			$minutetimer+=$ibi; 			
    			if ($minutetimer>=60000) { echo "$minutes: $breaths  \n"; $breaths=0; $minutetimer=0; $minutes++; }
    		}		
    

  • this might be a better approach, after putting it aside for the day it dawned on me instead of counting increase/decrease cycles I am not using the important data of the cumulative time for the breathe in/out waves like the Oura guide shows, this more closely resembles what they suggest, I added a filter for intervals that are too impossibly short or too long

    I also changed it to mimic Oura that a full cycle IN then OUT is a breath

    This comes up with around 40 breaths per minute aftr the first 10 minutes which seems plausible after the HRM is contacting better and I use either a 3 or 5 breathing pattern which has two short breaths out and double-long breathe in.

    Just wish I had garmin-connect graph to compare it to.

    		$ibi0=reset($hrv); $minutes=1; $minutetimer=0; $breathein=0; $breatheout=0; $breaths=0;
    		foreach ($hrv as $ibi) {
    			if ($ibi>250 && $ibi<1500) {  // bad data filter, HR would be <40 or >240
    				if ($ibi<$ibi0) { $breathein+=$ibi; if ($breatheout>250 && $breatheout<3000) { $breaths++; } $breatheout=0; }  
    				elseif ($ibi>$ibi0) { $breatheout+=$ibi; $breathein=0; }
    			}
    			$minutetimer+=$ibi; $ibi0=$ibi;
    			if ($minutetimer>=60000) { echo "$minutes: $breaths  \n"; $breaths=0; $minutetimer=0; $minutes++; }
    		}
    

    1: 41
    2: 41
    3: 21  (bad HRM contact first 10 minutes?)
    4: 28  (etc)
    5: 28  (etc)
    6: 26  (etc)
    7: 12  (!)
    8: 29  (etc)
    9: 37  (starting to work)
    10: 40 (steady from here)
    11: 43
    12: 48
    13: 43
    14: 39
    15: 41
    16: 41
    17: 40

  • copying this from another thread I posted in so I don't lose track of what I said on HRV

    I am (attempting) calculating respiration rate from HRV data, which is only present when using HRM strap and HRV logging is turned on in the watch

    That is how Garmin calculates it and unless I am mistaken, they don't bother to save the respiration rate itself in the FIT files even on supported watches but rather calculate it in realtime on garmin-connect

    So they could activate that feature for any watch like Fenix5 but refuse to, probably to make people buy new watches every year but it's silly that lowest cost newer models have features the Fenix5 does not when it was the flagship until a couple years ago

    Yes I am using the method OURA shared which basically made it far easier to figure out

    The HRV data is found as developer fields appended to the type 20 records as message id 78, looks like this

    hrv (78, type: 0, length: 11 bytes):
      time (0-5-UINT16): {0.434 s (434), 0.438 s (438), 0.432 s (432), 65535, 65535}

    You have to gather all of them and them you have the sequence of timing between heartbeats for the entire activity, I believe that's called RR intervals or interbeat intervals  https://en.wikipedia.org/wiki/Interbeat_interval

    Then you can do a lot of interesting calculations based on what we know about how the heart works during certain body behaviors. Respiration rate is just the start, there is a new exciting paper with open source code on how to identify the Lactate Threshold and aerobic threshold (LT1 LT2) from HRV data which I am investigating how to code myself but might take awhile:

    https://colab.research.google.com/drive/1GUZVjZGhc2_JqV-J5m1mgbvbiTBV9WzZ

    Oh there is another place you can get the HRV data if you have a HRM-TRI or HRM-PRO, if you use the "download heart rate data" after the activity on the watch, it then will directly append (chain) to the FIT file the HRV data directly from the strap so any lost ANT+ communications (dropouts) are bypassed which is nice. Then there is a special FIT file called HRDATA.FIT found in 

    \GARMIN\TEMPFIT\SAF_HR\HRDATA.FIT

    however that HRV is different than the main activity file (and that file is overwritten every time you download it, so make a different copy if it's important)

    that data looks like

    hr (132, type: 11, length: 21 bytes):
      filtered_bpm (6-8-UINT8): {57 bpm (57), 56 bpm (56), 57 bpm (57), 57 bpm (57), 57 bpm (57), 57 bpm (57), 57 bpm (57), 56 bpm (56)}
      event_timestamp_12 (10-12-BYTE): {0.219 s (224), 0.146 s (150), 0.174 s (178), 0.083 s (85), 0.140 s (143), 0.053 s (54), 0.164 s (168), 0.226 s (231), 0.186 s (190), 0.061 s (62), 0.016 s (16), 0.072 s (74)}

    what's weird is it's not a direct integer if you look carefully, there's some kind of scaler I haven't calculated yet, ie "74" = "0.72" or "224" = "0.219" which is odd

  • there is a new exciting paper with open source code on how to identify the Lactate Threshold and aerobic threshold (LT1 LT2) from HRV data which I am investigating how to code myself but might take awhile:

    https://colab.research.google.com/drive/1GUZVjZGhc2_JqV-J5m1mgbvbiTBV9WzZ

    AH..yes.. this is the same exact place I'm looking at for the DFA a1 data as well which is what piqued my interest and led me here. I"m still wrapping my head around the code and especially on how actually to use the colab.

    The HRV data is found as developer fields appended to the type 20 records as message id 78, looks like this

    hrv (78, type: 0, length: 11 bytes):
      time (0-5-UINT16): {0.434 s (434), 0.438 s (438), 0.432 s (432), 65535, 65535}

    I don't get it. Y is this in developer data field? the HRV data is populated into the FIT file (at least for the newer watches anyways) are you talking about injecting it as developer data field for older watches then? I think for this to be done, you need to direct connect to the HRM itself instead of using whatever Garmin sends out as info.activity (CIQ). Also, BT is supposedly better as well.

  • ah correction, it's not developer fields, it's regular global message id 78

    array of 16bit unsigned integers where 65535 is invalid/no data

    you don't connect to the HRM, you might be thinking of an app and not the FIT sdk

    with HRM-TRI or HRM-PRO you can also directly download the HRV data afterwards through the watch, so the ANT+ vs bluetooth issue is moot, you get the complete dataset that way but it's more complicated and unique to those models, see the TEMPFIT info I posted above

    if you are trying to write a realtime app then you want the CIQ sub-forum, not the FIT sdk sub-forum, FIT would be for processing afterwards

  • ah correction, it's not developer fields, it's regular global message id 78

    yes..

    if you are trying to write a realtime app then you want the CIQ sub-forum, not the FIT sdk sub-forum, FIT would be for processing afterwards

         FIT File HRV Data Array Interpretation    

    First would be for me to understand the hrv data (as per above thread) and now that I do, then comes to understanding the colab.

    thanks!