jimmykane / fit-parser Goto Github PK
View Code? Open in Web Editor NEWThis project forked from pierremtb/easy-fit
Parse your FIT files easily, directly from JS (Garmin, Polar, Suunto)
License: Other
This project forked from pierremtb/easy-fit
Parse your FIT files easily, directly from JS (Garmin, Polar, Suunto)
License: Other
Hi again
Just spoke with Garmin:
Elena Kononova (Garmin Health)
May 1, 17:29 CESTOk,
Seems that we have inconsistent naming, it should be renamed into device id...I see from my internal page that your 'device id' is 3996, which is different from serial number
I think to change that the fit.js should be modified here?
3: { field: 'serial_number', type: 'uint32z', scale: null, offset: '', units: '' },
to this:
3: { field: 'device_id', type: 'uint32z', scale: null, offset: '', units: '' },
I think this is what should be shown:
"file_id": {
"device_id": 3996,
"time_created": "2020-04-30T22:16:48.000Z",
"manufacturer": "garmin",
"product": 3113,
"type": "activity"
},
I submitted issues more than 28 days ago and received a response that the author is a new dad and time is scarce... but will try...
finding a different avenue of parsing now.
Why do you exclude SRC from npm published package ?
It would be useful if we have ES6, for modern browsers and node.js available.
Thanx
Hi again,
I am successfully using the parser now ! (wow... such a learning curve on Java)
Anyway, I downloaded an activity, parsed it and got some results. However, I think the FR945 is not in the listing of products
I assume 'product' in the following output is going to show fr945 ??
"file_id": {
"serial_number": 3996694396,
"time_created": "2020-04-30T22:16:48.000Z",
"manufacturer": "garmin",
"product": 3113,
"type": "activity"
},
I see a 'product' like category in fit.js that contains an fr920xt, an edge 280 (both of which I own), the fr935 and fenix5x (both of which I know are watches)... but I don't see the fr945 ?
garmin_product: {
0: 'hrm_bike',
1: 'hrm1',
<snip>
1765: 'fr920xt',
<snip>
2530: 'edge_820',
<snip>
2604: 'fenix5x',
2606: 'vivo_fit_jr',
2691: 'fr935',
I tried putting this code in and re-running the parser, but it did not pick it up:
2859: 'descent',
3113: 'fr945',
10007: 'sdm4',
Hi,
I just did a "triathlon" and ran it through the parser,
The fit-file-parser thinks the sport name is a "Run" and activity type of 'auto_multi-sport':
\"fileId_type\": \"activity\",
\"sport_Name\": \"Run\",
\"sport_Sport\": \"running\",
\"sport_SubSport\": \"generic\",
\"activity_Type\": \"auto_multi_sport\"
Garmin thinks it is a multi-sport
I did a swim, a bike and run for comparison
here is what I get for just a swim:
\"fileId_type\": \"activity\",
\"sport_Name\": \"Open Water\",
\"sport_Sport\": \"swimming\",
\"sport_SubSport\": \"open_water\",
\"activity_Type\": \"manual\"
here is what I get for just a bike:
\"fileId_type\": \"activity\",
\"sport_Name\": \"Bike\",
\"sport_Sport\": \"cycling\",
\"sport_SubSport\": \"generic\",
\"activity_Type\": \"manual\"
here is what I get for just a run:
\"fileId_type\": \"activity\",
\"sport_Name\": \"Run\",
\"sport_Sport\": \"running\",
\"sport_SubSport\": \"generic\",
\"activity_Type\": \"manual\"
Here is the FIT file for the multi-sport activity
4958669219.zip
HEllo,
first, thanks for your job.
I have a problem with the data like left_power_phase, avg_left_power_phase. Your script return NaN or nulll.
With an other script in python I get this data: (347.3437554272462, 201.0937531420899, 212.34375331787115, 94.218751472168). Data type is uint8
I didn't succeed to understand how this script work for bugfixes that. If you have an explanation ....
Regards
Hi,
I have a sample FIT that is producing generally good results but clearly bad speed data. I have multiple trackers of the same route and the enhanced_speed seems to have a scale or offset problem, as most values are rounded to 1, but slow real speeds result in values near 0.
If necessary I can provide the sample privately.
The FIT sdk documentation describes how to apply scale and offset to fields.
the binary quantity is divided by the scale factor and then the offset is subtracted, yielding a floating point quantity
compare D00001275 Flexible & Interoperable Data Transfer (FIT) Protocol Rev 2.3.pdf, section 4.4
In binary.js, you use data / scale + offset
(adding offset instead of subtracting). Is this correct? I could not test it, because my fit file doesn't contain any fields that use offsets.
Hello,
thanks for this parser.
The switch (messageType) { } is missing the case 'set' for Garmin Strength workouts, so the records do not show up in the parsed file / laps.
Could you add it?
Hi again,
Please forgive my ignorance if this is supposed to behave this way, but I am comparing the output from the 'cascade' option and the 'list' option and noticed a discrepancy between the two. The 'list' version has multiple device_info records and the cascade does not have any?
Here is the full output from both:
Cascade
{
"file_id": {
"device_id": 3996694396,
"time_created": "2020-04-30T22:16:48.000Z",
"manufacturer": "garmin",
"product": 3113,
"type": "activity"
},
"file_creator": {
"software_version": 440
},
"device_settings": {
"utc_offset": 0,
"time_offset": 4294942096,
"active_time_zone": 0,
"time_zone_offset": 0,
"mounting_side": "left"
},
"user_profile": {
"weight": 74.8,
"gender": "male",
"height": 0.00183,
"language": "english",
"elev_setting": "statute",
"weight_setting": "statute",
"resting_heart_rate": 0,
"hr_setting": "max",
"speed_setting": "statute",
"dist_setting": "statute",
"position_setting": "degree_minute_second",
"temperature_setting": "statute",
"height_setting": "statute"
},
"sport": {
"name": "Run",
"sport": "running",
"sub_sport": "generic"
},
"zones_target": {
"functional_threshold_power": 207,
"max_heart_rate": 180,
"threshold_heart_rate": 167,
"pwr_calc_type": "percent_ftp"
},
"activity": {
"timestamp": "2020-04-30T22:17:03.000Z",
"total_timer_time": 9.199,
"local_timestamp": "2020-04-30T15:17:03.000Z",
"num_sessions": 1,
"type": "manual",
"event": "activity",
"event_type": "stop",
"sessions": [
{
"timestamp": "2020-04-30T22:17:03.000Z",
"start_time": "2020-04-30T22:16:48.000Z",
"start_position_lat": 37.711845925077796,
"start_position_long": -121.72617522999644,
"total_elapsed_time": 9.199,
"total_timer_time": 9.199,
"total_distance": 0,
"nec_lat": 37.711845925077796,
"nec_long": -121.726168775931,
"swc_lat": 37.711771074682474,
"swc_long": -121.72637580893934,
"enhanced_avg_speed": 0,
"enhanced_max_speed": 0,
"message_index": {
"0": false,
"value": 0,
"reserved": false,
"selected": false
},
"total_calories": 0,
"total_ascent": 0,
"total_descent": 0,
"first_lap_index": 0,
"num_laps": 1,
"left_right_balance": {
"0": false,
"value": 16383,
"right": true
},
"event": "lap",
"event_type": "stop",
"sport": "running",
"sub_sport": "generic",
"trigger": "activity_end",
"avg_temperature": -246.14999999999998,
"max_temperature": -246.14999999999998,
"laps": [
{
"timestamp": "2020-04-30T22:17:03.000Z",
"start_time": "2020-04-30T22:16:48.000Z",
"start_position_lat": 37.711845925077796,
"start_position_long": -121.72617522999644,
"end_position_lat": 37.711771074682474,
"end_position_long": -121.72633968293667,
"total_elapsed_time": 9.199,
"total_timer_time": 9.199,
"total_distance": 0,
"total_cycles": 0,
"enhanced_avg_speed": 0,
"enhanced_max_speed": 0,
"message_index": {
"0": false,
"value": 0,
"reserved": false,
"selected": false
},
"total_calories": 0,
"total_ascent": 0,
"total_descent": 0,
"left_right_balance": {
"0": false,
"value": 16383,
"right": true
},
"wkt_step_index": {
"0": false,
"value": 4095,
"reserved": true,
"selected": true
},
"event": "lap",
"event_type": "stop",
"avg_cadence": 0,
"max_cadence": 0,
"lap_trigger": "session_end",
"sport": "running",
"sub_sport": "generic",
"avg_temperature": -246.14999999999998,
"max_temperature": -246.14999999999998,
"avg_fractional_cadence": 0,
"max_fractional_cadence": 0,
"records": [
{
"timestamp": "2020-04-30T22:16:48.000Z",
"elapsed_time": 0,
"timer_time": 0,
"position_lat": 37.711845925077796,
"position_long": -121.72617522999644,
"distance": 0,
"enhanced_speed": 0,
"enhanced_altitude": 0.1624,
"cadence": 0,
"temperature": -246.14999999999998,
"fractional_cadence": 0
},
{
"timestamp": "2020-04-30T22:16:54.000Z",
"elapsed_time": 6,
"timer_time": 6,
"position_lat": 37.71178691647947,
"position_long": -121.72637220472097,
"distance": 0,
"enhanced_speed": 0,
"enhanced_altitude": 0.163,
"cadence": 0,
"temperature": -246.14999999999998,
"fractional_cadence": 0
},
{
"timestamp": "2020-04-30T22:16:58.000Z",
"elapsed_time": 10,
"timer_time": 10,
"position_lat": 37.711771074682474,
"position_long": -121.72633968293667,
"distance": 0,
"enhanced_speed": 0,
"enhanced_altitude": 0.163,
"cadence": 0,
"temperature": -246.14999999999998,
"fractional_cadence": 0
}
]
}
]
}
],
"events": [
{
"timestamp": "2020-04-30T22:16:48.000Z",
"data": 0,
"event": "timer",
"event_type": "start",
"event_group": 0
},
{
"timestamp": "2020-04-30T22:16:58.000Z",
"data": 0,
"event": "timer",
"event_type": "stop_all",
"event_group": 0
},
{
"timestamp": "2020-04-30T22:17:02.000Z",
"data": 207,
"event_type": "marker",
"event_group": 1
}
],
"hrv": []
}
}
List
{
"file_id": {
"device_id": 3996694396,
"time_created": "2020-04-30T22:16:48.000Z",
"manufacturer": "garmin",
"product": 3113,
"type": "activity"
},
"file_creator": {
"software_version": 440
},
"device_settings": {
"utc_offset": 0,
"time_offset": 4294942096,
"active_time_zone": 0,
"time_zone_offset": 0,
"mounting_side": "left"
},
"user_profile": {
"weight": 74.8,
"gender": "male",
"height": 0.00183,
"language": "english",
"elev_setting": "statute",
"weight_setting": "statute",
"resting_heart_rate": 0,
"hr_setting": "max",
"speed_setting": "statute",
"dist_setting": "statute",
"position_setting": "degree_minute_second",
"temperature_setting": "statute",
"height_setting": "statute"
},
"sport": {
"name": "Run",
"sport": "running",
"sub_sport": "generic"
},
"zones_target": {
"functional_threshold_power": 207,
"max_heart_rate": 180,
"threshold_heart_rate": 167,
"pwr_calc_type": "percent_ftp"
},
"activity": {
"timestamp": "2020-04-30T22:17:03.000Z",
"total_timer_time": 9.199,
"local_timestamp": "2020-04-30T15:17:03.000Z",
"num_sessions": 1,
"type": "manual",
"event": "activity",
"event_type": "stop"
},
"sessions": [
{
"timestamp": "2020-04-30T22:17:03.000Z",
"start_time": "2020-04-30T22:16:48.000Z",
"start_position_lat": 37.711845925077796,
"start_position_long": -121.72617522999644,
"total_elapsed_time": 9.199,
"total_timer_time": 9.199,
"total_distance": 0,
"nec_lat": 37.711845925077796,
"nec_long": -121.726168775931,
"swc_lat": 37.711771074682474,
"swc_long": -121.72637580893934,
"enhanced_avg_speed": 0,
"enhanced_max_speed": 0,
"message_index": {
"0": false,
"value": 0,
"reserved": false,
"selected": false
},
"total_calories": 0,
"total_ascent": 0,
"total_descent": 0,
"first_lap_index": 0,
"num_laps": 1,
"left_right_balance": {
"0": false,
"value": 16383,
"right": true
},
"event": "lap",
"event_type": "stop",
"sport": "running",
"sub_sport": "generic",
"trigger": "activity_end",
"avg_temperature": -246.14999999999998,
"max_temperature": -246.14999999999998
}
],
"laps": [
{
"timestamp": "2020-04-30T22:17:03.000Z",
"start_time": "2020-04-30T22:16:48.000Z",
"start_position_lat": 37.711845925077796,
"start_position_long": -121.72617522999644,
"end_position_lat": 37.711771074682474,
"end_position_long": -121.72633968293667,
"total_elapsed_time": 9.199,
"total_timer_time": 9.199,
"total_distance": 0,
"total_cycles": 0,
"enhanced_avg_speed": 0,
"enhanced_max_speed": 0,
"message_index": {
"0": false,
"value": 0,
"reserved": false,
"selected": false
},
"total_calories": 0,
"total_ascent": 0,
"total_descent": 0,
"left_right_balance": {
"0": false,
"value": 16383,
"right": true
},
"wkt_step_index": {
"0": false,
"value": 4095,
"reserved": true,
"selected": true
},
"event": "lap",
"event_type": "stop",
"avg_cadence": 0,
"max_cadence": 0,
"lap_trigger": "session_end",
"sport": "running",
"sub_sport": "generic",
"avg_temperature": -246.14999999999998,
"max_temperature": -246.14999999999998,
"avg_fractional_cadence": 0,
"max_fractional_cadence": 0
}
],
"records": [
{
"timestamp": "2020-04-30T22:16:48.000Z",
"elapsed_time": 0,
"timer_time": 0,
"position_lat": 37.711845925077796,
"position_long": -121.72617522999644,
"distance": 0,
"enhanced_speed": 0,
"enhanced_altitude": 0.1624,
"cadence": 0,
"temperature": -246.14999999999998,
"fractional_cadence": 0
},
{
"timestamp": "2020-04-30T22:16:54.000Z",
"elapsed_time": 6,
"timer_time": 6,
"position_lat": 37.71178691647947,
"position_long": -121.72637220472097,
"distance": 0,
"enhanced_speed": 0,
"enhanced_altitude": 0.163,
"cadence": 0,
"temperature": -246.14999999999998,
"fractional_cadence": 0
},
{
"timestamp": "2020-04-30T22:16:58.000Z",
"elapsed_time": 10,
"timer_time": 10,
"position_lat": 37.711771074682474,
"position_long": -121.72633968293667,
"distance": 0,
"enhanced_speed": 0,
"enhanced_altitude": 0.163,
"cadence": 0,
"temperature": -246.14999999999998,
"fractional_cadence": 0
}
],
"events": [
{
"timestamp": "2020-04-30T22:16:48.000Z",
"data": 0,
"event": "timer",
"event_type": "start",
"event_group": 0
},
{
"timestamp": "2020-04-30T22:16:58.000Z",
"data": 0,
"event": "timer",
"event_type": "stop_all",
"event_group": 0
},
{
"timestamp": "2020-04-30T22:17:02.000Z",
"data": 207,
"event_type": "marker",
"event_group": 1
}
],
"device_infos": [
{
"timestamp": "2020-04-30T22:16:48.000Z",
"serial_number": 3996694396,
"manufacturer": "garmin",
"product": 3113,
"software_version": 4.4,
"device_index": 0,
"source_type": "local"
},
{
"timestamp": "2020-04-30T22:16:48.000Z",
"manufacturer": "garmin",
"product": 3113,
"software_version": 4.4,
"device_index": 1,
"source_type": "local"
},
{
"timestamp": "2020-04-30T22:16:48.000Z",
"manufacturer": "garmin",
"product": 3107,
"software_version": 4.1,
"device_index": 2,
"device_type": 0,
"source_type": "local"
},
{
"timestamp": "2020-04-30T22:16:48.000Z",
"product": 8195,
"software_version": 0,
"device_index": 3,
"source_type": "local"
},
{
"timestamp": "2020-04-30T22:16:48.000Z",
"manufacturer": 0,
"product": 0,
"software_version": 1.28,
"device_index": 4,
"source_type": "local"
},
{
"timestamp": "2020-04-30T22:16:48.000Z",
"manufacturer": "garmin",
"product": 3114,
"software_version": 4.01,
"device_index": 5,
"device_type": "environment_sensor_legacy",
"source_type": "local"
},
{
"timestamp": "2020-04-30T22:16:58.000Z",
"serial_number": 3996694396,
"manufacturer": "garmin",
"product": 3113,
"software_version": 4.4,
"device_index": 0,
"source_type": "local"
},
{
"timestamp": "2020-04-30T22:16:58.000Z",
"manufacturer": "garmin",
"product": 3113,
"software_version": 4.4,
"device_index": 1,
"source_type": "local"
},
{
"timestamp": "2020-04-30T22:16:58.000Z",
"manufacturer": "garmin",
"product": 3107,
"software_version": 4.1,
"device_index": 2,
"device_type": 0,
"source_type": "local"
},
{
"timestamp": "2020-04-30T22:16:58.000Z",
"product": 8195,
"software_version": 0,
"device_index": 3,
"source_type": "local"
},
{
"timestamp": "2020-04-30T22:16:58.000Z",
"product": 28745,
"software_version": 1.28,
"device_index": 4,
"source_type": "local"
},
{
"timestamp": "2020-04-30T22:16:58.000Z",
"manufacturer": "garmin",
"product": 3114,
"software_version": 4.01,
"device_index": 5,
"device_type": "environment_sensor_legacy",
"source_type": "local"
}
],
"developer_data_ids": [],
"field_descriptions": [],
"hrv": [],
"dive_gases": [],
"course_points": []
}
It would be really useful if type definition files were added to the project so that TypeScript consumers can more easily access functions and classes.
I think the fit file format has the temp in celsius. So the calculation to Fahrenheit is not correct...
Is there any place where the fields are explained? Most are pretty self-explanitory, but a bunch are not. For example, while it is obvious the starting location for a session/lap there are two possibilities for the ending location (swc
or nec
). No idea what those are...
Are you aware of any documentation anywhere where these fields are explained?
Hey @jimmykane,
I'm looking to understand why every cycling dynamics stats like avg_left_power_phase
, avg_left_power_phase_peak
are returning NaN
values inside this library (file used: 7386755164.zip).
1 - I noticed that in the npm distributed package (v1.9.0) the following file node_modules\fit-file-parser\dist\binary.js
has an error. Indeed inside function readData
the following line:
return dataView.getUnt8(0, fDef.littleEndian);
Should be
return dataView.getUint8(0, fDef.littleEndian);
getUnt8
function doesn't exists, the getUint8
yes !
2 - Aside this, the readData
(dist folder) from repository is not the same than the npm package. Why? I was unable to find tag 1.9.0 on this repository.
Thanks for your help !
Thomas
Hello,
Thanks for this great fit-parser!
I sometimes get the following error for Garmin files (probably from a Garmin 935 watch):
TypeError: Cannot read property 'globalMessageNumber' of undefined
2019-10-19 15:02: at readRecord (/node_modules/fit-file-parser/dist/binary.js:324:60)
2019-10-19 15:02: at FitParser.parse (/node_modules/fit-file-parser/dist/fit-parser.js:111:50)
It corresponds to the following line:
var message = (0, _messages.getFitMessage)(messageType.globalMessageNumber);
Any idea on how to solve it?
Best,
Hi, congratulations for the module. Really useful.
I have a file shot in 2019 where the records show timestamps from 1989, like "1989-12-31T00:00:06.000Z"
Could this be a problem in the parsing process? Or is the file corrupt in some way?
I can share the file privately if you can have a look at it. Or if you can provide instructions for debugging this I will follow them on my end.
Thank you.
After the 1s December, Garmin will save the files with the "summary first" method instead of the "summary last". Using their example file, I don't seem to be able to get the data inside the sessions? Are the files using the summary first method supported?
Line 1285 in 9425b8d
Importing a paragliding activity from Suunto to qualified-self results in a hang-gliding activity.
Either Suunto exports it with the wrong sport (which is 26) or the mapping is incorrect.
If so then the issue should be fixed by the following change:
fit.js:1285 26: 'hang_gliding',
is supposed to be 26: 'paragliding',
At least Suunto does not support hang gliding as it is not that popular any more but there is a test case that depends on it being hang gliding.
For people not familiar with the sports:
are you sure this folder shell be part of distributed npm package ?
why ?
Hi,
I am trying to use your fit file parser and ran into a little issue
I am hoping you will be able to shed some light on.
I am using the garmin health-api and downloading a FIT file directly through the REST api. Here is an example call
https://healthapi.garmin.com/wellness-api/rest/activityFile?id=86711045091
Of course there is a bunch of stuff (oAuth, headers and callbacks) that needs to occur before you can download the fit file, but rest assured, I have worked with Garmin to get all of the "rest of the stuff" hammered out and I am getting FIT file data from the REST api.
Here is what the fit file data looks like when I view it in my debugger:
Here is the code where I am using your object to directly parse the downloaded data from the REST call:
const internalParse = async (data) => {
// Create a FitParser instance (options argument is optional)
var fitParser = new FitParser({
force: true,
speedUnit: 'km/h',
lengthUnit: 'km',
temperatureUnit: 'kelvin',
elapsedRecordField: true,
mode: 'cascade',
});
var error = null;
var data = null;
// Parse your file
try{
fitParser.parse(data, function (error, data){
When I try to read this data using your object I get the following error:
Incorrect header size
Missing '.FIT' in header
My question to you:
Is there a way I can simply pass that data to your object for parsing? or do I need to do something to it before your object will accept it?
I have tried this fit-file-parser, but it seems to return default fields.
My FIT activity contains other field that have not been parsed.
Should I modified the options in the var fitParser = new FitParser({});
?
Where can I find these options? Should I contact the device manufacturer to know some variable names?
In my case, I am using the Nielsen-Kellerman Speedcoach 2 which retrieve GPS data, Splits/500m, stoke rate and data from the NK empower: Catch angle, slip angle, Force Max, ForceMax Angle, etc... Approx 13 different metrics.
Kind regards,
Sylvain
Each negative temperature from my fit files are parsed as 255. (Using celsius)
Hi, I was wondering whether you are planing to implement encode for fit files from the js object ? I Your parser is the best I could find (I tried a lot :)) but I would like to save the changes back to an actual fit file after editing.
thanks
When parsing any file generated from my PC8 everything seems to work, except the left_right_balance field which returns as undefined.
Not sure why this would be the case but the data is definitely in there as it can be parsed by Golden Cheetah correctly and it is displayed on the PC8.
Seems to be the same issue in the original lib pierremtb#31
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.