fit-file-parser 2.2.1 → 2.2.3
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/deep_probe.js +57 -56
- package/dist/binary.js +4 -4
- package/dist/cjs/binary.js +4 -4
- package/dist/cjs/fit.js +17 -1
- package/dist/cjs/fit_types.d.ts +48 -31
- package/dist/cjs/type_generator.js +16 -8
- package/dist/fit.js +17 -1
- package/dist/fit_types.d.ts +48 -31
- package/dist/type_generator.js +16 -8
- package/eslint.config.js +5 -0
- package/package.json +2 -2
- package/probe_session.js +0 -44
package/deep_probe.js
CHANGED
|
@@ -1,69 +1,70 @@
|
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
import { createRequire } from 'module'
|
|
1
3
|
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const require = createRequire(import.meta.url);
|
|
5
|
-
const FitParser = require('./dist/fit-parser.js').default;
|
|
4
|
+
const require = createRequire(import.meta.url)
|
|
5
|
+
const FitParser = require('./dist/fit-parser.js').default
|
|
6
6
|
|
|
7
|
-
const content = fs.readFileSync('/Users/dimitrios/Projects/sports-lib/samples/fit/jumps-mtb.fit')
|
|
7
|
+
const content = fs.readFileSync('/Users/dimitrios/Projects/sports-lib/samples/fit/jumps-mtb.fit')
|
|
8
8
|
const fitParser = new FitParser({
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
})
|
|
9
|
+
force: true,
|
|
10
|
+
speedUnit: 'km/h',
|
|
11
|
+
lengthUnit: 'm',
|
|
12
|
+
temperatureUnit: 'celsius',
|
|
13
|
+
elapsedRecordField: true,
|
|
14
|
+
mode: 'both',
|
|
15
|
+
})
|
|
16
16
|
|
|
17
17
|
function search(obj, path = []) {
|
|
18
|
-
|
|
18
|
+
if (!obj || typeof obj !== 'object')
|
|
19
|
+
return
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
Object.keys(obj).forEach((key) => {
|
|
22
|
+
const val = obj[key]
|
|
23
|
+
const newPath = [...path, key]
|
|
23
24
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
25
|
+
// Check match 11 (Jump Count)
|
|
26
|
+
if (typeof val === 'number' && Math.abs(val - 11) < 0.001) {
|
|
27
|
+
console.log(`>>> FOUND 11 at: ${newPath.join('.')} (Value: ${val})`)
|
|
28
|
+
}
|
|
29
|
+
// Check match 159 (Resting Cals)
|
|
30
|
+
if (typeof val === 'number' && Math.abs(val - 159) < 0.1) {
|
|
31
|
+
console.log(`>>> FOUND 159 at: ${newPath.join('.')} (Value: ${val})`)
|
|
32
|
+
}
|
|
33
|
+
// Check match for Training Load Peak
|
|
34
|
+
if (typeof val === 'number' && Math.abs(val - 6079174) < 100) {
|
|
35
|
+
console.log(`>>> FOUND 6M at: ${newPath.join('.')} (Value: ${val})`)
|
|
36
|
+
}
|
|
36
37
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
if (typeof val === 'object') {
|
|
39
|
+
search(val, newPath)
|
|
40
|
+
}
|
|
41
|
+
})
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
fitParser.parse(content, (error, data) => {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
if (error) {
|
|
46
|
+
console.error(error)
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
console.log('Starting deep search...')
|
|
50
|
+
search(data)
|
|
49
51
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
console.log(`Found ${jumpEvents.length} jump events`);
|
|
67
|
-
}
|
|
52
|
+
if (data.sessions && data.sessions.length > 0) {
|
|
53
|
+
console.log('\n=== SESSION OBJECT (First) ===')
|
|
54
|
+
const session = data.sessions[0]
|
|
55
|
+
Object.keys(session).forEach((key) => {
|
|
56
|
+
console.log(`${key}: ${session[key]}`)
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
// Explicitly check for jump-related keys
|
|
60
|
+
if (data.jumps) {
|
|
61
|
+
console.log('=== JUMPS OBJECT === ')
|
|
62
|
+
console.log(JSON.stringify(data.jumps, null, 2))
|
|
63
|
+
}
|
|
64
|
+
// Also check for jump events in events
|
|
65
|
+
if (data.events) {
|
|
66
|
+
const jumpEvents = data.events.filter(e => JSON.stringify(e).includes('jump'))
|
|
67
|
+
console.log(`Found ${jumpEvents.length} jump events`)
|
|
68
68
|
}
|
|
69
|
-
}
|
|
69
|
+
}
|
|
70
|
+
})
|
package/dist/binary.js
CHANGED
|
@@ -228,10 +228,10 @@ function applyOptions(data, field, options) {
|
|
|
228
228
|
case 'end_pressure':
|
|
229
229
|
return convertTo(data, 'pressureUnits', options.pressureUnit);
|
|
230
230
|
case 'ant_id': {
|
|
231
|
-
const n1 = (data >>> 28) &
|
|
232
|
-
const n2 = (data >>> 24) &
|
|
233
|
-
const n3 = (data >>> 16) &
|
|
234
|
-
const n4 = data &
|
|
231
|
+
const n1 = (data >>> 28) & 0xF;
|
|
232
|
+
const n2 = (data >>> 24) & 0xF;
|
|
233
|
+
const n3 = (data >>> 16) & 0xFF;
|
|
234
|
+
const n4 = data & 0xFFFF;
|
|
235
235
|
return `${n1.toString(16).toUpperCase()}-${n2.toString(16).toUpperCase()}-${n3.toString(16).toUpperCase().padStart(2, '0')}-${n4.toString(16).toUpperCase().padStart(4, '0')}`;
|
|
236
236
|
}
|
|
237
237
|
default:
|
package/dist/cjs/binary.js
CHANGED
|
@@ -234,10 +234,10 @@ function applyOptions(data, field, options) {
|
|
|
234
234
|
case 'end_pressure':
|
|
235
235
|
return convertTo(data, 'pressureUnits', options.pressureUnit);
|
|
236
236
|
case 'ant_id': {
|
|
237
|
-
const n1 = (data >>> 28) &
|
|
238
|
-
const n2 = (data >>> 24) &
|
|
239
|
-
const n3 = (data >>> 16) &
|
|
240
|
-
const n4 = data &
|
|
237
|
+
const n1 = (data >>> 28) & 0xF;
|
|
238
|
+
const n2 = (data >>> 24) & 0xF;
|
|
239
|
+
const n3 = (data >>> 16) & 0xFF;
|
|
240
|
+
const n4 = data & 0xFFFF;
|
|
241
241
|
return `${n1.toString(16).toUpperCase()}-${n2.toString(16).toUpperCase()}-${n3.toString(16).toUpperCase().padStart(2, '0')}-${n4.toString(16).toUpperCase().padStart(4, '0')}`;
|
|
242
242
|
}
|
|
243
243
|
default:
|
package/dist/cjs/fit.js
CHANGED
|
@@ -1734,6 +1734,13 @@ exports.FIT = {
|
|
|
1734
1734
|
offset: 0,
|
|
1735
1735
|
units: 'kGrit',
|
|
1736
1736
|
},
|
|
1737
|
+
182: {
|
|
1738
|
+
field: 'total_flow',
|
|
1739
|
+
type: 'float32',
|
|
1740
|
+
scale: 1,
|
|
1741
|
+
offset: 0,
|
|
1742
|
+
units: 'Flow',
|
|
1743
|
+
},
|
|
1737
1744
|
183: {
|
|
1738
1745
|
field: 'jump_count',
|
|
1739
1746
|
type: 'uint16',
|
|
@@ -1741,6 +1748,13 @@ exports.FIT = {
|
|
|
1741
1748
|
offset: 0,
|
|
1742
1749
|
units: '',
|
|
1743
1750
|
},
|
|
1751
|
+
186: {
|
|
1752
|
+
field: 'avg_grit',
|
|
1753
|
+
type: 'float32',
|
|
1754
|
+
scale: 1,
|
|
1755
|
+
offset: 0,
|
|
1756
|
+
units: 'kGrit',
|
|
1757
|
+
},
|
|
1744
1758
|
187: {
|
|
1745
1759
|
field: 'avg_flow',
|
|
1746
1760
|
type: 'float32',
|
|
@@ -4875,8 +4889,10 @@ exports.FIT = {
|
|
|
4875
4889
|
268: 'dive_summary',
|
|
4876
4890
|
285: 'jump',
|
|
4877
4891
|
317: 'climb_pro',
|
|
4878
|
-
319: '
|
|
4892
|
+
319: 'tank_update',
|
|
4879
4893
|
323: 'tank_summary',
|
|
4894
|
+
216: 'time_in_zone',
|
|
4895
|
+
347: 'o_hr_settings',
|
|
4880
4896
|
65280: 'mfg_range_min',
|
|
4881
4897
|
65534: 'mfg_range_max',
|
|
4882
4898
|
},
|
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' | 'set' | 'stress_level' | 'dive_settings' | 'dive_gas' | 'dive_alarm' | 'exercise_title' | 'dive_summary' | 'jump' | 'climb_pro' | '
|
|
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';
|
|
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';
|
|
@@ -363,6 +363,8 @@ export interface ParsedSession {
|
|
|
363
363
|
training_stress_score?: number;
|
|
364
364
|
intensity_factor?: number;
|
|
365
365
|
left_right_balance?: LeftRightBalance100;
|
|
366
|
+
end_position_lat?: number;
|
|
367
|
+
end_position_long?: number;
|
|
366
368
|
avg_stroke_count?: number;
|
|
367
369
|
avg_stroke_distance?: number;
|
|
368
370
|
swim_stroke?: SwimStroke;
|
|
@@ -387,10 +389,10 @@ export interface ParsedSession {
|
|
|
387
389
|
max_pos_vertical_speed?: number;
|
|
388
390
|
max_neg_vertical_speed?: number;
|
|
389
391
|
min_heart_rate?: number;
|
|
390
|
-
time_in_hr_zone?:
|
|
391
|
-
time_in_speed_zone?:
|
|
392
|
-
time_in_cadence_zone?:
|
|
393
|
-
time_in_power_zone?:
|
|
392
|
+
time_in_hr_zone?: number[];
|
|
393
|
+
time_in_speed_zone?: number[];
|
|
394
|
+
time_in_cadence_zone?: number[];
|
|
395
|
+
time_in_power_zone?: number[];
|
|
394
396
|
avg_lap_time?: number;
|
|
395
397
|
best_lap_index?: number;
|
|
396
398
|
min_altitude?: number;
|
|
@@ -418,6 +420,7 @@ export interface ParsedSession {
|
|
|
418
420
|
avg_left_pedal_smoothness?: number;
|
|
419
421
|
avg_right_pedal_smoothness?: number;
|
|
420
422
|
avg_combined_pedal_smoothness?: number;
|
|
423
|
+
sport_profile_name?: string;
|
|
421
424
|
sport_index?: number;
|
|
422
425
|
time_standing?: number;
|
|
423
426
|
stand_count?: number;
|
|
@@ -442,14 +445,24 @@ export interface ParsedSession {
|
|
|
442
445
|
avg_vertical_ratio?: number;
|
|
443
446
|
avg_stance_time_balance?: number;
|
|
444
447
|
avg_step_length?: number;
|
|
445
|
-
|
|
448
|
+
total_anaerobic_training_effect?: number;
|
|
446
449
|
avg_vam?: number;
|
|
450
|
+
recovery_advisor?: number;
|
|
451
|
+
min_temperature?: number;
|
|
452
|
+
training_load_peak?: number;
|
|
453
|
+
enhanced_avg_respiration_rate?: number;
|
|
454
|
+
enhanced_max_respiration_rate?: number;
|
|
455
|
+
est_sweat_loss?: number;
|
|
456
|
+
enhanced_min_respiration_rate?: number;
|
|
447
457
|
total_grit?: number;
|
|
448
458
|
total_flow?: number;
|
|
449
|
-
|
|
450
|
-
workout_rpe?: number;
|
|
459
|
+
jump_count?: number;
|
|
451
460
|
avg_grit?: number;
|
|
452
461
|
avg_flow?: number;
|
|
462
|
+
primary_benefit?: number;
|
|
463
|
+
workout_feel?: number;
|
|
464
|
+
workout_rpe?: number;
|
|
465
|
+
resting_calories?: number;
|
|
453
466
|
timestamp: string;
|
|
454
467
|
message_index?: MessageIndex;
|
|
455
468
|
laps?: ParsedLap[];
|
|
@@ -506,10 +519,10 @@ export interface ParsedLap {
|
|
|
506
519
|
avg_neg_vertical_speed?: number;
|
|
507
520
|
max_pos_vertical_speed?: number;
|
|
508
521
|
max_neg_vertical_speed?: number;
|
|
509
|
-
time_in_hr_zone?:
|
|
510
|
-
time_in_speed_zone?:
|
|
511
|
-
time_in_cadence_zone?:
|
|
512
|
-
time_in_power_zone?:
|
|
522
|
+
time_in_hr_zone?: number[];
|
|
523
|
+
time_in_speed_zone?: number[];
|
|
524
|
+
time_in_cadence_zone?: number[];
|
|
525
|
+
time_in_power_zone?: number[];
|
|
513
526
|
repetition_num?: number;
|
|
514
527
|
min_altitude?: number;
|
|
515
528
|
min_heart_rate?: number;
|
|
@@ -666,6 +679,7 @@ export interface ParsedDeviceInfo {
|
|
|
666
679
|
ant_transmission_type?: number;
|
|
667
680
|
ant_device_number?: number;
|
|
668
681
|
ant_network?: AntNetwork;
|
|
682
|
+
ant_id?: number;
|
|
669
683
|
source_type?: SourceType;
|
|
670
684
|
product_name?: string;
|
|
671
685
|
battery_level?: number;
|
|
@@ -823,7 +837,7 @@ export interface ParsedMonitoring {
|
|
|
823
837
|
timestamp: number;
|
|
824
838
|
}
|
|
825
839
|
export interface ParsedHrv {
|
|
826
|
-
time?:
|
|
840
|
+
time?: number[];
|
|
827
841
|
}
|
|
828
842
|
export interface ParsedLength {
|
|
829
843
|
event?: Event;
|
|
@@ -858,6 +872,7 @@ export interface ParsedOHrSettings {
|
|
|
858
872
|
timestamp: string;
|
|
859
873
|
}
|
|
860
874
|
export interface ParsedJump {
|
|
875
|
+
enhanced_mets?: number;
|
|
861
876
|
distance?: number;
|
|
862
877
|
height?: number;
|
|
863
878
|
score?: number;
|
|
@@ -875,11 +890,30 @@ export interface ParsedFieldDescription {
|
|
|
875
890
|
}
|
|
876
891
|
export interface ParsedDeveloperDataId {
|
|
877
892
|
developer_id?: number;
|
|
878
|
-
application_id?:
|
|
893
|
+
application_id?: number[];
|
|
879
894
|
manufacturer_id?: Manufacturer;
|
|
880
895
|
developer_data_index?: number;
|
|
881
896
|
application_version?: number;
|
|
882
897
|
}
|
|
898
|
+
export interface ParsedTimeInZone {
|
|
899
|
+
reference_mesg?: number;
|
|
900
|
+
reference_index?: number;
|
|
901
|
+
time_in_hr_zone?: number[];
|
|
902
|
+
time_in_speed_zone?: number[];
|
|
903
|
+
time_in_power_zone?: number[];
|
|
904
|
+
hr_zone_high_boundary_deprecated?: number[];
|
|
905
|
+
hr_zone_high_boundary?: number[];
|
|
906
|
+
speed_zone_high_boundary?: number[];
|
|
907
|
+
power_zone_high_boundary?: number[];
|
|
908
|
+
hr_calc_type?: HrZoneCalc;
|
|
909
|
+
max_heart_rate_deprecated?: number;
|
|
910
|
+
max_heart_rate?: number;
|
|
911
|
+
resting_heart_rate?: number;
|
|
912
|
+
threshold_heart_rate?: number;
|
|
913
|
+
pwr_calc_type?: PwrZoneCalc;
|
|
914
|
+
functional_threshold_power?: number;
|
|
915
|
+
timestamp: string;
|
|
916
|
+
}
|
|
883
917
|
export interface ParsedStressLevel {
|
|
884
918
|
stress_level_value?: number;
|
|
885
919
|
stress_level_time?: string;
|
|
@@ -952,23 +986,6 @@ export interface ParsedTankSummary {
|
|
|
952
986
|
end_pressure?: number;
|
|
953
987
|
volume_used?: number;
|
|
954
988
|
}
|
|
955
|
-
export interface ParsedTimeInZone {
|
|
956
|
-
timestamp: string;
|
|
957
|
-
reference_mesg?: number;
|
|
958
|
-
reference_index?: number;
|
|
959
|
-
time_in_hr_zone?: number[];
|
|
960
|
-
time_in_speed_zone?: number[];
|
|
961
|
-
time_in_power_zone?: number[];
|
|
962
|
-
hr_zone_high_boundary?: number[];
|
|
963
|
-
speed_zone_high_boundary?: number[];
|
|
964
|
-
power_zone_high_boundary?: number[];
|
|
965
|
-
hr_calc_type?: HrZoneCalc;
|
|
966
|
-
max_heart_rate?: number;
|
|
967
|
-
resting_heart_rate?: number;
|
|
968
|
-
threshold_heart_rate?: number;
|
|
969
|
-
pwr_calc_type?: PwrZoneCalc;
|
|
970
|
-
functional_threshold_power?: number;
|
|
971
|
-
}
|
|
972
989
|
export interface ParsedFit {
|
|
973
990
|
protocolVersion?: number;
|
|
974
991
|
profileVersion?: number;
|
|
@@ -60,8 +60,14 @@ function generateArrayProperty(name, type, optional = true) {
|
|
|
60
60
|
}
|
|
61
61
|
function generateTypeFromField(def) {
|
|
62
62
|
switch (def.type) {
|
|
63
|
+
case 'uint32_array':
|
|
64
|
+
case 'uint16_array':
|
|
65
|
+
case 'uint8_array':
|
|
66
|
+
case 'sint32_array':
|
|
67
|
+
case 'sint16_array':
|
|
68
|
+
case 'sint8_array':
|
|
63
69
|
case 'byte_array':
|
|
64
|
-
return typescript_1.default.factory.createArrayTypeNode(typescript_1.default.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.
|
|
70
|
+
return typescript_1.default.factory.createArrayTypeNode(typescript_1.default.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.NumberKeyword));
|
|
65
71
|
case 'bool':
|
|
66
72
|
return typescript_1.default.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.BooleanKeyword);
|
|
67
73
|
case 'date_time':
|
|
@@ -104,11 +110,7 @@ function generateTypes(types) {
|
|
|
104
110
|
const typeAlias = typescript_1.default.factory.createTypeAliasDeclaration([
|
|
105
111
|
typescript_1.default.factory.createModifier(typescript_1.default.SyntaxKind.ExportKeyword),
|
|
106
112
|
], snakeToCamel(name), undefined, typescript_1.default.factory.createUnionTypeNode([...names.map(n => typescript_1.default.factory.createLiteralTypeNode(typescript_1.default.factory.createStringLiteral(String(n)))), ...(name === 'mesg_num'
|
|
107
|
-
// TODO those are somehow missing in the FIT.types.mesg_num definition
|
|
108
|
-
// especially 'tank_update' (319 in messages) is mapped to 'tank_pressure' a bug?
|
|
109
113
|
? [
|
|
110
|
-
typescript_1.default.factory.createLiteralTypeNode(typescript_1.default.factory.createStringLiteral('o_hr_settings')),
|
|
111
|
-
typescript_1.default.factory.createLiteralTypeNode(typescript_1.default.factory.createStringLiteral('tank_update')),
|
|
112
114
|
typescript_1.default.factory.createLiteralTypeNode(typescript_1.default.factory.createStringLiteral('definition')),
|
|
113
115
|
]
|
|
114
116
|
: [])]));
|
|
@@ -207,6 +209,7 @@ function generateFitType() {
|
|
|
207
209
|
tank_updates: 'parsed_tank_update',
|
|
208
210
|
tank_summaries: 'parsed_tank_summary',
|
|
209
211
|
jumps: 'parsed_jump',
|
|
212
|
+
time_in_zone: 'parsed_time_in_zone',
|
|
210
213
|
};
|
|
211
214
|
const referenceProperties = {
|
|
212
215
|
file_creator: 'parsed_file_creator',
|
|
@@ -231,13 +234,18 @@ function generateMessages(messages) {
|
|
|
231
234
|
const nodes = [];
|
|
232
235
|
Object.keys(messages).forEach((name) => {
|
|
233
236
|
const msg = fit_js_1.FIT.messages[Number(name)];
|
|
237
|
+
const usedFields = new Set();
|
|
234
238
|
const messageType = typescript_1.default.factory.createInterfaceDeclaration([
|
|
235
239
|
typescript_1.default.factory.createModifier(typescript_1.default.SyntaxKind.ExportKeyword),
|
|
236
240
|
], snakeToCamel(`parsed_${msg.name}`), undefined, undefined, [
|
|
237
|
-
...Object.keys(msg).filter(n => n !== 'name').
|
|
241
|
+
...Object.keys(msg).filter(n => n !== 'name').reduce((acc, id) => {
|
|
238
242
|
const def = msg[Number(id)];
|
|
239
|
-
|
|
240
|
-
|
|
243
|
+
if (!usedFields.has(def.field)) {
|
|
244
|
+
usedFields.add(def.field);
|
|
245
|
+
acc.push(generateProperty(def.field, generateTypeFromField(def), !['start_time', 'timestamp'].includes(def.field)));
|
|
246
|
+
}
|
|
247
|
+
return acc;
|
|
248
|
+
}, []),
|
|
241
249
|
...generateAdditionalFields(msg),
|
|
242
250
|
]);
|
|
243
251
|
nodes.push(messageType);
|
package/dist/fit.js
CHANGED
|
@@ -1731,6 +1731,13 @@ export const FIT = {
|
|
|
1731
1731
|
offset: 0,
|
|
1732
1732
|
units: 'kGrit',
|
|
1733
1733
|
},
|
|
1734
|
+
182: {
|
|
1735
|
+
field: 'total_flow',
|
|
1736
|
+
type: 'float32',
|
|
1737
|
+
scale: 1,
|
|
1738
|
+
offset: 0,
|
|
1739
|
+
units: 'Flow',
|
|
1740
|
+
},
|
|
1734
1741
|
183: {
|
|
1735
1742
|
field: 'jump_count',
|
|
1736
1743
|
type: 'uint16',
|
|
@@ -1738,6 +1745,13 @@ export const FIT = {
|
|
|
1738
1745
|
offset: 0,
|
|
1739
1746
|
units: '',
|
|
1740
1747
|
},
|
|
1748
|
+
186: {
|
|
1749
|
+
field: 'avg_grit',
|
|
1750
|
+
type: 'float32',
|
|
1751
|
+
scale: 1,
|
|
1752
|
+
offset: 0,
|
|
1753
|
+
units: 'kGrit',
|
|
1754
|
+
},
|
|
1741
1755
|
187: {
|
|
1742
1756
|
field: 'avg_flow',
|
|
1743
1757
|
type: 'float32',
|
|
@@ -4872,8 +4886,10 @@ export const FIT = {
|
|
|
4872
4886
|
268: 'dive_summary',
|
|
4873
4887
|
285: 'jump',
|
|
4874
4888
|
317: 'climb_pro',
|
|
4875
|
-
319: '
|
|
4889
|
+
319: 'tank_update',
|
|
4876
4890
|
323: 'tank_summary',
|
|
4891
|
+
216: 'time_in_zone',
|
|
4892
|
+
347: 'o_hr_settings',
|
|
4877
4893
|
65280: 'mfg_range_min',
|
|
4878
4894
|
65534: 'mfg_range_max',
|
|
4879
4895
|
},
|
package/dist/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' | 'set' | 'stress_level' | 'dive_settings' | 'dive_gas' | 'dive_alarm' | 'exercise_title' | 'dive_summary' | 'jump' | 'climb_pro' | '
|
|
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';
|
|
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';
|
|
@@ -363,6 +363,8 @@ export interface ParsedSession {
|
|
|
363
363
|
training_stress_score?: number;
|
|
364
364
|
intensity_factor?: number;
|
|
365
365
|
left_right_balance?: LeftRightBalance100;
|
|
366
|
+
end_position_lat?: number;
|
|
367
|
+
end_position_long?: number;
|
|
366
368
|
avg_stroke_count?: number;
|
|
367
369
|
avg_stroke_distance?: number;
|
|
368
370
|
swim_stroke?: SwimStroke;
|
|
@@ -387,10 +389,10 @@ export interface ParsedSession {
|
|
|
387
389
|
max_pos_vertical_speed?: number;
|
|
388
390
|
max_neg_vertical_speed?: number;
|
|
389
391
|
min_heart_rate?: number;
|
|
390
|
-
time_in_hr_zone?:
|
|
391
|
-
time_in_speed_zone?:
|
|
392
|
-
time_in_cadence_zone?:
|
|
393
|
-
time_in_power_zone?:
|
|
392
|
+
time_in_hr_zone?: number[];
|
|
393
|
+
time_in_speed_zone?: number[];
|
|
394
|
+
time_in_cadence_zone?: number[];
|
|
395
|
+
time_in_power_zone?: number[];
|
|
394
396
|
avg_lap_time?: number;
|
|
395
397
|
best_lap_index?: number;
|
|
396
398
|
min_altitude?: number;
|
|
@@ -418,6 +420,7 @@ export interface ParsedSession {
|
|
|
418
420
|
avg_left_pedal_smoothness?: number;
|
|
419
421
|
avg_right_pedal_smoothness?: number;
|
|
420
422
|
avg_combined_pedal_smoothness?: number;
|
|
423
|
+
sport_profile_name?: string;
|
|
421
424
|
sport_index?: number;
|
|
422
425
|
time_standing?: number;
|
|
423
426
|
stand_count?: number;
|
|
@@ -442,14 +445,24 @@ export interface ParsedSession {
|
|
|
442
445
|
avg_vertical_ratio?: number;
|
|
443
446
|
avg_stance_time_balance?: number;
|
|
444
447
|
avg_step_length?: number;
|
|
445
|
-
|
|
448
|
+
total_anaerobic_training_effect?: number;
|
|
446
449
|
avg_vam?: number;
|
|
450
|
+
recovery_advisor?: number;
|
|
451
|
+
min_temperature?: number;
|
|
452
|
+
training_load_peak?: number;
|
|
453
|
+
enhanced_avg_respiration_rate?: number;
|
|
454
|
+
enhanced_max_respiration_rate?: number;
|
|
455
|
+
est_sweat_loss?: number;
|
|
456
|
+
enhanced_min_respiration_rate?: number;
|
|
447
457
|
total_grit?: number;
|
|
448
458
|
total_flow?: number;
|
|
449
|
-
|
|
450
|
-
workout_rpe?: number;
|
|
459
|
+
jump_count?: number;
|
|
451
460
|
avg_grit?: number;
|
|
452
461
|
avg_flow?: number;
|
|
462
|
+
primary_benefit?: number;
|
|
463
|
+
workout_feel?: number;
|
|
464
|
+
workout_rpe?: number;
|
|
465
|
+
resting_calories?: number;
|
|
453
466
|
timestamp: string;
|
|
454
467
|
message_index?: MessageIndex;
|
|
455
468
|
laps?: ParsedLap[];
|
|
@@ -506,10 +519,10 @@ export interface ParsedLap {
|
|
|
506
519
|
avg_neg_vertical_speed?: number;
|
|
507
520
|
max_pos_vertical_speed?: number;
|
|
508
521
|
max_neg_vertical_speed?: number;
|
|
509
|
-
time_in_hr_zone?:
|
|
510
|
-
time_in_speed_zone?:
|
|
511
|
-
time_in_cadence_zone?:
|
|
512
|
-
time_in_power_zone?:
|
|
522
|
+
time_in_hr_zone?: number[];
|
|
523
|
+
time_in_speed_zone?: number[];
|
|
524
|
+
time_in_cadence_zone?: number[];
|
|
525
|
+
time_in_power_zone?: number[];
|
|
513
526
|
repetition_num?: number;
|
|
514
527
|
min_altitude?: number;
|
|
515
528
|
min_heart_rate?: number;
|
|
@@ -666,6 +679,7 @@ export interface ParsedDeviceInfo {
|
|
|
666
679
|
ant_transmission_type?: number;
|
|
667
680
|
ant_device_number?: number;
|
|
668
681
|
ant_network?: AntNetwork;
|
|
682
|
+
ant_id?: number;
|
|
669
683
|
source_type?: SourceType;
|
|
670
684
|
product_name?: string;
|
|
671
685
|
battery_level?: number;
|
|
@@ -823,7 +837,7 @@ export interface ParsedMonitoring {
|
|
|
823
837
|
timestamp: number;
|
|
824
838
|
}
|
|
825
839
|
export interface ParsedHrv {
|
|
826
|
-
time?:
|
|
840
|
+
time?: number[];
|
|
827
841
|
}
|
|
828
842
|
export interface ParsedLength {
|
|
829
843
|
event?: Event;
|
|
@@ -858,6 +872,7 @@ export interface ParsedOHrSettings {
|
|
|
858
872
|
timestamp: string;
|
|
859
873
|
}
|
|
860
874
|
export interface ParsedJump {
|
|
875
|
+
enhanced_mets?: number;
|
|
861
876
|
distance?: number;
|
|
862
877
|
height?: number;
|
|
863
878
|
score?: number;
|
|
@@ -875,11 +890,30 @@ export interface ParsedFieldDescription {
|
|
|
875
890
|
}
|
|
876
891
|
export interface ParsedDeveloperDataId {
|
|
877
892
|
developer_id?: number;
|
|
878
|
-
application_id?:
|
|
893
|
+
application_id?: number[];
|
|
879
894
|
manufacturer_id?: Manufacturer;
|
|
880
895
|
developer_data_index?: number;
|
|
881
896
|
application_version?: number;
|
|
882
897
|
}
|
|
898
|
+
export interface ParsedTimeInZone {
|
|
899
|
+
reference_mesg?: number;
|
|
900
|
+
reference_index?: number;
|
|
901
|
+
time_in_hr_zone?: number[];
|
|
902
|
+
time_in_speed_zone?: number[];
|
|
903
|
+
time_in_power_zone?: number[];
|
|
904
|
+
hr_zone_high_boundary_deprecated?: number[];
|
|
905
|
+
hr_zone_high_boundary?: number[];
|
|
906
|
+
speed_zone_high_boundary?: number[];
|
|
907
|
+
power_zone_high_boundary?: number[];
|
|
908
|
+
hr_calc_type?: HrZoneCalc;
|
|
909
|
+
max_heart_rate_deprecated?: number;
|
|
910
|
+
max_heart_rate?: number;
|
|
911
|
+
resting_heart_rate?: number;
|
|
912
|
+
threshold_heart_rate?: number;
|
|
913
|
+
pwr_calc_type?: PwrZoneCalc;
|
|
914
|
+
functional_threshold_power?: number;
|
|
915
|
+
timestamp: string;
|
|
916
|
+
}
|
|
883
917
|
export interface ParsedStressLevel {
|
|
884
918
|
stress_level_value?: number;
|
|
885
919
|
stress_level_time?: string;
|
|
@@ -952,23 +986,6 @@ export interface ParsedTankSummary {
|
|
|
952
986
|
end_pressure?: number;
|
|
953
987
|
volume_used?: number;
|
|
954
988
|
}
|
|
955
|
-
export interface ParsedTimeInZone {
|
|
956
|
-
timestamp: string;
|
|
957
|
-
reference_mesg?: number;
|
|
958
|
-
reference_index?: number;
|
|
959
|
-
time_in_hr_zone?: number[];
|
|
960
|
-
time_in_speed_zone?: number[];
|
|
961
|
-
time_in_power_zone?: number[];
|
|
962
|
-
hr_zone_high_boundary?: number[];
|
|
963
|
-
speed_zone_high_boundary?: number[];
|
|
964
|
-
power_zone_high_boundary?: number[];
|
|
965
|
-
hr_calc_type?: HrZoneCalc;
|
|
966
|
-
max_heart_rate?: number;
|
|
967
|
-
resting_heart_rate?: number;
|
|
968
|
-
threshold_heart_rate?: number;
|
|
969
|
-
pwr_calc_type?: PwrZoneCalc;
|
|
970
|
-
functional_threshold_power?: number;
|
|
971
|
-
}
|
|
972
989
|
export interface ParsedFit {
|
|
973
990
|
protocolVersion?: number;
|
|
974
991
|
profileVersion?: number;
|
package/dist/type_generator.js
CHANGED
|
@@ -40,8 +40,14 @@ export function generateArrayProperty(name, type, optional = true) {
|
|
|
40
40
|
}
|
|
41
41
|
export function generateTypeFromField(def) {
|
|
42
42
|
switch (def.type) {
|
|
43
|
+
case 'uint32_array':
|
|
44
|
+
case 'uint16_array':
|
|
45
|
+
case 'uint8_array':
|
|
46
|
+
case 'sint32_array':
|
|
47
|
+
case 'sint16_array':
|
|
48
|
+
case 'sint8_array':
|
|
43
49
|
case 'byte_array':
|
|
44
|
-
return ts.factory.createArrayTypeNode(ts.factory.createKeywordTypeNode(ts.SyntaxKind.
|
|
50
|
+
return ts.factory.createArrayTypeNode(ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword));
|
|
45
51
|
case 'bool':
|
|
46
52
|
return ts.factory.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword);
|
|
47
53
|
case 'date_time':
|
|
@@ -84,11 +90,7 @@ export function generateTypes(types) {
|
|
|
84
90
|
const typeAlias = ts.factory.createTypeAliasDeclaration([
|
|
85
91
|
ts.factory.createModifier(ts.SyntaxKind.ExportKeyword),
|
|
86
92
|
], snakeToCamel(name), undefined, ts.factory.createUnionTypeNode([...names.map(n => ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral(String(n)))), ...(name === 'mesg_num'
|
|
87
|
-
// TODO those are somehow missing in the FIT.types.mesg_num definition
|
|
88
|
-
// especially 'tank_update' (319 in messages) is mapped to 'tank_pressure' a bug?
|
|
89
93
|
? [
|
|
90
|
-
ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral('o_hr_settings')),
|
|
91
|
-
ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral('tank_update')),
|
|
92
94
|
ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral('definition')),
|
|
93
95
|
]
|
|
94
96
|
: [])]));
|
|
@@ -187,6 +189,7 @@ export function generateFitType() {
|
|
|
187
189
|
tank_updates: 'parsed_tank_update',
|
|
188
190
|
tank_summaries: 'parsed_tank_summary',
|
|
189
191
|
jumps: 'parsed_jump',
|
|
192
|
+
time_in_zone: 'parsed_time_in_zone',
|
|
190
193
|
};
|
|
191
194
|
const referenceProperties = {
|
|
192
195
|
file_creator: 'parsed_file_creator',
|
|
@@ -211,13 +214,18 @@ export function generateMessages(messages) {
|
|
|
211
214
|
const nodes = [];
|
|
212
215
|
Object.keys(messages).forEach((name) => {
|
|
213
216
|
const msg = FIT.messages[Number(name)];
|
|
217
|
+
const usedFields = new Set();
|
|
214
218
|
const messageType = ts.factory.createInterfaceDeclaration([
|
|
215
219
|
ts.factory.createModifier(ts.SyntaxKind.ExportKeyword),
|
|
216
220
|
], snakeToCamel(`parsed_${msg.name}`), undefined, undefined, [
|
|
217
|
-
...Object.keys(msg).filter(n => n !== 'name').
|
|
221
|
+
...Object.keys(msg).filter(n => n !== 'name').reduce((acc, id) => {
|
|
218
222
|
const def = msg[Number(id)];
|
|
219
|
-
|
|
220
|
-
|
|
223
|
+
if (!usedFields.has(def.field)) {
|
|
224
|
+
usedFields.add(def.field);
|
|
225
|
+
acc.push(generateProperty(def.field, generateTypeFromField(def), !['start_time', 'timestamp'].includes(def.field)));
|
|
226
|
+
}
|
|
227
|
+
return acc;
|
|
228
|
+
}, []),
|
|
221
229
|
...generateAdditionalFields(msg),
|
|
222
230
|
]);
|
|
223
231
|
nodes.push(messageType);
|
package/eslint.config.js
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fit-file-parser",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "2.2.
|
|
4
|
+
"version": "2.2.3",
|
|
5
5
|
"private": false,
|
|
6
6
|
"description": "Parse your .FIT files easily, directly from JS (Garmin, Polar, Suunto)",
|
|
7
7
|
"author": {
|
|
@@ -75,4 +75,4 @@
|
|
|
75
75
|
"typescript": "^5.9.3",
|
|
76
76
|
"vitest": "^4.0.13"
|
|
77
77
|
}
|
|
78
|
-
}
|
|
78
|
+
}
|
package/probe_session.js
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import fs from 'fs';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
// Note: Importing TS directly might not work with node without a loader.
|
|
6
|
-
// Better to use the built file if available, or use ts-node.
|
|
7
|
-
// But earlier I saw `dist/fit-parser.js`. Let's try to import that if key is default.
|
|
8
|
-
// Actually, let's just use `fs.readFileSync` and basic node.
|
|
9
|
-
// If `dist` is CJS, I can use `createRequire`.
|
|
10
|
-
|
|
11
|
-
import { createRequire } from 'module';
|
|
12
|
-
const require = createRequire(import.meta.url);
|
|
13
|
-
|
|
14
|
-
const FitParserDist = require('./dist/fit-parser.js').default;
|
|
15
|
-
|
|
16
|
-
const content = fs.readFileSync('/Users/dimitrios/Projects/sports-lib/samples/fit/jumps-mtb.fit');
|
|
17
|
-
const fitParser = new FitParserDist({
|
|
18
|
-
force: true,
|
|
19
|
-
speedUnit: 'km/h',
|
|
20
|
-
lengthUnit: 'm',
|
|
21
|
-
temperatureUnit: 'celsius',
|
|
22
|
-
elapsedRecordField: true,
|
|
23
|
-
mode: 'both',
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
fitParser.parse(content, (error, data) => {
|
|
27
|
-
if (error) {
|
|
28
|
-
console.error(error);
|
|
29
|
-
} else {
|
|
30
|
-
const session = data.sessions[0];
|
|
31
|
-
if (session) {
|
|
32
|
-
console.log("=== SESSION KEYS & VALUES ===");
|
|
33
|
-
Object.keys(session).forEach(key => {
|
|
34
|
-
const val = session[key];
|
|
35
|
-
console.log(`${key}: ${val}`);
|
|
36
|
-
if (Math.abs(val - 11) < 0.1) console.log(`>>> MATCH 11 (Jump Count?): ${key}`);
|
|
37
|
-
if (Math.abs(val - 159) < 0.1) console.log(`>>> MATCH 159 (Resting Cals?): ${key}`);
|
|
38
|
-
if (Math.abs(val - 6079174) < 1) console.log(`>>> MATCH 6M (Training Load?): ${key}`);
|
|
39
|
-
if (Math.abs(val - 100) < 0.1) console.log(`>>> MATCH 100 (Avg VAM?): ${key}`);
|
|
40
|
-
if (Math.abs(val - 2756) < 1) console.log(`>>> MATCH 2756 (Avg Resp?): ${key}`);
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
});
|