fit-file-parser 2.2.5 → 2.3.0
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 +43 -18
- package/dist/cjs/binary.js +43 -18
- package/dist/cjs/fit-parser.js +5 -0
- package/dist/cjs/fit.js +118 -9
- package/dist/cjs/fit_types.d.ts +20 -5
- package/dist/cjs/type_generator.js +1 -0
- package/dist/fit-parser.js +5 -0
- package/dist/fit.js +118 -9
- package/dist/fit_types.d.ts +20 -5
- package/dist/type_generator.js +1 -0
- package/package.json +1 -1
- package/debug_output.json +0 -413301
package/dist/binary.js
CHANGED
|
@@ -187,8 +187,23 @@ function convertTo(data, unitsList, unitName) {
|
|
|
187
187
|
const unit = options[unitName];
|
|
188
188
|
return unit ? data * unit.multiplier + unit.offset : data;
|
|
189
189
|
}
|
|
190
|
-
function applyOptions(data, field, options) {
|
|
190
|
+
function applyOptions(data, field, options, fields) {
|
|
191
191
|
switch (field) {
|
|
192
|
+
case 'device_type': {
|
|
193
|
+
const isLocal = fields.source_type === 'local' || fields.source_type === 5;
|
|
194
|
+
const isBLE = fields.source_type === 'bluetooth_low_energy' || fields.source_type === 3 || fields.source_type === 'bluetooth' || fields.source_type === 2;
|
|
195
|
+
const isANT = fields.source_type === 'antplus' || fields.source_type === 1 || fields.source_type === 'ant' || fields.source_type === 0;
|
|
196
|
+
if (isLocal) {
|
|
197
|
+
return FIT.types.local_device_type[data] || data;
|
|
198
|
+
}
|
|
199
|
+
if (isBLE) {
|
|
200
|
+
return FIT.types.ble_device_type[data] || data;
|
|
201
|
+
}
|
|
202
|
+
if (isANT) {
|
|
203
|
+
return FIT.types.antplus_device_type[data] || data;
|
|
204
|
+
}
|
|
205
|
+
return data;
|
|
206
|
+
}
|
|
192
207
|
case 'speed':
|
|
193
208
|
case 'enhanced_speed':
|
|
194
209
|
case 'vertical_speed':
|
|
@@ -332,31 +347,41 @@ export function readRecord(blob, messageTypes, developerFields, startIndex, opti
|
|
|
332
347
|
let readDataFromIndex = startIndex + 1;
|
|
333
348
|
const fields = {};
|
|
334
349
|
const message = getFitMessage(messageType.globalMessageNumber);
|
|
350
|
+
const rawFields = [];
|
|
335
351
|
for (let i = 0; i < messageType.fieldDefs.length; i++) {
|
|
336
352
|
const fDef = messageType.fieldDefs[i];
|
|
337
353
|
const data = readData(blob, fDef, readDataFromIndex, options);
|
|
338
354
|
if (!isInvalidValue(data, fDef.type)) {
|
|
339
|
-
|
|
340
|
-
const field = fDef.name;
|
|
341
|
-
const { type } = fDef;
|
|
342
|
-
const { scale } = fDef;
|
|
343
|
-
const { offset } = fDef;
|
|
344
|
-
fields[fDef.name] = applyOptions(formatByType(data, type, scale, offset), field, options);
|
|
345
|
-
}
|
|
346
|
-
else {
|
|
347
|
-
const { field, type, scale, offset } = message.getAttributes(fDef.fDefNo);
|
|
348
|
-
if (field !== 'unknown' && field !== '' && field !== undefined) {
|
|
349
|
-
fields[field] = applyOptions(formatByType(data, type, scale, offset), field, options);
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
if (message.name === 'record' && options.elapsedRecordField) {
|
|
353
|
-
fields.elapsed_time = (fields.timestamp - (startDate || 0)) / 1000;
|
|
354
|
-
fields.timer_time = fields.elapsed_time - pausedTime;
|
|
355
|
-
}
|
|
355
|
+
rawFields.push({ fDef, data });
|
|
356
356
|
}
|
|
357
357
|
readDataFromIndex += fDef.size;
|
|
358
358
|
messageSize += fDef.size;
|
|
359
359
|
}
|
|
360
|
+
for (const { fDef, data } of rawFields) {
|
|
361
|
+
const { field } = fDef.isDeveloperField ? { field: fDef.name } : message.getAttributes(fDef.fDefNo);
|
|
362
|
+
if (field !== 'unknown' && field !== '' && field !== undefined) {
|
|
363
|
+
fields[field] = data;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
for (const { fDef, data } of rawFields) {
|
|
367
|
+
if (fDef.isDeveloperField) {
|
|
368
|
+
const field = fDef.name;
|
|
369
|
+
const { type } = fDef;
|
|
370
|
+
const { scale } = fDef;
|
|
371
|
+
const { offset } = fDef;
|
|
372
|
+
fields[fDef.name] = applyOptions(formatByType(data, type, scale, offset), field, options, fields);
|
|
373
|
+
}
|
|
374
|
+
else {
|
|
375
|
+
const { field, type, scale, offset } = message.getAttributes(fDef.fDefNo);
|
|
376
|
+
if (field !== 'unknown' && field !== '' && field !== undefined) {
|
|
377
|
+
fields[field] = applyOptions(formatByType(data, type, scale, offset), field, options, fields);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
if (message.name === 'record' && options.elapsedRecordField) {
|
|
381
|
+
fields.elapsed_time = (fields.timestamp - (startDate || 0)) / 1000;
|
|
382
|
+
fields.timer_time = fields.elapsed_time - pausedTime;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
360
385
|
if (message.name === 'field_description') {
|
|
361
386
|
developerFields[fields.developer_data_index]
|
|
362
387
|
= developerFields[fields.developer_data_index] || [];
|
package/dist/cjs/binary.js
CHANGED
|
@@ -193,8 +193,23 @@ function convertTo(data, unitsList, unitName) {
|
|
|
193
193
|
const unit = options[unitName];
|
|
194
194
|
return unit ? data * unit.multiplier + unit.offset : data;
|
|
195
195
|
}
|
|
196
|
-
function applyOptions(data, field, options) {
|
|
196
|
+
function applyOptions(data, field, options, fields) {
|
|
197
197
|
switch (field) {
|
|
198
|
+
case 'device_type': {
|
|
199
|
+
const isLocal = fields.source_type === 'local' || fields.source_type === 5;
|
|
200
|
+
const isBLE = fields.source_type === 'bluetooth_low_energy' || fields.source_type === 3 || fields.source_type === 'bluetooth' || fields.source_type === 2;
|
|
201
|
+
const isANT = fields.source_type === 'antplus' || fields.source_type === 1 || fields.source_type === 'ant' || fields.source_type === 0;
|
|
202
|
+
if (isLocal) {
|
|
203
|
+
return fit_js_1.FIT.types.local_device_type[data] || data;
|
|
204
|
+
}
|
|
205
|
+
if (isBLE) {
|
|
206
|
+
return fit_js_1.FIT.types.ble_device_type[data] || data;
|
|
207
|
+
}
|
|
208
|
+
if (isANT) {
|
|
209
|
+
return fit_js_1.FIT.types.antplus_device_type[data] || data;
|
|
210
|
+
}
|
|
211
|
+
return data;
|
|
212
|
+
}
|
|
198
213
|
case 'speed':
|
|
199
214
|
case 'enhanced_speed':
|
|
200
215
|
case 'vertical_speed':
|
|
@@ -338,31 +353,41 @@ function readRecord(blob, messageTypes, developerFields, startIndex, options, st
|
|
|
338
353
|
let readDataFromIndex = startIndex + 1;
|
|
339
354
|
const fields = {};
|
|
340
355
|
const message = (0, messages_js_1.getFitMessage)(messageType.globalMessageNumber);
|
|
356
|
+
const rawFields = [];
|
|
341
357
|
for (let i = 0; i < messageType.fieldDefs.length; i++) {
|
|
342
358
|
const fDef = messageType.fieldDefs[i];
|
|
343
359
|
const data = readData(blob, fDef, readDataFromIndex, options);
|
|
344
360
|
if (!isInvalidValue(data, fDef.type)) {
|
|
345
|
-
|
|
346
|
-
const field = fDef.name;
|
|
347
|
-
const { type } = fDef;
|
|
348
|
-
const { scale } = fDef;
|
|
349
|
-
const { offset } = fDef;
|
|
350
|
-
fields[fDef.name] = applyOptions(formatByType(data, type, scale, offset), field, options);
|
|
351
|
-
}
|
|
352
|
-
else {
|
|
353
|
-
const { field, type, scale, offset } = message.getAttributes(fDef.fDefNo);
|
|
354
|
-
if (field !== 'unknown' && field !== '' && field !== undefined) {
|
|
355
|
-
fields[field] = applyOptions(formatByType(data, type, scale, offset), field, options);
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
if (message.name === 'record' && options.elapsedRecordField) {
|
|
359
|
-
fields.elapsed_time = (fields.timestamp - (startDate || 0)) / 1000;
|
|
360
|
-
fields.timer_time = fields.elapsed_time - pausedTime;
|
|
361
|
-
}
|
|
361
|
+
rawFields.push({ fDef, data });
|
|
362
362
|
}
|
|
363
363
|
readDataFromIndex += fDef.size;
|
|
364
364
|
messageSize += fDef.size;
|
|
365
365
|
}
|
|
366
|
+
for (const { fDef, data } of rawFields) {
|
|
367
|
+
const { field } = fDef.isDeveloperField ? { field: fDef.name } : message.getAttributes(fDef.fDefNo);
|
|
368
|
+
if (field !== 'unknown' && field !== '' && field !== undefined) {
|
|
369
|
+
fields[field] = data;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
for (const { fDef, data } of rawFields) {
|
|
373
|
+
if (fDef.isDeveloperField) {
|
|
374
|
+
const field = fDef.name;
|
|
375
|
+
const { type } = fDef;
|
|
376
|
+
const { scale } = fDef;
|
|
377
|
+
const { offset } = fDef;
|
|
378
|
+
fields[fDef.name] = applyOptions(formatByType(data, type, scale, offset), field, options, fields);
|
|
379
|
+
}
|
|
380
|
+
else {
|
|
381
|
+
const { field, type, scale, offset } = message.getAttributes(fDef.fDefNo);
|
|
382
|
+
if (field !== 'unknown' && field !== '' && field !== undefined) {
|
|
383
|
+
fields[field] = applyOptions(formatByType(data, type, scale, offset), field, options, fields);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
if (message.name === 'record' && options.elapsedRecordField) {
|
|
387
|
+
fields.elapsed_time = (fields.timestamp - (startDate || 0)) / 1000;
|
|
388
|
+
fields.timer_time = fields.elapsed_time - pausedTime;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
366
391
|
if (message.name === 'field_description') {
|
|
367
392
|
developerFields[fields.developer_data_index]
|
|
368
393
|
= developerFields[fields.developer_data_index] || [];
|
package/dist/cjs/fit-parser.js
CHANGED
|
@@ -103,6 +103,7 @@ class FitParser {
|
|
|
103
103
|
const tank_summaries = [];
|
|
104
104
|
const jumps = [];
|
|
105
105
|
const time_in_zone = [];
|
|
106
|
+
const activity_metrics = [];
|
|
106
107
|
let loopIndex = headerLength;
|
|
107
108
|
const messageTypes = [];
|
|
108
109
|
const developerFields = [];
|
|
@@ -204,6 +205,9 @@ class FitParser {
|
|
|
204
205
|
case 'time_in_zone':
|
|
205
206
|
time_in_zone.push(message);
|
|
206
207
|
break;
|
|
208
|
+
case 'activity_metrics':
|
|
209
|
+
activity_metrics.push(message);
|
|
210
|
+
break;
|
|
207
211
|
default:
|
|
208
212
|
if (messageType !== '') {
|
|
209
213
|
fitObj[messageType] = message;
|
|
@@ -225,6 +229,7 @@ class FitParser {
|
|
|
225
229
|
fitObj.tank_summaries = tank_summaries;
|
|
226
230
|
fitObj.jumps = jumps;
|
|
227
231
|
fitObj.time_in_zone = time_in_zone;
|
|
232
|
+
fitObj.activity_metrics = activity_metrics;
|
|
228
233
|
if (isCascadeNeeded) {
|
|
229
234
|
laps = (0, helper_js_1.mapDataIntoLap)(laps, 'records', records);
|
|
230
235
|
laps = (0, helper_js_1.mapDataIntoLap)(laps, 'lengths', lengths);
|
package/dist/cjs/fit.js
CHANGED
|
@@ -3106,7 +3106,7 @@ exports.FIT = {
|
|
|
3106
3106
|
},
|
|
3107
3107
|
1: {
|
|
3108
3108
|
field: 'device_type',
|
|
3109
|
-
type: '
|
|
3109
|
+
type: 'uint8',
|
|
3110
3110
|
scale: null,
|
|
3111
3111
|
offset: 0,
|
|
3112
3112
|
units: '',
|
|
@@ -4190,6 +4190,93 @@ exports.FIT = {
|
|
|
4190
4190
|
},
|
|
4191
4191
|
0: { field: 'enabled', type: 'byte', scale: null, offset: 0, units: '' },
|
|
4192
4192
|
},
|
|
4193
|
+
140: {
|
|
4194
|
+
name: 'activity_metrics',
|
|
4195
|
+
1: {
|
|
4196
|
+
field: 'new_max_heart_rate',
|
|
4197
|
+
type: 'uint8',
|
|
4198
|
+
scale: null,
|
|
4199
|
+
offset: 0,
|
|
4200
|
+
units: 'bpm',
|
|
4201
|
+
},
|
|
4202
|
+
4: {
|
|
4203
|
+
field: 'aerobic_training_effect',
|
|
4204
|
+
type: 'uint8',
|
|
4205
|
+
scale: 10,
|
|
4206
|
+
offset: 0,
|
|
4207
|
+
units: '',
|
|
4208
|
+
},
|
|
4209
|
+
7: {
|
|
4210
|
+
field: 'vo2_max',
|
|
4211
|
+
type: 'uint32',
|
|
4212
|
+
scale: 65536 / 3.5,
|
|
4213
|
+
offset: 0,
|
|
4214
|
+
units: 'ml/kg/min',
|
|
4215
|
+
},
|
|
4216
|
+
9: {
|
|
4217
|
+
field: 'recovery_time',
|
|
4218
|
+
type: 'uint16',
|
|
4219
|
+
scale: null,
|
|
4220
|
+
offset: 0,
|
|
4221
|
+
units: 'min',
|
|
4222
|
+
},
|
|
4223
|
+
11: {
|
|
4224
|
+
field: 'sport',
|
|
4225
|
+
type: 'sport',
|
|
4226
|
+
scale: null,
|
|
4227
|
+
offset: 0,
|
|
4228
|
+
units: '',
|
|
4229
|
+
},
|
|
4230
|
+
20: {
|
|
4231
|
+
field: 'anaerobic_training_effect',
|
|
4232
|
+
type: 'uint8',
|
|
4233
|
+
scale: 10,
|
|
4234
|
+
offset: 0,
|
|
4235
|
+
units: '',
|
|
4236
|
+
},
|
|
4237
|
+
29: {
|
|
4238
|
+
field: 'first_vo2_max',
|
|
4239
|
+
type: 'uint32',
|
|
4240
|
+
scale: 65536 / 3.5,
|
|
4241
|
+
offset: 0,
|
|
4242
|
+
units: 'ml/kg/min',
|
|
4243
|
+
},
|
|
4244
|
+
41: {
|
|
4245
|
+
field: 'primary_benefit',
|
|
4246
|
+
type: 'uint8',
|
|
4247
|
+
scale: null,
|
|
4248
|
+
offset: 0,
|
|
4249
|
+
units: '',
|
|
4250
|
+
},
|
|
4251
|
+
60: {
|
|
4252
|
+
field: 'total_ascent',
|
|
4253
|
+
type: 'uint16',
|
|
4254
|
+
scale: null,
|
|
4255
|
+
offset: 0,
|
|
4256
|
+
units: 'm',
|
|
4257
|
+
},
|
|
4258
|
+
61: {
|
|
4259
|
+
field: 'total_descent',
|
|
4260
|
+
type: 'uint16',
|
|
4261
|
+
scale: null,
|
|
4262
|
+
offset: 0,
|
|
4263
|
+
units: 'm',
|
|
4264
|
+
},
|
|
4265
|
+
62: {
|
|
4266
|
+
field: 'avg_power',
|
|
4267
|
+
type: 'uint16',
|
|
4268
|
+
scale: null,
|
|
4269
|
+
offset: 0,
|
|
4270
|
+
units: 'watts',
|
|
4271
|
+
},
|
|
4272
|
+
63: {
|
|
4273
|
+
field: 'avg_heart_rate',
|
|
4274
|
+
type: 'uint8',
|
|
4275
|
+
scale: null,
|
|
4276
|
+
offset: 0,
|
|
4277
|
+
units: 'bpm',
|
|
4278
|
+
},
|
|
4279
|
+
},
|
|
4193
4280
|
206: {
|
|
4194
4281
|
name: 'field_description',
|
|
4195
4282
|
0: {
|
|
@@ -4705,11 +4792,11 @@ exports.FIT = {
|
|
|
4705
4792
|
units: 's',
|
|
4706
4793
|
},
|
|
4707
4794
|
5: {
|
|
4708
|
-
field: '
|
|
4709
|
-
type: '
|
|
4710
|
-
scale:
|
|
4795
|
+
field: 'time_in_power_zone',
|
|
4796
|
+
type: 'uint32_array',
|
|
4797
|
+
scale: 1000,
|
|
4711
4798
|
offset: 0,
|
|
4712
|
-
units: '',
|
|
4799
|
+
units: 's',
|
|
4713
4800
|
},
|
|
4714
4801
|
6: {
|
|
4715
4802
|
field: 'hr_zone_high_boundary',
|
|
@@ -4733,11 +4820,11 @@ exports.FIT = {
|
|
|
4733
4820
|
units: 'watts',
|
|
4734
4821
|
},
|
|
4735
4822
|
9: {
|
|
4736
|
-
field: '
|
|
4737
|
-
type: '
|
|
4823
|
+
field: 'power_zone_high_boundary',
|
|
4824
|
+
type: 'uint16_array',
|
|
4738
4825
|
scale: null,
|
|
4739
4826
|
offset: 0,
|
|
4740
|
-
units: '',
|
|
4827
|
+
units: 'watts',
|
|
4741
4828
|
},
|
|
4742
4829
|
10: {
|
|
4743
4830
|
field: 'max_heart_rate_deprecated',
|
|
@@ -4855,6 +4942,7 @@ exports.FIT = {
|
|
|
4855
4942
|
129: 'weather_alert',
|
|
4856
4943
|
131: 'cadence_zone',
|
|
4857
4944
|
132: 'hr',
|
|
4945
|
+
140: 'activity_metrics',
|
|
4858
4946
|
142: 'segment_lap',
|
|
4859
4947
|
145: 'memo_glob',
|
|
4860
4948
|
148: 'segment_id',
|
|
@@ -5978,7 +6066,6 @@ exports.FIT = {
|
|
|
5978
6066
|
65534: 'connect',
|
|
5979
6067
|
},
|
|
5980
6068
|
antplus_device_type: {
|
|
5981
|
-
0: 0,
|
|
5982
6069
|
1: 'antfs',
|
|
5983
6070
|
11: 'bike_power',
|
|
5984
6071
|
12: 'environment_sensor_legacy',
|
|
@@ -5992,10 +6079,12 @@ exports.FIT = {
|
|
|
5992
6079
|
26: 'racquet',
|
|
5993
6080
|
27: 'control_hub',
|
|
5994
6081
|
31: 'muscle_oxygen',
|
|
6082
|
+
34: 'shifting',
|
|
5995
6083
|
35: 'bike_light_main',
|
|
5996
6084
|
36: 'bike_light_shared',
|
|
5997
6085
|
38: 'exd',
|
|
5998
6086
|
40: 'bike_radar',
|
|
6087
|
+
46: 'bike_aero',
|
|
5999
6088
|
119: 'weight_scale',
|
|
6000
6089
|
120: 'heart_rate',
|
|
6001
6090
|
121: 'bike_speed_cadence',
|
|
@@ -6003,6 +6092,26 @@ exports.FIT = {
|
|
|
6003
6092
|
123: 'bike_speed',
|
|
6004
6093
|
124: 'stride_speed_distance',
|
|
6005
6094
|
},
|
|
6095
|
+
local_device_type: {
|
|
6096
|
+
0: 'gps',
|
|
6097
|
+
1: 'glonass',
|
|
6098
|
+
2: 'gps_glonass',
|
|
6099
|
+
3: 'accelerometer',
|
|
6100
|
+
4: 'barometer',
|
|
6101
|
+
5: 'temperature',
|
|
6102
|
+
10: 'whr',
|
|
6103
|
+
12: 'sensor_hub',
|
|
6104
|
+
},
|
|
6105
|
+
ble_device_type: {
|
|
6106
|
+
0: 'connected_gps',
|
|
6107
|
+
1: 'heart_rate',
|
|
6108
|
+
2: 'bike_power',
|
|
6109
|
+
3: 'bike_speed_cadence',
|
|
6110
|
+
4: 'bike_speed',
|
|
6111
|
+
5: 'bike_cadence',
|
|
6112
|
+
6: 'footpod',
|
|
6113
|
+
7: 'bike_trainer',
|
|
6114
|
+
},
|
|
6006
6115
|
ant_network: {
|
|
6007
6116
|
0: 'public',
|
|
6008
6117
|
1: 'antplus',
|
package/dist/cjs/fit_types.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ export interface MessageIndex {
|
|
|
16
16
|
selected: boolean;
|
|
17
17
|
}
|
|
18
18
|
export type File = 'device' | 'settings' | 'sport' | 'activity' | 'workout' | 'course' | 'schedules' | 'weight' | 'totals' | 'goals' | 'blood_pressure' | 'monitoring_a' | 'activity_summary' | 'monitoring_daily' | 'monitoring_b' | 'segment' | 'segment_list' | 'exd_configuration' | 'mfg_range_min' | 'mfg_range_max';
|
|
19
|
-
export type MesgNum = 'file_id' | 'capabilities' | 'device_settings' | 'user_profile' | 'hrm_profile' | 'sdm_profile' | 'bike_profile' | 'zones_target' | 'hr_zone' | 'power_zone' | 'met_zone' | 'sport' | 'goal' | 'session' | 'lap' | 'record' | 'event' | 'device_info' | 'workout' | 'workout_step' | 'schedule' | 'weight_scale' | 'course' | 'course_point' | 'totals' | 'activity' | 'software' | 'file_capabilities' | 'mesg_capabilities' | 'field_capabilities' | 'file_creator' | 'blood_pressure' | 'speed_zone' | 'monitoring' | 'training_file' | 'hrv' | 'ant_rx' | 'ant_tx' | 'ant_channel_id' | 'length' | 'monitoring_info' | 'pad' | 'slave_device' | 'connectivity' | 'weather_conditions' | 'weather_alert' | 'cadence_zone' | 'hr' | 'segment_lap' | 'memo_glob' | 'segment_id' | 'segment_leaderboard_entry' | 'segment_point' | 'segment_file' | 'workout_session' | 'watchface_settings' | 'gps_metadata' | 'camera_event' | 'timestamp_correlation' | 'gyroscope_data' | 'accelerometer_data' | 'three_d_sensor_calibration' | 'video_frame' | 'obdii_data' | 'nmea_sentence' | 'aviation_attitude' | 'video' | 'video_title' | 'video_description' | 'video_clip' | 'exd_screen_configuration' | 'exd_data_field_configuration' | 'exd_data_concept_configuration' | 'field_description' | 'developer_data_id' | 'magnetometer_data' | 'barometer_data' | 'one_d_sensor_calibration' | 'time_in_zone' | 'set' | 'stress_level' | 'dive_settings' | 'dive_gas' | 'dive_alarm' | 'exercise_title' | 'dive_summary' | 'jump' | 'climb_pro' | 'tank_update' | 'tank_summary' | 'o_hr_settings' | 'mfg_range_min' | 'mfg_range_max' | 'definition';
|
|
19
|
+
export type MesgNum = 'file_id' | 'capabilities' | 'device_settings' | 'user_profile' | 'hrm_profile' | 'sdm_profile' | 'bike_profile' | 'zones_target' | 'hr_zone' | 'power_zone' | 'met_zone' | 'sport' | 'goal' | 'session' | 'lap' | 'record' | 'event' | 'device_info' | 'workout' | 'workout_step' | 'schedule' | 'weight_scale' | 'course' | 'course_point' | 'totals' | 'activity' | 'software' | 'file_capabilities' | 'mesg_capabilities' | 'field_capabilities' | 'file_creator' | 'blood_pressure' | 'speed_zone' | 'monitoring' | 'training_file' | 'hrv' | 'ant_rx' | 'ant_tx' | 'ant_channel_id' | 'length' | 'monitoring_info' | 'pad' | 'slave_device' | 'connectivity' | 'weather_conditions' | 'weather_alert' | 'cadence_zone' | 'hr' | 'activity_metrics' | 'segment_lap' | 'memo_glob' | 'segment_id' | 'segment_leaderboard_entry' | 'segment_point' | 'segment_file' | 'workout_session' | 'watchface_settings' | 'gps_metadata' | 'camera_event' | 'timestamp_correlation' | 'gyroscope_data' | 'accelerometer_data' | 'three_d_sensor_calibration' | 'video_frame' | 'obdii_data' | 'nmea_sentence' | 'aviation_attitude' | 'video' | 'video_title' | 'video_description' | 'video_clip' | 'exd_screen_configuration' | 'exd_data_field_configuration' | 'exd_data_concept_configuration' | 'field_description' | 'developer_data_id' | 'magnetometer_data' | 'barometer_data' | 'one_d_sensor_calibration' | 'time_in_zone' | 'set' | 'stress_level' | 'dive_settings' | 'dive_gas' | 'dive_alarm' | 'exercise_title' | 'dive_summary' | 'jump' | 'climb_pro' | 'tank_update' | 'tank_summary' | 'o_hr_settings' | 'mfg_range_min' | 'mfg_range_max' | 'definition';
|
|
20
20
|
export type Checksum = 'clear' | 'ok';
|
|
21
21
|
export type FileFlags = '0' | 'read' | 'write' | 'erase';
|
|
22
22
|
export type MesgCount = 'num_per_file' | 'max_per_file' | 'max_per_file_type';
|
|
@@ -72,7 +72,9 @@ export type Schedule = 'workout' | 'course';
|
|
|
72
72
|
export type CoursePoint = 'generic' | 'summit' | 'valley' | 'water' | 'food' | 'danger' | 'left' | 'right' | 'straight' | 'first_aid' | 'fourth_category' | 'third_category' | 'second_category' | 'first_category' | 'hors_category' | 'sprint' | 'left_fork' | 'right_fork' | 'middle_fork' | 'slight_left' | 'sharp_left' | 'slight_right' | 'sharp_right' | 'u_turn' | 'segment_start' | 'segment_end';
|
|
73
73
|
export type Manufacturer = '0' | 'garmin' | 'garmin_fr405_antfs' | 'zephyr' | 'dayton' | 'idt' | 'srm' | 'quarq' | 'ibike' | 'saris' | 'spark_hk' | 'tanita' | 'echowell' | 'dynastream_oem' | 'nautilus' | 'dynastream' | 'timex' | 'metrigear' | 'xelic' | 'beurer' | 'cardiosport' | 'a_and_d' | 'hmm' | 'suunto' | 'thita_elektronik' | 'gpulse' | 'clean_mobile' | 'pedal_brain' | 'peaksware' | 'saxonar' | 'lemond_fitness' | 'dexcom' | 'wahoo_fitness' | 'octane_fitness' | 'archinoetics' | 'the_hurt_box' | 'citizen_systems' | 'magellan' | 'osynce' | 'holux' | 'concept2' | 'one_giant_leap' | 'ace_sensor' | 'brim_brothers' | 'xplova' | 'perception_digital' | 'bf1systems' | 'pioneer' | 'spantec' | 'metalogics' | '4iiiis' | 'seiko_epson' | 'seiko_epson_oem' | 'ifor_powell' | 'maxwell_guider' | 'star_trac' | 'breakaway' | 'alatech_technology_ltd' | 'mio_technology_europe' | 'rotor' | 'geonaute' | 'id_bike' | 'specialized' | 'wtek' | 'physical_enterprises' | 'north_pole_engineering' | 'bkool' | 'cateye' | 'stages_cycling' | 'sigmasport' | 'tomtom' | 'peripedal' | 'wattbike' | 'moxy' | 'ciclosport' | 'powerbahn' | 'acorn_projects_aps' | 'lifebeam' | 'bontrager' | 'wellgo' | 'scosche' | 'magura' | 'woodway' | 'elite' | 'nielsen_kellerman' | 'dk_city' | 'tacx' | 'direction_technology' | 'magtonic' | '1partcarbon' | 'inside_ride_technologies' | 'sound_of_motion' | 'stryd' | 'icg' | 'mipulse' | 'bsx_athletics' | 'look' | 'campagnolo_srl' | 'body_bike_smart' | 'praxisworks' | 'limits_technology' | 'topaction_technology' | 'cosinuss' | 'fitcare' | 'magene' | 'giant_manufacturing_co' | 'tigrasport' | 'salutron' | 'technogym' | 'bryton_sensors' | 'latitude_limited' | 'soaring_technology' | 'igpsport' | 'thinkrider' | 'gopher_sport' | 'waterrower' | 'orangetheory' | 'inpeak' | 'kinetic' | 'johnson_health_tech' | 'polar_electro' | 'seesense' | 'nci_technology' | 'development' | 'healthandlife' | 'lezyne' | 'scribe_labs' | 'zwift' | 'watteam' | 'recon' | 'favero_electronics' | 'dynovelo' | 'strava' | 'precor' | 'bryton' | 'sram' | 'navman' | 'cobi' | 'spivi' | 'mio_magellan' | 'evesports' | 'sensitivus_gauge' | 'podoon' | 'life_time_fitness' | 'falco_e_motors' | 'minoura' | 'cycliq' | 'luxottica' | 'trainer_road' | 'the_sufferfest' | 'fullspeedahead' | 'virtualtraining' | 'feedbacksports' | 'omata' | 'vdo' | 'magneticdays' | 'hammerhead' | 'kinetic_by_kurt' | 'shapelog' | 'dabuziduo' | 'jetblack' | 'coros' | 'virtugo' | 'velosense' | 'actigraphcorp';
|
|
74
74
|
export type GarminProduct = 'hrm_bike' | 'hrm1' | 'axh01' | 'axb01' | 'axb02' | 'hrm2ss' | 'dsi_alf02' | 'hrm3ss' | 'hrm_run_single_byte_product_id' | 'bsm' | 'bcm' | 'axs01' | 'hrm_tri_single_byte_product_id' | 'fr225_single_byte_product_id' | 'fr301_china' | 'fr301_japan' | 'fr301_korea' | 'fr301_taiwan' | 'fr405' | 'fr50' | 'fr405_japan' | 'fr60' | 'dsi_alf01' | 'fr310xt' | 'edge500' | 'fr110' | 'edge800' | 'edge500_taiwan' | 'edge500_japan' | 'chirp' | 'fr110_japan' | 'edge200' | 'fr910xt' | 'edge800_taiwan' | 'edge800_japan' | 'alf04' | 'fr610' | 'fr210_japan' | 'vector_ss' | 'vector_cp' | 'edge800_china' | 'edge500_china' | 'fr610_japan' | 'edge500_korea' | 'fr70' | 'fr310xt_4t' | 'amx' | 'fr10' | 'edge800_korea' | 'swim' | 'fr910xt_china' | 'fenix' | 'edge200_taiwan' | 'edge510' | 'edge810' | 'tempe' | 'fr910xt_japan' | 'fr620' | 'fr220' | 'fr910xt_korea' | 'fr10_japan' | 'edge810_japan' | 'virb_elite' | 'edge_touring' | 'edge510_japan' | 'hrm_tri' | 'hrm_run' | 'fr920xt' | 'edge510_asia' | 'edge810_china' | 'edge810_taiwan' | 'edge1000' | 'vivo_fit' | 'virb_remote' | 'vivo_ki' | 'fr15' | 'vivo_active' | 'edge510_korea' | 'fr620_japan' | 'fr620_china' | 'fr220_japan' | 'fr220_china' | 'approach_s6' | 'vivo_smart' | 'fenix2' | 'epix' | 'fenix3' | 'edge1000_taiwan' | 'edge1000_japan' | 'fr15_japan' | 'edge520' | 'edge1000_china' | 'fr620_russia' | 'fr220_russia' | 'vector_s' | 'edge1000_korea' | 'fr920xt_taiwan' | 'fr920xt_china' | 'fr920xt_japan' | 'virbx' | 'vivo_smart_apac' | 'etrex_touch' | 'edge25' | 'fr25' | 'vivo_fit2' | 'fr225' | 'fr630' | 'fr230' | 'fr735xt' | 'vivo_active_apac' | 'vector_2' | 'vector_2s' | 'virbxe' | 'fr620_taiwan' | 'fr220_taiwan' | 'truswing' | 'fenix3_china' | 'fenix3_twn' | 'varia_headlight' | 'varia_taillight_old' | 'edge_explore_1000' | 'fr225_asia' | 'varia_radar_taillight' | 'varia_radar_display' | 'edge20' | 'd2_bravo' | 'approach_s20' | 'varia_remote' | 'hrm4_run' | 'vivo_active_hr' | 'vivo_smart_hr' | 'vivo_move' | 'varia_vision' | 'vivo_fit3' | 'fenix3_hr' | 'virb_ultra_30' | 'index_smart_scale' | 'fr235' | 'fenix3_chronos' | 'oregon7xx' | 'rino7xx' | 'nautix' | 'edge_820' | 'edge_explore_820' | 'fenix5s' | 'd2_bravo_titanium' | 'varia_ut800' | 'running_dynamics_pod' | 'fenix5x' | 'vivo_fit_jr' | 'fr935' | 'fenix5' | 'descent' | 'sdm4' | 'edge_remote' | 'training_center' | 'connectiq_simulator' | 'android_antplus_plugin' | 'connect';
|
|
75
|
-
export type AntplusDeviceType = '
|
|
75
|
+
export type AntplusDeviceType = 'antfs' | 'bike_power' | 'environment_sensor_legacy' | 'multi_sport_speed_distance' | 'control' | 'fitness_equipment' | 'blood_pressure' | 'geocache_node' | 'light_electric_vehicle' | 'env_sensor' | 'racquet' | 'control_hub' | 'muscle_oxygen' | 'shifting' | 'bike_light_main' | 'bike_light_shared' | 'exd' | 'bike_radar' | 'bike_aero' | 'weight_scale' | 'heart_rate' | 'bike_speed_cadence' | 'bike_cadence' | 'bike_speed' | 'stride_speed_distance';
|
|
76
|
+
export type LocalDeviceType = 'gps' | 'glonass' | 'gps_glonass' | 'accelerometer' | 'barometer' | 'temperature' | 'whr' | 'sensor_hub';
|
|
77
|
+
export type BleDeviceType = 'connected_gps' | 'heart_rate' | 'bike_power' | 'bike_speed_cadence' | 'bike_speed' | 'bike_cadence' | 'footpod' | 'bike_trainer';
|
|
76
78
|
export type AntNetwork = 'public' | 'antplus' | 'antfs' | 'private';
|
|
77
79
|
export type WorkoutCapabilities = '0' | 'interval' | 'custom' | 'fitness_equipment' | 'firstbeat' | 'new_leaf' | 'tcx' | 'speed' | 'heart_rate' | 'distance' | 'cadence' | 'power' | 'grade' | 'resistance' | 'protected';
|
|
78
80
|
export type BatteryStatus = '0' | 'new' | 'good' | 'ok' | 'low' | 'critical' | 'charging' | 'unknown';
|
|
@@ -665,7 +667,7 @@ export interface ParsedEvent {
|
|
|
665
667
|
}
|
|
666
668
|
export interface ParsedDeviceInfo {
|
|
667
669
|
device_index?: number;
|
|
668
|
-
device_type?:
|
|
670
|
+
device_type?: number;
|
|
669
671
|
manufacturer?: Manufacturer;
|
|
670
672
|
serial_number?: number;
|
|
671
673
|
product?: number;
|
|
@@ -871,6 +873,20 @@ export interface ParsedOHrSettings {
|
|
|
871
873
|
enabled?: number;
|
|
872
874
|
timestamp: string;
|
|
873
875
|
}
|
|
876
|
+
export interface ParsedActivityMetrics {
|
|
877
|
+
new_max_heart_rate?: number;
|
|
878
|
+
aerobic_training_effect?: number;
|
|
879
|
+
vo2_max?: number;
|
|
880
|
+
recovery_time?: number;
|
|
881
|
+
sport?: Sport;
|
|
882
|
+
anaerobic_training_effect?: number;
|
|
883
|
+
first_vo2_max?: number;
|
|
884
|
+
primary_benefit?: number;
|
|
885
|
+
total_ascent?: number;
|
|
886
|
+
total_descent?: number;
|
|
887
|
+
avg_power?: number;
|
|
888
|
+
avg_heart_rate?: number;
|
|
889
|
+
}
|
|
874
890
|
export interface ParsedFieldDescription {
|
|
875
891
|
developer_data_index?: number;
|
|
876
892
|
field_definition_number?: number;
|
|
@@ -894,11 +910,9 @@ export interface ParsedTimeInZone {
|
|
|
894
910
|
time_in_hr_zone?: number[];
|
|
895
911
|
time_in_speed_zone?: number[];
|
|
896
912
|
time_in_power_zone?: number[];
|
|
897
|
-
hr_zone_high_boundary_deprecated?: number[];
|
|
898
913
|
hr_zone_high_boundary?: number[];
|
|
899
914
|
speed_zone_high_boundary?: number[];
|
|
900
915
|
power_zone_high_boundary?: number[];
|
|
901
|
-
hr_calc_type?: HrZoneCalc;
|
|
902
916
|
max_heart_rate_deprecated?: number;
|
|
903
917
|
max_heart_rate?: number;
|
|
904
918
|
resting_heart_rate?: number;
|
|
@@ -1025,4 +1039,5 @@ export interface ParsedFit {
|
|
|
1025
1039
|
tank_summaries?: ParsedTankSummary[];
|
|
1026
1040
|
jumps?: ParsedJump[];
|
|
1027
1041
|
time_in_zone?: ParsedTimeInZone[];
|
|
1042
|
+
activity_metrics?: ParsedActivityMetrics[];
|
|
1028
1043
|
}
|
|
@@ -210,6 +210,7 @@ function generateFitType() {
|
|
|
210
210
|
tank_summaries: 'parsed_tank_summary',
|
|
211
211
|
jumps: 'parsed_jump',
|
|
212
212
|
time_in_zone: 'parsed_time_in_zone',
|
|
213
|
+
activity_metrics: 'parsed_activity_metrics',
|
|
213
214
|
};
|
|
214
215
|
const referenceProperties = {
|
|
215
216
|
file_creator: 'parsed_file_creator',
|
package/dist/fit-parser.js
CHANGED
|
@@ -101,6 +101,7 @@ export default class FitParser {
|
|
|
101
101
|
const tank_summaries = [];
|
|
102
102
|
const jumps = [];
|
|
103
103
|
const time_in_zone = [];
|
|
104
|
+
const activity_metrics = [];
|
|
104
105
|
let loopIndex = headerLength;
|
|
105
106
|
const messageTypes = [];
|
|
106
107
|
const developerFields = [];
|
|
@@ -202,6 +203,9 @@ export default class FitParser {
|
|
|
202
203
|
case 'time_in_zone':
|
|
203
204
|
time_in_zone.push(message);
|
|
204
205
|
break;
|
|
206
|
+
case 'activity_metrics':
|
|
207
|
+
activity_metrics.push(message);
|
|
208
|
+
break;
|
|
205
209
|
default:
|
|
206
210
|
if (messageType !== '') {
|
|
207
211
|
fitObj[messageType] = message;
|
|
@@ -223,6 +227,7 @@ export default class FitParser {
|
|
|
223
227
|
fitObj.tank_summaries = tank_summaries;
|
|
224
228
|
fitObj.jumps = jumps;
|
|
225
229
|
fitObj.time_in_zone = time_in_zone;
|
|
230
|
+
fitObj.activity_metrics = activity_metrics;
|
|
226
231
|
if (isCascadeNeeded) {
|
|
227
232
|
laps = mapDataIntoLap(laps, 'records', records);
|
|
228
233
|
laps = mapDataIntoLap(laps, 'lengths', lengths);
|