Pages

Sunday 30 April 2017

Adding Fitbit HR data to a Garmin TCX file

Earlier this month I talked about Running Watches on a Budget and how I had got my hands on an old Garmin Forerunner 405 for not very much on eBay.  This is working well for me especially now I have sorted out mobile data uploads, but I'm always looking to see what else I can do.  The two immediately obvious things I am missing are heart rate data and cadence.

I've had a Fitbit Charge HR through work for a while, and this tracks steps (obviously) and being the HR model also uses optical wrist based heart rate monitoring, as featured on some more recent high end Garmin watches.  There is a Fitbit Developer API so I started to have a look and see what was available.  Registering an app is easy, so I get set up with an account and explored the endpoints.  The Get Activity Intraday Time Series endpoint appeared to have what I wanted, so time to choose the technology.

Initially I started looking at Python and the Ruby on Rails, with the idea being that I could make use of existing libraries and minimise the amount of code I have to write myself, but I soon found myself staring down blind alleys with no idea how to proceed, so I fell back to my old faithful approach of hacking together PHP in a very inelegant fashion.  Before too long and with a bit of help from OAuth2-Fitbit I could authenticate with the API and make requests.

My first experiments were with cadence, but I found this was unreliable as although the 1 minute resolution SHOULD be sufficient for steps per minute this data appeared to be smoothed over much longer periods of time, roughly corresponding to the duration of an activity as far as Fitbit is concerned. This effectively meant that you cadence was shown as a flat line for the duration of your run at a rate of (total steps)/(duration) - useless for running purposes.  Heartrate data looked a lot more promising though, and I was soon able to return an array of heartrate data against time for a given activity window.

The next step then was to upload a TCX file and extract the start and end time of activities in order to know what requests to make.  Parsing XML is not something I particularly enjoy, but using SimpleXML I was able to iterate through the nested structure of Activities -> Activity > Lap[] -> Track and build up a list of trackpoint timestamps, then I take the first and the last, convert to unix timestamps, add 60 seconds to the last one so as not to miss any data from the last minute of the activity, and then reconvert to the format required by the API - a separate date, then start and end times in HH:MM. One thing which tripped me up was that the API returns data in local time whereas the TCX file works in GMT - I only spotted this when I went for a 12k run and the first hour had me at a resting heart rate...

The resolution of the heart rate data is less granular than that of the TCX file, so in order to combine them I again use SimpleXML to step through each Trackpoint, and when I get to the trackpoint I find the nearest heart rate datapoint for that time using some code shamelessly found on Stack Overflow.  It was here I hit a slight complication as I need to add a new HeartRateBpm element and in the world of SimpleXML all you can do is append an element, you cannot choose where to insert it.  The various systems using TCX are very particular about where this element appears, as it must come after Time, Position, Altitude and Distance but before Extensions. Only Time is mandatory, and from analysing TCX files from my watch some or all of the others may well be missing.  This involved getting in to the world of DOMElement to be able to specify the element after which Heart Rate should be inserted  - itself based on a series of tests to see which statements are and are not present for a given trackpoint.

With that in place I now have a web page where I can upload a TCX file and my script will read it and add in the heart rate data then presenting it for download, where I can manually add it to Garmin Connect and have it propagate to Strava and Endomondo. My post run workflow now goes as follows:

  • Sync Fitbit and make sure it has captured a running activity with Heart Rate Data
  • Download TCX from my watch using Sportablet Uploader
  • Upload to my web page
  • Download new file and add it to Garmin Connect via their website
It's a bit cumbersome, but it's bearable.  I'd love a newer watch with built in Heartrate, Cadence and low power Bluetooth sync, but for now this works for me.



No comments:

Post a Comment