fit-file-parser 2.1.1 → 2.2.2
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/debug_output.json +413301 -0
- package/deep_probe.js +70 -0
- package/dist/binary.js +22 -4
- package/dist/cjs/binary.js +22 -4
- package/dist/cjs/fit-parser.js +10 -0
- package/dist/cjs/fit.js +276 -4
- package/dist/cjs/fit_types.d.ts +61 -12
- package/dist/cjs/type_generator.js +9 -5
- package/dist/fit-parser.js +10 -0
- package/dist/fit.js +276 -4
- package/dist/fit_types.d.ts +61 -12
- package/dist/type_generator.js +9 -5
- package/eslint.config.js +5 -0
- package/package.json +1 -1
- package/probe_session.js +49 -0
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,10 +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;
|
|
457
|
+
total_grit?: number;
|
|
458
|
+
jump_count?: number;
|
|
459
|
+
avg_flow?: number;
|
|
460
|
+
primary_benefit?: number;
|
|
447
461
|
workout_feel?: number;
|
|
448
462
|
workout_rpe?: number;
|
|
463
|
+
resting_calories?: number;
|
|
464
|
+
avg_grit?: number;
|
|
465
|
+
avg_flow?: number;
|
|
449
466
|
timestamp: string;
|
|
450
467
|
message_index?: MessageIndex;
|
|
451
468
|
laps?: ParsedLap[];
|
|
@@ -502,10 +519,10 @@ export interface ParsedLap {
|
|
|
502
519
|
avg_neg_vertical_speed?: number;
|
|
503
520
|
max_pos_vertical_speed?: number;
|
|
504
521
|
max_neg_vertical_speed?: number;
|
|
505
|
-
time_in_hr_zone?:
|
|
506
|
-
time_in_speed_zone?:
|
|
507
|
-
time_in_cadence_zone?:
|
|
508
|
-
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[];
|
|
509
526
|
repetition_num?: number;
|
|
510
527
|
min_altitude?: number;
|
|
511
528
|
min_heart_rate?: number;
|
|
@@ -627,6 +644,8 @@ export interface ParsedRecord {
|
|
|
627
644
|
ndl_time?: number;
|
|
628
645
|
cns_load?: number;
|
|
629
646
|
n2_load?: number;
|
|
647
|
+
grit?: number;
|
|
648
|
+
flow?: number;
|
|
630
649
|
timestamp: string;
|
|
631
650
|
}
|
|
632
651
|
export interface ParsedEvent {
|
|
@@ -660,8 +679,10 @@ export interface ParsedDeviceInfo {
|
|
|
660
679
|
ant_transmission_type?: number;
|
|
661
680
|
ant_device_number?: number;
|
|
662
681
|
ant_network?: AntNetwork;
|
|
682
|
+
ant_id?: number;
|
|
663
683
|
source_type?: SourceType;
|
|
664
684
|
product_name?: string;
|
|
685
|
+
battery_level?: number;
|
|
665
686
|
timestamp: string;
|
|
666
687
|
}
|
|
667
688
|
export interface ParsedWorkout {
|
|
@@ -816,7 +837,7 @@ export interface ParsedMonitoring {
|
|
|
816
837
|
timestamp: number;
|
|
817
838
|
}
|
|
818
839
|
export interface ParsedHrv {
|
|
819
|
-
time?:
|
|
840
|
+
time?: number[];
|
|
820
841
|
}
|
|
821
842
|
export interface ParsedLength {
|
|
822
843
|
event?: Event;
|
|
@@ -850,6 +871,13 @@ export interface ParsedOHrSettings {
|
|
|
850
871
|
enabled?: number;
|
|
851
872
|
timestamp: string;
|
|
852
873
|
}
|
|
874
|
+
export interface ParsedJump {
|
|
875
|
+
enhanced_mets?: number;
|
|
876
|
+
distance?: number;
|
|
877
|
+
height?: number;
|
|
878
|
+
score?: number;
|
|
879
|
+
timestamp: string;
|
|
880
|
+
}
|
|
853
881
|
export interface ParsedFieldDescription {
|
|
854
882
|
developer_data_index?: number;
|
|
855
883
|
field_definition_number?: number;
|
|
@@ -862,11 +890,30 @@ export interface ParsedFieldDescription {
|
|
|
862
890
|
}
|
|
863
891
|
export interface ParsedDeveloperDataId {
|
|
864
892
|
developer_id?: number;
|
|
865
|
-
application_id?:
|
|
893
|
+
application_id?: number[];
|
|
866
894
|
manufacturer_id?: Manufacturer;
|
|
867
895
|
developer_data_index?: number;
|
|
868
896
|
application_version?: number;
|
|
869
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
|
+
}
|
|
870
917
|
export interface ParsedStressLevel {
|
|
871
918
|
stress_level_value?: number;
|
|
872
919
|
stress_level_time?: string;
|
|
@@ -971,4 +1018,6 @@ export interface ParsedFit {
|
|
|
971
1018
|
definitions?: unknown[];
|
|
972
1019
|
tank_updates?: ParsedTankUpdate[];
|
|
973
1020
|
tank_summaries?: ParsedTankSummary[];
|
|
1021
|
+
jumps?: ParsedJump[];
|
|
1022
|
+
time_in_zone?: ParsedTimeInZone[];
|
|
974
1023
|
}
|
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
|
: [])]));
|
|
@@ -186,6 +188,8 @@ export function generateFitType() {
|
|
|
186
188
|
definitions: 'unknown',
|
|
187
189
|
tank_updates: 'parsed_tank_update',
|
|
188
190
|
tank_summaries: 'parsed_tank_summary',
|
|
191
|
+
jumps: 'parsed_jump',
|
|
192
|
+
time_in_zone: 'parsed_time_in_zone',
|
|
189
193
|
};
|
|
190
194
|
const referenceProperties = {
|
|
191
195
|
file_creator: 'parsed_file_creator',
|
package/eslint.config.js
CHANGED
package/package.json
CHANGED
package/probe_session.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { createRequire } from 'module'
|
|
2
|
+
|
|
3
|
+
// Note: Importing TS directly might not work with node without a loader.
|
|
4
|
+
// Better to use the built file if available, or use ts-node.
|
|
5
|
+
// But earlier I saw `dist/fit-parser.js`. Let's try to import that if key is default.
|
|
6
|
+
// Actually, let's just use `fs.readFileSync` and basic node.
|
|
7
|
+
// If `dist` is CJS, I can use `createRequire`.
|
|
8
|
+
|
|
9
|
+
import fs from 'node:fs'
|
|
10
|
+
|
|
11
|
+
const require = createRequire(import.meta.url)
|
|
12
|
+
|
|
13
|
+
const FitParserDist = require('./dist/fit-parser.js').default
|
|
14
|
+
|
|
15
|
+
const content = fs.readFileSync('/Users/dimitrios/Projects/sports-lib/samples/fit/jumps-mtb.fit')
|
|
16
|
+
const fitParser = new FitParserDist({
|
|
17
|
+
force: true,
|
|
18
|
+
speedUnit: 'km/h',
|
|
19
|
+
lengthUnit: 'm',
|
|
20
|
+
temperatureUnit: 'celsius',
|
|
21
|
+
elapsedRecordField: true,
|
|
22
|
+
mode: 'both',
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
fitParser.parse(content, (error, data) => {
|
|
26
|
+
if (error) {
|
|
27
|
+
console.error(error)
|
|
28
|
+
}
|
|
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)
|
|
37
|
+
console.log(`>>> MATCH 11 (Jump Count?): ${key}`)
|
|
38
|
+
if (Math.abs(val - 159) < 0.1)
|
|
39
|
+
console.log(`>>> MATCH 159 (Resting Cals?): ${key}`)
|
|
40
|
+
if (Math.abs(val - 6079174) < 1)
|
|
41
|
+
console.log(`>>> MATCH 6M (Training Load?): ${key}`)
|
|
42
|
+
if (Math.abs(val - 100) < 0.1)
|
|
43
|
+
console.log(`>>> MATCH 100 (Avg VAM?): ${key}`)
|
|
44
|
+
if (Math.abs(val - 2756) < 1)
|
|
45
|
+
console.log(`>>> MATCH 2756 (Avg Resp?): ${key}`)
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
})
|