fit-file-parser 2.3.3 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/binary.js CHANGED
@@ -190,6 +190,13 @@ function isInvalidValue(data, type) {
190
190
  return false;
191
191
  }
192
192
  }
193
+ function isInvalidBaseTypeValue(data, baseTypeNo) {
194
+ if (Array.isArray(data)) {
195
+ return false;
196
+ }
197
+ const baseType = FIT.types.fit_base_type[baseTypeNo];
198
+ return typeof baseType === 'string' ? isInvalidValue(data, baseType) : false;
199
+ }
193
200
  function convertTo(data, unitsList, unitName) {
194
201
  const options = FIT.options[unitsList];
195
202
  const unit = options[unitName];
@@ -266,6 +273,7 @@ function applyOptions(data, field, options, fields) {
266
273
  }
267
274
  }
268
275
  export function readRecord(blob, messageTypes, developerFields, startIndex, options, startDate, pausedTime) {
276
+ var _a, _b;
269
277
  const recordHeader = blob[startIndex];
270
278
  let localMessageType = recordHeader & 15;
271
279
  if ((recordHeader & CompressedHeaderMask) === CompressedHeaderMask) {
@@ -301,7 +309,7 @@ export function readRecord(blob, messageTypes, developerFields, startIndex, opti
301
309
  size: blob[fDefIndex + 1],
302
310
  endianAbility: (baseType & 128) === 128,
303
311
  littleEndian: lEnd,
304
- baseTypeNo: baseType & 15,
312
+ baseTypeNo: baseType,
305
313
  name: field,
306
314
  dataType: getFitMessageBaseType(baseType & 15),
307
315
  };
@@ -323,7 +331,7 @@ export function readRecord(blob, messageTypes, developerFields, startIndex, opti
323
331
  size,
324
332
  endianAbility: (baseType & 128) === 128,
325
333
  littleEndian: lEnd,
326
- baseTypeNo: baseType & 15,
334
+ baseTypeNo: baseType,
327
335
  name: devDef.field_name,
328
336
  dataType: getFitMessageBaseType(baseType & 15),
329
337
  scale: devDef.scale || 1,
@@ -359,7 +367,7 @@ export function readRecord(blob, messageTypes, developerFields, startIndex, opti
359
367
  for (let i = 0; i < messageType.fieldDefs.length; i++) {
360
368
  const fDef = messageType.fieldDefs[i];
361
369
  const data = readData(blob, fDef, readDataFromIndex, options);
362
- if (!isInvalidValue(data, fDef.type)) {
370
+ if (!isInvalidValue(data, fDef.type) && !isInvalidBaseTypeValue(data, fDef.baseTypeNo)) {
363
371
  rawFields.push({ fDef, data });
364
372
  }
365
373
  readDataFromIndex += fDef.size;
@@ -375,8 +383,8 @@ export function readRecord(blob, messageTypes, developerFields, startIndex, opti
375
383
  if (fDef.isDeveloperField) {
376
384
  const field = fDef.name;
377
385
  const { type } = fDef;
378
- const { scale } = fDef;
379
- const { offset } = fDef;
386
+ const scale = (_a = fDef.scale) !== null && _a !== void 0 ? _a : null;
387
+ const offset = (_b = fDef.offset) !== null && _b !== void 0 ? _b : 0;
380
388
  fields[fDef.name] = applyOptions(formatByType(data, type, scale, offset), field, options, fields);
381
389
  }
382
390
  else {
@@ -196,6 +196,13 @@ function isInvalidValue(data, type) {
196
196
  return false;
197
197
  }
198
198
  }
199
+ function isInvalidBaseTypeValue(data, baseTypeNo) {
200
+ if (Array.isArray(data)) {
201
+ return false;
202
+ }
203
+ const baseType = fit_js_1.FIT.types.fit_base_type[baseTypeNo];
204
+ return typeof baseType === 'string' ? isInvalidValue(data, baseType) : false;
205
+ }
199
206
  function convertTo(data, unitsList, unitName) {
200
207
  const options = fit_js_1.FIT.options[unitsList];
201
208
  const unit = options[unitName];
@@ -272,6 +279,7 @@ function applyOptions(data, field, options, fields) {
272
279
  }
273
280
  }
274
281
  function readRecord(blob, messageTypes, developerFields, startIndex, options, startDate, pausedTime) {
282
+ var _a, _b;
275
283
  const recordHeader = blob[startIndex];
276
284
  let localMessageType = recordHeader & 15;
277
285
  if ((recordHeader & CompressedHeaderMask) === CompressedHeaderMask) {
@@ -307,7 +315,7 @@ function readRecord(blob, messageTypes, developerFields, startIndex, options, st
307
315
  size: blob[fDefIndex + 1],
308
316
  endianAbility: (baseType & 128) === 128,
309
317
  littleEndian: lEnd,
310
- baseTypeNo: baseType & 15,
318
+ baseTypeNo: baseType,
311
319
  name: field,
312
320
  dataType: (0, messages_js_1.getFitMessageBaseType)(baseType & 15),
313
321
  };
@@ -329,7 +337,7 @@ function readRecord(blob, messageTypes, developerFields, startIndex, options, st
329
337
  size,
330
338
  endianAbility: (baseType & 128) === 128,
331
339
  littleEndian: lEnd,
332
- baseTypeNo: baseType & 15,
340
+ baseTypeNo: baseType,
333
341
  name: devDef.field_name,
334
342
  dataType: (0, messages_js_1.getFitMessageBaseType)(baseType & 15),
335
343
  scale: devDef.scale || 1,
@@ -365,7 +373,7 @@ function readRecord(blob, messageTypes, developerFields, startIndex, options, st
365
373
  for (let i = 0; i < messageType.fieldDefs.length; i++) {
366
374
  const fDef = messageType.fieldDefs[i];
367
375
  const data = readData(blob, fDef, readDataFromIndex, options);
368
- if (!isInvalidValue(data, fDef.type)) {
376
+ if (!isInvalidValue(data, fDef.type) && !isInvalidBaseTypeValue(data, fDef.baseTypeNo)) {
369
377
  rawFields.push({ fDef, data });
370
378
  }
371
379
  readDataFromIndex += fDef.size;
@@ -381,8 +389,8 @@ function readRecord(blob, messageTypes, developerFields, startIndex, options, st
381
389
  if (fDef.isDeveloperField) {
382
390
  const field = fDef.name;
383
391
  const { type } = fDef;
384
- const { scale } = fDef;
385
- const { offset } = fDef;
392
+ const scale = (_a = fDef.scale) !== null && _a !== void 0 ? _a : null;
393
+ const offset = (_b = fDef.offset) !== null && _b !== void 0 ? _b : 0;
386
394
  fields[fDef.name] = applyOptions(formatByType(data, type, scale, offset), field, options, fields);
387
395
  }
388
396
  else {
@@ -102,8 +102,11 @@ class FitParser {
102
102
  const tank_updates = [];
103
103
  const tank_summaries = [];
104
104
  const jumps = [];
105
+ const splits = [];
106
+ const split_summaries = [];
105
107
  const time_in_zone = [];
106
108
  const activity_metrics = [];
109
+ const user_metrics = [];
107
110
  let loopIndex = headerLength;
108
111
  const messageTypes = [];
109
112
  const developerFields = [];
@@ -202,12 +205,21 @@ class FitParser {
202
205
  case 'jump':
203
206
  jumps.push(message);
204
207
  break;
208
+ case 'split':
209
+ splits.push(message);
210
+ break;
211
+ case 'split_summary':
212
+ split_summaries.push(message);
213
+ break;
205
214
  case 'time_in_zone':
206
215
  time_in_zone.push(message);
207
216
  break;
208
217
  case 'activity_metrics':
209
218
  activity_metrics.push(message);
210
219
  break;
220
+ case 'user_metrics':
221
+ user_metrics.push(message);
222
+ break;
211
223
  default:
212
224
  if (messageType !== '') {
213
225
  fitObj[messageType] = message;
@@ -228,8 +240,11 @@ class FitParser {
228
240
  fitObj.tank_updates = tank_updates;
229
241
  fitObj.tank_summaries = tank_summaries;
230
242
  fitObj.jumps = jumps;
243
+ fitObj.splits = splits;
244
+ fitObj.split_summaries = split_summaries;
231
245
  fitObj.time_in_zone = time_in_zone;
232
246
  fitObj.activity_metrics = activity_metrics;
247
+ fitObj.user_metrics = user_metrics;
233
248
  if (isCascadeNeeded) {
234
249
  laps = (0, helper_js_1.mapDataIntoLap)(laps, 'records', records);
235
250
  laps = (0, helper_js_1.mapDataIntoLap)(laps, 'lengths', lengths);
@@ -238,7 +253,9 @@ class FitParser {
238
253
  sessions,
239
254
  events,
240
255
  hrv,
241
- device_infos, developer_data_ids: applications, field_descriptions: fieldDescriptions, sports });
256
+ device_infos, developer_data_ids: applications, field_descriptions: fieldDescriptions, sports,
257
+ splits,
258
+ split_summaries });
242
259
  }
243
260
  if (!isModeCascade) {
244
261
  fitObj.sessions = sessions;
package/dist/cjs/fit.js CHANGED
@@ -1776,6 +1776,27 @@ exports.FIT = {
1776
1776
  offset: 0,
1777
1777
  units: 'kcal',
1778
1778
  },
1779
+ 205: {
1780
+ field: 'beginning_potential_stamina',
1781
+ type: 'uint8',
1782
+ scale: 1,
1783
+ offset: 0,
1784
+ units: 'percent',
1785
+ },
1786
+ 206: {
1787
+ field: 'ending_potential_stamina',
1788
+ type: 'uint8',
1789
+ scale: 1,
1790
+ offset: 0,
1791
+ units: 'percent',
1792
+ },
1793
+ 207: {
1794
+ field: 'min_stamina',
1795
+ type: 'uint8',
1796
+ scale: 1,
1797
+ offset: 0,
1798
+ units: 'percent',
1799
+ },
1779
1800
  214: {
1780
1801
  field: 'avg_grit',
1781
1802
  type: 'float32',
@@ -3004,6 +3025,292 @@ exports.FIT = {
3004
3025
  offset: 0,
3005
3026
  units: 'percent',
3006
3027
  },
3028
+ 137: {
3029
+ field: 'potential_stamina',
3030
+ type: 'uint8',
3031
+ scale: 1,
3032
+ offset: 0,
3033
+ units: 'percent',
3034
+ },
3035
+ 138: {
3036
+ field: 'stamina',
3037
+ type: 'uint8',
3038
+ scale: 1,
3039
+ offset: 0,
3040
+ units: 'percent',
3041
+ },
3042
+ },
3043
+ 312: {
3044
+ name: 'split',
3045
+ 254: {
3046
+ field: 'message_index',
3047
+ type: 'message_index',
3048
+ scale: null,
3049
+ offset: 0,
3050
+ units: '',
3051
+ },
3052
+ 0: {
3053
+ field: 'split_type',
3054
+ type: 'split_type',
3055
+ scale: null,
3056
+ offset: 0,
3057
+ units: '',
3058
+ },
3059
+ 1: {
3060
+ field: 'total_elapsed_time',
3061
+ type: 'uint32',
3062
+ scale: 1000,
3063
+ offset: 0,
3064
+ units: 's',
3065
+ },
3066
+ 2: {
3067
+ field: 'total_timer_time',
3068
+ type: 'uint32',
3069
+ scale: 1000,
3070
+ offset: 0,
3071
+ units: 's',
3072
+ },
3073
+ 3: {
3074
+ field: 'total_distance',
3075
+ type: 'uint32',
3076
+ scale: 100,
3077
+ offset: 0,
3078
+ units: 'm',
3079
+ },
3080
+ 4: {
3081
+ field: 'avg_speed',
3082
+ type: 'uint32',
3083
+ scale: 1000,
3084
+ offset: 0,
3085
+ units: 'm/s',
3086
+ },
3087
+ 9: {
3088
+ field: 'start_time',
3089
+ type: 'date_time',
3090
+ scale: null,
3091
+ offset: 0,
3092
+ units: '',
3093
+ },
3094
+ 13: {
3095
+ field: 'total_ascent',
3096
+ type: 'uint16',
3097
+ scale: null,
3098
+ offset: 0,
3099
+ units: 'm',
3100
+ },
3101
+ 14: {
3102
+ field: 'total_descent',
3103
+ type: 'uint16',
3104
+ scale: null,
3105
+ offset: 0,
3106
+ units: 'm',
3107
+ },
3108
+ 21: {
3109
+ field: 'start_position_lat',
3110
+ type: 'sint32',
3111
+ scale: null,
3112
+ offset: 0,
3113
+ units: 'semicircles',
3114
+ },
3115
+ 22: {
3116
+ field: 'start_position_long',
3117
+ type: 'sint32',
3118
+ scale: null,
3119
+ offset: 0,
3120
+ units: 'semicircles',
3121
+ },
3122
+ 23: {
3123
+ field: 'end_position_lat',
3124
+ type: 'sint32',
3125
+ scale: null,
3126
+ offset: 0,
3127
+ units: 'semicircles',
3128
+ },
3129
+ 24: {
3130
+ field: 'end_position_long',
3131
+ type: 'sint32',
3132
+ scale: null,
3133
+ offset: 0,
3134
+ units: 'semicircles',
3135
+ },
3136
+ 25: {
3137
+ field: 'max_speed',
3138
+ type: 'uint32',
3139
+ scale: 1000,
3140
+ offset: 0,
3141
+ units: 'm/s',
3142
+ },
3143
+ 26: {
3144
+ field: 'avg_vert_speed',
3145
+ type: 'sint32',
3146
+ scale: 1000,
3147
+ offset: 0,
3148
+ units: 'm/s',
3149
+ },
3150
+ 27: {
3151
+ field: 'end_time',
3152
+ type: 'date_time',
3153
+ scale: null,
3154
+ offset: 0,
3155
+ units: '',
3156
+ },
3157
+ 28: {
3158
+ field: 'total_calories',
3159
+ type: 'uint32',
3160
+ scale: null,
3161
+ offset: 0,
3162
+ units: 'kcal',
3163
+ },
3164
+ 74: {
3165
+ field: 'start_elevation',
3166
+ type: 'uint32',
3167
+ scale: 5,
3168
+ offset: -500,
3169
+ units: 'm',
3170
+ },
3171
+ 78: {
3172
+ field: 'active_time',
3173
+ type: 'uint32',
3174
+ scale: 1000,
3175
+ offset: 0,
3176
+ units: 's',
3177
+ },
3178
+ 107: {
3179
+ field: 'beginning_potential_stamina',
3180
+ type: 'uint8',
3181
+ scale: 1,
3182
+ offset: 0,
3183
+ units: 'percent',
3184
+ },
3185
+ 108: {
3186
+ field: 'ending_potential_stamina',
3187
+ type: 'uint8',
3188
+ scale: 1,
3189
+ offset: 0,
3190
+ units: 'percent',
3191
+ },
3192
+ 109: {
3193
+ field: 'min_stamina',
3194
+ type: 'uint8',
3195
+ scale: 1,
3196
+ offset: 0,
3197
+ units: 'percent',
3198
+ },
3199
+ 110: {
3200
+ field: 'total_moving_time',
3201
+ type: 'uint32',
3202
+ scale: 1000,
3203
+ offset: 0,
3204
+ units: 's',
3205
+ },
3206
+ },
3207
+ 313: {
3208
+ name: 'split_summary',
3209
+ 254: {
3210
+ field: 'message_index',
3211
+ type: 'message_index',
3212
+ scale: null,
3213
+ offset: 0,
3214
+ units: '',
3215
+ },
3216
+ 0: {
3217
+ field: 'split_type',
3218
+ type: 'split_type',
3219
+ scale: null,
3220
+ offset: 0,
3221
+ units: '',
3222
+ },
3223
+ 3: {
3224
+ field: 'num_splits',
3225
+ type: 'uint16',
3226
+ scale: null,
3227
+ offset: 0,
3228
+ units: '',
3229
+ },
3230
+ 4: {
3231
+ field: 'total_timer_time',
3232
+ type: 'uint32',
3233
+ scale: 1000,
3234
+ offset: 0,
3235
+ units: 's',
3236
+ },
3237
+ 5: {
3238
+ field: 'total_distance',
3239
+ type: 'uint32',
3240
+ scale: 100,
3241
+ offset: 0,
3242
+ units: 'm',
3243
+ },
3244
+ 6: {
3245
+ field: 'avg_speed',
3246
+ type: 'uint32',
3247
+ scale: 1000,
3248
+ offset: 0,
3249
+ units: 'm/s',
3250
+ },
3251
+ 7: {
3252
+ field: 'max_speed',
3253
+ type: 'uint32',
3254
+ scale: 1000,
3255
+ offset: 0,
3256
+ units: 'm/s',
3257
+ },
3258
+ 8: {
3259
+ field: 'total_ascent',
3260
+ type: 'uint16',
3261
+ scale: null,
3262
+ offset: 0,
3263
+ units: 'm',
3264
+ },
3265
+ 9: {
3266
+ field: 'total_descent',
3267
+ type: 'uint16',
3268
+ scale: null,
3269
+ offset: 0,
3270
+ units: 'm',
3271
+ },
3272
+ 10: {
3273
+ field: 'avg_heart_rate',
3274
+ type: 'uint8',
3275
+ scale: null,
3276
+ offset: 0,
3277
+ units: 'bpm',
3278
+ },
3279
+ 11: {
3280
+ field: 'max_heart_rate',
3281
+ type: 'uint8',
3282
+ scale: null,
3283
+ offset: 0,
3284
+ units: 'bpm',
3285
+ },
3286
+ 12: {
3287
+ field: 'avg_vert_speed',
3288
+ type: 'sint32',
3289
+ scale: 1000,
3290
+ offset: 0,
3291
+ units: 'm/s',
3292
+ },
3293
+ 13: {
3294
+ field: 'total_calories',
3295
+ type: 'uint32',
3296
+ scale: null,
3297
+ offset: 0,
3298
+ units: 'kcal',
3299
+ },
3300
+ 65: {
3301
+ field: 'active_time',
3302
+ type: 'uint32',
3303
+ scale: 1000,
3304
+ offset: 0,
3305
+ units: 's',
3306
+ },
3307
+ 77: {
3308
+ field: 'total_moving_time',
3309
+ type: 'uint32',
3310
+ scale: 1000,
3311
+ offset: 0,
3312
+ units: 's',
3313
+ },
3007
3314
  },
3008
3315
  285: {
3009
3316
  name: 'jump',
@@ -4134,6 +4441,109 @@ exports.FIT = {
4134
4441
  units: 'counts',
4135
4442
  },
4136
4443
  },
4444
+ // Undocumented Garmin user metrics message. Observed in activity FIT files exported
4445
+ // from Garmin Connect and displayed by fitfileviewer as "User Metrics".
4446
+ 79: {
4447
+ name: 'user_metrics',
4448
+ 253: {
4449
+ field: 'timestamp',
4450
+ type: 'date_time',
4451
+ scale: null,
4452
+ offset: 0,
4453
+ units: '',
4454
+ },
4455
+ 0: {
4456
+ field: 'vo2_max',
4457
+ type: 'uint16',
4458
+ scale: 1024 / 3.5,
4459
+ offset: 0,
4460
+ units: 'ml/kg/min',
4461
+ },
4462
+ 1: {
4463
+ field: 'age',
4464
+ type: 'uint8',
4465
+ scale: null,
4466
+ offset: 0,
4467
+ units: 'years',
4468
+ },
4469
+ 2: {
4470
+ field: 'height',
4471
+ type: 'uint8',
4472
+ scale: 100,
4473
+ offset: 0,
4474
+ units: 'm',
4475
+ },
4476
+ 3: {
4477
+ field: 'weight',
4478
+ type: 'uint16',
4479
+ scale: 10,
4480
+ offset: 0,
4481
+ units: 'kg',
4482
+ },
4483
+ 4: {
4484
+ field: 'gender',
4485
+ type: 'gender',
4486
+ scale: null,
4487
+ offset: 0,
4488
+ units: '',
4489
+ },
4490
+ 6: {
4491
+ field: 'max_heart_rate',
4492
+ type: 'uint8',
4493
+ scale: null,
4494
+ offset: 0,
4495
+ units: 'bpm',
4496
+ },
4497
+ 8: {
4498
+ field: 'remaining_recovery_time',
4499
+ type: 'uint16',
4500
+ scale: null,
4501
+ offset: 0,
4502
+ units: '',
4503
+ },
4504
+ 11: {
4505
+ field: 'lthr',
4506
+ type: 'uint16',
4507
+ scale: null,
4508
+ offset: 0,
4509
+ units: 'bpm',
4510
+ },
4511
+ 12: {
4512
+ field: 'ltpower',
4513
+ type: 'uint16',
4514
+ scale: null,
4515
+ offset: 0,
4516
+ units: 'watts',
4517
+ },
4518
+ 13: {
4519
+ field: 'ltspeed',
4520
+ type: 'uint16',
4521
+ scale: 1000,
4522
+ offset: 0,
4523
+ units: 'm/s',
4524
+ },
4525
+ 16: {
4526
+ field: 'start_of_activity',
4527
+ type: 'date_time',
4528
+ scale: null,
4529
+ offset: 0,
4530
+ units: '',
4531
+ },
4532
+ 19: {
4533
+ field: 'first_vo2_max',
4534
+ type: 'uint32',
4535
+ scale: 65536 / 3.5,
4536
+ offset: 0,
4537
+ units: 'ml/kg/min',
4538
+ },
4539
+ 35: {
4540
+ field: 'end_of_previous_activity',
4541
+ type: 'date_time',
4542
+ scale: null,
4543
+ offset: 0,
4544
+ units: '',
4545
+ },
4546
+ },
4137
4547
  103: {
4138
4548
  name: 'monitoring_info',
4139
4549
  253: {
@@ -4930,6 +5340,7 @@ exports.FIT = {
4930
5340
  55: 'monitoring',
4931
5341
  72: 'training_file',
4932
5342
  78: 'hrv',
5343
+ 79: 'user_metrics',
4933
5344
  80: 'ant_rx',
4934
5345
  81: 'ant_tx',
4935
5346
  82: 'ant_channel_id',
@@ -4981,6 +5392,8 @@ exports.FIT = {
4981
5392
  264: 'exercise_title',
4982
5393
  268: 'dive_summary',
4983
5394
  285: 'jump',
5395
+ 312: 'split',
5396
+ 313: 'split_summary',
4984
5397
  317: 'climb_pro',
4985
5398
  319: 'tank_update',
4986
5399
  323: 'tank_summary',
@@ -6497,6 +6910,29 @@ exports.FIT = {
6497
6910
  0: 'starred',
6498
6911
  1: 'suggested',
6499
6912
  },
6913
+ split_type: {
6914
+ 1: 'ascent_split',
6915
+ 2: 'descent_split',
6916
+ 3: 'interval_active',
6917
+ 4: 'interval_rest',
6918
+ 5: 'interval_warmup',
6919
+ 6: 'interval_cooldown',
6920
+ 7: 'interval_recovery',
6921
+ 8: 'interval_other',
6922
+ 9: 'climb_active',
6923
+ 10: 'climb_rest',
6924
+ 11: 'surf_active',
6925
+ 12: 'run_active',
6926
+ 13: 'run_rest',
6927
+ 14: 'workout_round',
6928
+ 17: 'rwd_run',
6929
+ 18: 'rwd_walk',
6930
+ 21: 'windsurf_active',
6931
+ 22: 'rwd_stand',
6932
+ 23: 'transition',
6933
+ 28: 'ski_lift_split',
6934
+ 29: 'ski_run_split',
6935
+ },
6500
6936
  source_type: {
6501
6937
  0: 'ant',
6502
6938
  1: 'antplus',