If it’s not on Strava, it did not happen.
Introduction
Long story short: I used my Wahoo Elmnt Bolt for a run, since my phone was almost dead, but it auto-paused as I left the house. However: it appears it nevertheless kept recording my position and heart rate but this data didnt contribute to the recorded activity.

I initially hoped Strava would auto-fix this when uploading but no such luck. I also tried https://fitfiletools.com in hopes of that it somehow also could unmangle this mess, also without luck.
Recovering the data:
Scouring the web for .fit
file tools, to inspect and fix them, initially returned no promising results. But in this moment as I am writing this up I just found out that there exists as fit-csv tool for converting .fit into .csv files (https://developer.garmin.com/fit/fitcsvtool/windows/) which may have made the following steps easier.
But there also exist several python libraries for working with these files:
python-fitparse
also ships with a fitdump
tool for dumping out all information that is inside the file.
Inspecting with the following code only returned the initial and last data points:
import fitparsefitfile = fitparse.FitFile("a.fit")
for record in fitfile.get_messages("event"):
for data in record:
if data.units:
print(" * {}: {} ({})".format(data.name, data.value, data.units))
else:
print(" * {}: {}".format(data.name, data.value))
Output:
* altitude: 398.6 (m)
* battery_soc: 35.0 (percent)
* calories: 0 (kcal)
* distance: 0.0 (m)
* enhanced_altitude: 398.6 (m)
* position_lat: XXXXXX(semicircles)
* position_long: XXXXXX(semicircles)
* timestamp: 2021-01-07 13:44:29
---
* altitude: 398.6 (m)
* battery_soc: 35.0 (percent)
* calories: 0 (kcal)
* distance: 0.0 (m)
* enhanced_altitude: 398.6 (m)
* grade: 1.03 (%)
* heart_rate: 120 (bpm)
* position_lat: XXXX(semicircles)
* position_long: XXXXX(semicircles)
* timestamp: 2021-01-07 13:44:30
---[...]---
* calories: 0 (kcal)
* distance: 0.0 (m)
* timestamp: 2021-01-07 14:22:32
---
Using the fitdump tool returned much more data, around 2370 data points, which gave me more hope since my run was ~38:00 minutes long, (38 * 60 = 2280)
Output of fitdump:
[...]87. unknown_65280
* ascent: 0 [m]
* unknown_0: XXXXXXX
* unknown_1: XXXXXX
* unknown_13: 21
* unknown_2: 4499
* unknown_253: 978961484
* unknown_3: 125
* unknown_32: 47
* unknown_33: 0
* unknown_5: 0
* unknown_6: 3018
* unknown_9: 300[...]
Now the challenge was finding out what field stood for what, rearranging the data, converting the data back into a fit file and uploading to strava.
unknown_0
and unknown_1
looked like location data (which I censored it from here) but much more interestingly they were not given in floating point layout but rather were integer data points.
That is because they were given in “semicircles”. (https://en.wikipedia.org/wiki/Semicircle)
The formula for the conversion from “semicircles” into floating point representation is given here:
def convert_semicircle(i):
return (i * 180) / math.pow(2,31)
The other fields where of not much interest; except for heartrate which turned out to be unknown_3
and temperature which was unknown_13
.
I am not exactly sure what the other fields were for as I still had my bike power meter paired, so I expect the fields for power and cadence to exist but filled with zeros.
I cleaned up and rearranged the data with VSCode and many regex’s I ended up with this:
[0-9]*. unknown_[0-9]*\n( \* [a-z_0-9]*: [0-9]*\n)*
Above is an example regex for detecting a valid data recording point in the fitdump output, apart from the device_info and other irrelevant fields.
Also very handy was the grouping feature in the regex engine of vscode.
([0-9]*),([0-9.]*),([0-9.]*),([0-9.]*),([0-9.]*)
If an entry matches the expression above, you can rearrange the different comma separated fields in the text, with the following search and replace formula:
$1 $3 $2 $4 $5
This formula, when executed would swap the second and third entry for every entry that it matches.
Quick sidenote: I like to think that using vscode here like this saved me from learning many obscure linux command line tools with manuals as big as me when printed.
After everything was done we were left with the following csv file:
secs,lat,long,temp,hr
0,48.XXXX,9.XXXX,22,120
1,48.XXXX,9.XXXX,22,120
2,48.XXXX,9.XXXX,22,120
[...]
Now we had everything to construct out correct fit file.
I noticed that GoldenCheetah (https://github.com/GoldenCheetah/GoldenCheetah/) supports file import and export of csv and fit files.
After initially failing to import a valid file I exported another run to see what fields GC wanted to be populated and in which way.
Here is what GC wanted (shown already with the valid padded zero entries):
secs,cad,hr,km,kph,nm,watts,alt,lon,lat,headwind,slope,temp,interval,lrbalance,lte,rte,lps,rps,smo2,thb,o2hb,hhbsecs,cad,hr,km,kph,nm,watts,alt,lon,lat,headwind,slope,temp,interval,lrbalance,lte,rte,lps,rps,smo2,thb,o2hb,hhb
0,0,120,0,0,0,0,0,48.X,9.X,0,0,22,0,0,0,0,0,0,0,0,0,0
1,0,120,0,0,0,0,0,48.X,9.X,0,0,22,0,0,0,0,0,0,0,0,0,0
This time I could import the activity no problem:

Ergo the export also worked.
After uploading I was almost heartbroken when I saw that the data for the running cadence (which I havent even filled) and temperature were broken in the strava analysis view (jumping all over the place, I dont know why) but quickly were adjusted to the correct values.

Conclusion
Thanks for reading so far.
Even Confucius knew if it didnt happen on Strava it didnt happen. Fixing this mangled fit file took the better part of my afternoon that day but I’m happy that I managed to unfuck this mess.
Drop me a kudos and follow me on strava or instagram if you enjoyed this:
Strava Cyclist Profile | Wilhelm B.
Join Wilhelm and get inspired for your next workout
www.strava.com
https://www.instagram.com/wbuchmueller/
Also if you shop (running shoes or bike stuff for example) on amazon.de you can use my referal link, which gives me a little kickback at no extra cost to you.