fit-file-parser 3.0.1 → 3.1.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/CHANGELOG.md +5 -0
- package/README.md +25 -0
- package/dist/cjs/fit-encoder.d.ts +68 -0
- package/dist/cjs/fit-encoder.js +354 -0
- package/dist/cjs/fit-parser.d.ts +2 -1
- package/dist/cjs/fit-parser.js +4 -0
- package/dist/cjs/fit.js +28 -0
- package/dist/cjs/fit_types.d.ts +1 -1
- package/dist/fit-encoder.d.ts +68 -0
- package/dist/fit-encoder.js +350 -0
- package/dist/fit-parser.d.ts +2 -1
- package/dist/fit-parser.js +1 -0
- package/dist/fit.js +28 -0
- package/dist/fit_types.d.ts +1 -1
- package/package.json +9 -2
- package/.agent/rules/fit-parser-dev.md +0 -35
- package/.agent/skills/add-fit-message/SKILL.md +0 -29
- package/.agent/workflows/fit-workflows.md +0 -28
- package/.editorconfig +0 -3
- package/.github/workflows/ci.yml +0 -40
- package/.vscode/extensions.json +0 -6
- package/.vscode/launch.json +0 -27
- package/.vscode/tasks.json +0 -34
- package/INVESTIGATING.md +0 -50
- package/check_ids.js +0 -24
- package/check_jump_fields.js +0 -46
- package/codegen/codegen.ts +0 -9
- package/eslint.config.js +0 -20
- package/find_field.js +0 -23
- package/find_field_esm.js +0 -28
- package/inspect_parser.ts +0 -34
- package/output.txt +0 -154
- package/scan_jumps.js +0 -29
- package/scripts/deep_probe.js +0 -74
- package/scripts/inspect_fit.js +0 -69
- package/tsconfig.cjs.json +0 -8
- package/tsconfig.json +0 -16
- package/vitest.config.js +0 -8
package/.vscode/tasks.json
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": "2.0.0",
|
|
3
|
-
"tasks": [
|
|
4
|
-
{
|
|
5
|
-
"type": "npm",
|
|
6
|
-
"script": "dev",
|
|
7
|
-
"group": "build",
|
|
8
|
-
"isBackground": true,
|
|
9
|
-
"problemMatcher": {
|
|
10
|
-
"owner": "typescript",
|
|
11
|
-
"pattern": "$tsc",
|
|
12
|
-
"background": {
|
|
13
|
-
"activeOnStart": true,
|
|
14
|
-
"beginsPattern": {
|
|
15
|
-
"regexp": "^.*Starting compilation in watch mode.*$"
|
|
16
|
-
},
|
|
17
|
-
"endsPattern": {
|
|
18
|
-
"regexp": "^.*Found [0-9]+ errors.*$"
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
},
|
|
22
|
-
"label": "npm: dev",
|
|
23
|
-
"detail": "tsx --watch --watch-path=src/type_generator.ts --watch-path=src/fit.ts codegen/codegen.ts"
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
"type": "npm",
|
|
27
|
-
"script": "test",
|
|
28
|
-
"group": "test",
|
|
29
|
-
"problemMatcher": [],
|
|
30
|
-
"label": "npm: test",
|
|
31
|
-
"detail": "vitest"
|
|
32
|
-
}
|
|
33
|
-
]
|
|
34
|
-
}
|
package/INVESTIGATING.md
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
# Investigating FIT Files
|
|
2
|
-
|
|
3
|
-
This guide explains how to investigate FIT files when you suspect fields are missing or incorrectly parsed, and how to add support for new messages.
|
|
4
|
-
|
|
5
|
-
## Tools Included
|
|
6
|
-
|
|
7
|
-
We provide scripts in the `scripts/` directory to help you probe FIT files.
|
|
8
|
-
|
|
9
|
-
### 1. Inspecting Parsed Data (`scripts/inspect_fit.js`)
|
|
10
|
-
|
|
11
|
-
Use this script to see what the parser currently outputs for a file.
|
|
12
|
-
|
|
13
|
-
```bash
|
|
14
|
-
# View summary of all messages in file
|
|
15
|
-
node scripts/inspect_fit.js examples/jumps-mtb.fit
|
|
16
|
-
|
|
17
|
-
# View specific message details
|
|
18
|
-
node scripts/inspect_fit.js examples/jumps-mtb.fit jumps
|
|
19
|
-
node scripts/inspect_fit.js examples/jumps-mtb.fit session
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
### 2. Probing for Unknown Data (`scripts/deep_probe.js`)
|
|
23
|
-
|
|
24
|
-
Use this script when you know a value exists (e.g., from Garmin Connect or another tool) but can't find it in the parser output. It recursively searches the raw parsed structure for that value.
|
|
25
|
-
|
|
26
|
-
```bash
|
|
27
|
-
# Edit the script to set the value you're looking for, then run:
|
|
28
|
-
node scripts/deep_probe.js
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
## Workflow for Missing Fields
|
|
32
|
-
|
|
33
|
-
If you suspect a field is missing (e.g., "Jump Hang Time"):
|
|
34
|
-
|
|
35
|
-
1. **Verify it exists**: Check the file with an external tool or online viewer. Note the exact value (e.g., `0.36` seconds).
|
|
36
|
-
2. **Probe**: Use `deep_probe.js` or inspect the `examples/` output to see if the value appears in an unknown field (e.g., `field_123`).
|
|
37
|
-
3. **Identify Message ID**:
|
|
38
|
-
* Check `src/fit.ts` for the message definition.
|
|
39
|
-
* If the message ID seems wrong, or fields are missing, compare with the **Official Garmin FIT SDK**.
|
|
40
|
-
* *Tip: You can find the official SDK in `@garmin/fitsdk` if installed, or search online.*
|
|
41
|
-
4. **Fix**:
|
|
42
|
-
* Update `src/fit.ts` with the correct Message ID and Field IDs.
|
|
43
|
-
* Run `npm run codegen` to update types.
|
|
44
|
-
* Add a test case in `test/` relative to your new message.
|
|
45
|
-
|
|
46
|
-
## Common Issues
|
|
47
|
-
|
|
48
|
-
* **Wrong Message ID**: Sometimes `fit-parser` has legacy or guessed IDs (e.g., `jump` was 140, but official is 285).
|
|
49
|
-
* **Missing Fields**: New devices add new fields. They often appear as `unknown_field_X`.
|
|
50
|
-
* **Scale/Offset**: If values look huge (e.g., 20838184 instead of 20.838), check if they need a scale factor (e.g., `semicircles` to `degrees`).
|
package/check_ids.js
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import FitParser from './dist/fit-parser.js';
|
|
3
|
-
|
|
4
|
-
const content = fs.readFileSync('../sports-lib/samples/fit/jumps-mtb.fit');
|
|
5
|
-
const fitParser = new (FitParser.default || FitParser)({ force: true, mode: 'both' });
|
|
6
|
-
|
|
7
|
-
fitParser.parse(content, (error, data) => {
|
|
8
|
-
if (error) { console.error(error); return; }
|
|
9
|
-
|
|
10
|
-
// The parser stores messages in data[messageName]
|
|
11
|
-
// Any unknown messages are in data[messageId] (if numeric)
|
|
12
|
-
console.log('Keys in data:', Object.keys(data));
|
|
13
|
-
|
|
14
|
-
// Let's check for anything that looks like a message ID
|
|
15
|
-
for (const key of Object.keys(data)) {
|
|
16
|
-
if (!isNaN(parseInt(key))) {
|
|
17
|
-
console.log(`Unknown Message ID found: ${key} (${data[key].length} items)`);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
if (data.jumps) {
|
|
22
|
-
console.log('Jumps fields:', Object.keys(data.jumps[0]));
|
|
23
|
-
}
|
|
24
|
-
});
|
package/check_jump_fields.js
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import fs from 'fs'
|
|
2
|
-
import { createRequire } from 'module'
|
|
3
|
-
|
|
4
|
-
const require = createRequire(import.meta.url)
|
|
5
|
-
const FitParser = require('./dist/fit-parser.js').default
|
|
6
|
-
|
|
7
|
-
const content = fs.readFileSync('./examples/jumps-mtb.fit')
|
|
8
|
-
const fitParser = new FitParser({
|
|
9
|
-
force: true,
|
|
10
|
-
speedUnit: 'm/s',
|
|
11
|
-
lengthUnit: 'm',
|
|
12
|
-
temperatureUnit: 'celsius',
|
|
13
|
-
elapsedRecordField: true,
|
|
14
|
-
mode: 'both',
|
|
15
|
-
})
|
|
16
|
-
|
|
17
|
-
fitParser.parse(content, (error, data) => {
|
|
18
|
-
if (error) {
|
|
19
|
-
console.error(error)
|
|
20
|
-
return
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
console.log('=== JUMPS ARRAY ===')
|
|
24
|
-
if (data.jumps && data.jumps.length > 0) {
|
|
25
|
-
data.jumps.forEach((jump, i) => {
|
|
26
|
-
console.log(`\nJump ${i + 1}:`)
|
|
27
|
-
Object.keys(jump).forEach((key) => {
|
|
28
|
-
console.log(` ${key}: ${JSON.stringify(jump[key])}`)
|
|
29
|
-
})
|
|
30
|
-
})
|
|
31
|
-
}
|
|
32
|
-
else {
|
|
33
|
-
console.log('No jumps found')
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// Also check definitions array for message 140 (jump)
|
|
37
|
-
console.log('\n=== DEFINITIONS FOR MESSAGE 140 (JUMP) ===')
|
|
38
|
-
if (data.definitions) {
|
|
39
|
-
const jumpDefs = data.definitions.filter(d => d && d.messageType === 140)
|
|
40
|
-
console.log(`Found ${jumpDefs.length} jump definitions`)
|
|
41
|
-
jumpDefs.forEach((def, i) => {
|
|
42
|
-
console.log(`\nDefinition ${i + 1}:`)
|
|
43
|
-
console.log(JSON.stringify(def, null, 2))
|
|
44
|
-
})
|
|
45
|
-
}
|
|
46
|
-
})
|
package/codegen/codegen.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { execSync } from 'node:child_process'
|
|
2
|
-
import fs from 'node:fs'
|
|
3
|
-
import { main } from '../src/type_generator.js'
|
|
4
|
-
|
|
5
|
-
const out = main()
|
|
6
|
-
|
|
7
|
-
fs.writeFileSync(`${import.meta.dirname}/../src/fit_types.ts`, out)
|
|
8
|
-
|
|
9
|
-
execSync('npx eslint --fix src/fit_types.ts', { stdio: 'inherit' })
|
package/eslint.config.js
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { antfu } from '@antfu/eslint-config'
|
|
2
|
-
|
|
3
|
-
export default antfu({
|
|
4
|
-
type: 'lib',
|
|
5
|
-
gitignore: true,
|
|
6
|
-
ignores: ['**/test/*.json'],
|
|
7
|
-
stylistic: {
|
|
8
|
-
indent: 2,
|
|
9
|
-
quotes: 'single',
|
|
10
|
-
},
|
|
11
|
-
formatters: true,
|
|
12
|
-
rules: {
|
|
13
|
-
'unicorn/prefer-node-protocol': 'off',
|
|
14
|
-
},
|
|
15
|
-
}, {
|
|
16
|
-
files: ['**/examples/*.js', '**/codegen/*.ts', '**/probe_session.js', '**/deep_probe.js'],
|
|
17
|
-
rules: {
|
|
18
|
-
'no-console': 'off',
|
|
19
|
-
},
|
|
20
|
-
})
|
package/find_field.js
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
const FitParser = require('./dist/fit-parser.js').default;
|
|
3
|
-
|
|
4
|
-
const content = fs.readFileSync('../sports-lib/samples/fit/jumps-mtb.fit');
|
|
5
|
-
const fitParser = new FitParser({ force: true, mode: 'both' });
|
|
6
|
-
|
|
7
|
-
fitParser.parse(content, (error, data) => {
|
|
8
|
-
if (error) { console.error(error); return; }
|
|
9
|
-
|
|
10
|
-
function search(obj, path = '') {
|
|
11
|
-
if (!obj || typeof obj !== 'object') return;
|
|
12
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
13
|
-
const currentPath = path ? `${path}.${key}` : key;
|
|
14
|
-
if (key === 'enhanced_mets') {
|
|
15
|
-
console.log(`FOUND enhanced_mets at ${currentPath}: ${value}`);
|
|
16
|
-
}
|
|
17
|
-
search(value, currentPath);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
search(data);
|
|
22
|
-
console.log('Search finished.');
|
|
23
|
-
});
|
package/find_field_esm.js
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import FitParser from './dist/fit-parser.js';
|
|
3
|
-
|
|
4
|
-
const content = fs.readFileSync('../sports-lib/samples/fit/jumps-mtb.fit');
|
|
5
|
-
// Try both ways to instantiate
|
|
6
|
-
const fitParser = new (FitParser.default || FitParser)({ force: true, mode: 'both' });
|
|
7
|
-
|
|
8
|
-
fitParser.parse(content, (error, data) => {
|
|
9
|
-
if (error) { console.error(error); return; }
|
|
10
|
-
|
|
11
|
-
function search(obj, path = '') {
|
|
12
|
-
if (!obj || typeof obj !== 'object') return;
|
|
13
|
-
if (Array.isArray(obj)) {
|
|
14
|
-
obj.forEach((item, index) => search(item, `${path}[${index}]`));
|
|
15
|
-
return;
|
|
16
|
-
}
|
|
17
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
18
|
-
const currentPath = path ? `${path}.${key}` : key;
|
|
19
|
-
if (key === 'enhanced_mets') {
|
|
20
|
-
console.log(`FOUND enhanced_mets at ${currentPath}: ${value}`);
|
|
21
|
-
}
|
|
22
|
-
search(value, currentPath);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
search(data);
|
|
27
|
-
console.log('Search finished.');
|
|
28
|
-
});
|
package/inspect_parser.ts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import FitParser from './src/fit-parser';
|
|
3
|
-
import * as fs from 'fs';
|
|
4
|
-
|
|
5
|
-
const filePath = 'examples/road-with-power.fit';
|
|
6
|
-
|
|
7
|
-
async function run() {
|
|
8
|
-
try {
|
|
9
|
-
const content = fs.readFileSync(filePath);
|
|
10
|
-
const parser = new FitParser({
|
|
11
|
-
force: true,
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
parser.parse(content, (error: any, data: any) => {
|
|
15
|
-
if (error) {
|
|
16
|
-
console.error(error);
|
|
17
|
-
} else {
|
|
18
|
-
console.log('Parsed successfully');
|
|
19
|
-
// Check sessions for power zone data
|
|
20
|
-
if (data.sessions && data.sessions.length > 0) {
|
|
21
|
-
const session = data.sessions[0];
|
|
22
|
-
console.log('Session keys:', Object.keys(session));
|
|
23
|
-
console.log('time_in_power_zone:', session.time_in_power_zone);
|
|
24
|
-
console.log('time_in_hr_zone:', session.time_in_hr_zone);
|
|
25
|
-
console.log('time_in_speed_zone:', session.time_in_speed_zone);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
});
|
|
29
|
-
} catch (e) {
|
|
30
|
-
console.error(e);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
run();
|
package/output.txt
DELETED
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
>>> LOADING BINARY.TS <<<
|
|
2
|
-
[DEBUG] Session field: 253 (timestamp), Type: date_time, Data: 1137663486
|
|
3
|
-
[DEBUG] Session field: 2 (start_time), Type: date_time, Data: 1137663486
|
|
4
|
-
[DEBUG] Session field: 3 (start_position_lat), Type: sint32, Data: 472983081
|
|
5
|
-
[DEBUG] Session field: 4 (start_position_long), Type: sint32, Data: 250374956
|
|
6
|
-
[DEBUG] Session field: 7 (total_elapsed_time), Type: uint32, Data: 14455786
|
|
7
|
-
[DEBUG] Session field: 8 (total_timer_time), Type: uint32, Data: 14455786
|
|
8
|
-
[DEBUG] Session field: 9 (total_distance), Type: uint32, Data: 3628685
|
|
9
|
-
[DEBUG] Session field: 10 (total_cycles), Type: uint32, Data: 4294967295
|
|
10
|
-
[DEBUG] Session field: 29 (nec_lat), Type: sint32, Data: 473011054
|
|
11
|
-
[DEBUG] Session field: 30 (nec_long), Type: sint32, Data: 250518365
|
|
12
|
-
[DEBUG] Session field: 31 (swc_lat), Type: sint32, Data: 472847023
|
|
13
|
-
[DEBUG] Session field: 32 (swc_long), Type: sint32, Data: 250368555
|
|
14
|
-
[DEBUG] Session field: 38 (end_position_lat), Type: sint32, Data: 472990941
|
|
15
|
-
[DEBUG] Session field: 39 (end_position_long), Type: sint32, Data: 250515862
|
|
16
|
-
[DEBUG] Session field: 48 (total_work), Type: uint32, Data: 4294967295
|
|
17
|
-
[DEBUG] Session field: 110 (sport_profile_name), Type: string, Data: DOWNHILL MTB
|
|
18
|
-
[DEBUG] Session field: 112 (time_standing), Type: uint32, Data: 4294967295
|
|
19
|
-
[DEBUG] Session field: 116 (avg_left_power_phase), Type: uint8, Data: 255
|
|
20
|
-
[DEBUG] Session field: 117 (avg_left_power_phase_peak), Type: uint8, Data: 255
|
|
21
|
-
[DEBUG] Session field: 118 (avg_right_power_phase), Type: uint8, Data: 255
|
|
22
|
-
[DEBUG] Session field: 119 (avg_right_power_phase_peak), Type: uint8, Data: 255
|
|
23
|
-
[DEBUG] Session field: 120 (avg_power_position), Type: uint16, Data: 65535
|
|
24
|
-
[DEBUG] Session field: 121 (max_power_position), Type: uint16, Data: 65535
|
|
25
|
-
[DEBUG] Session field: 124 (enhanced_avg_speed), Type: uint32, Data: 2510
|
|
26
|
-
[DEBUG] Session field: 125 (enhanced_max_speed), Type: uint32, Data: 11561
|
|
27
|
-
[DEBUG] Session field: 168 (training_load_peak), Type: uint32, Data: 10061199
|
|
28
|
-
[DEBUG] Session field: 181 (total_grit), Type: float32, Data: 180.6374969482422
|
|
29
|
-
[DEBUG] Session field: 187 (avg_flow), Type: float32, Data: 5.879695892333984
|
|
30
|
-
[DEBUG] Session field: 203 (undefined), Type: undefined, Data: 4294967295
|
|
31
|
-
[DEBUG] Session field: 224 (undefined), Type: undefined, Data: 2147483647
|
|
32
|
-
[DEBUG] Session field: 225 (undefined), Type: undefined, Data: 2147483647
|
|
33
|
-
[DEBUG] Session field: 226 (undefined), Type: undefined, Data: 2147483647
|
|
34
|
-
[DEBUG] Session field: 229 (undefined), Type: undefined, Data: 4294967295
|
|
35
|
-
[DEBUG] Session field: 254 (message_index), Type: message_index, Data: 0
|
|
36
|
-
[DEBUG] Session field: 11 (total_calories), Type: uint16, Data: 1215
|
|
37
|
-
[DEBUG] Session field: 13 (total_fat_calories), Type: uint16, Data: 65535
|
|
38
|
-
[DEBUG] Session field: 20 (avg_power), Type: uint16, Data: 65535
|
|
39
|
-
[DEBUG] Session field: 21 (max_power), Type: uint16, Data: 65535
|
|
40
|
-
[DEBUG] Session field: 22 (total_ascent), Type: uint16, Data: 1633
|
|
41
|
-
[DEBUG] Session field: 23 (total_descent), Type: uint16, Data: 1880
|
|
42
|
-
[DEBUG] Session field: 25 (first_lap_index), Type: uint16, Data: 0
|
|
43
|
-
[DEBUG] Session field: 26 (num_laps), Type: uint16, Data: 1
|
|
44
|
-
[DEBUG] Session field: 34 (normalized_power), Type: uint16, Data: 65535
|
|
45
|
-
[DEBUG] Session field: 35 (training_stress_score), Type: uint16, Data: 65535
|
|
46
|
-
[DEBUG] Session field: 36 (intensity_factor), Type: uint16, Data: 65535
|
|
47
|
-
[DEBUG] Session field: 37 (left_right_balance), Type: left_right_balance_100, Data: 65535
|
|
48
|
-
[DEBUG] Session field: 45 (threshold_power), Type: uint16, Data: 65535
|
|
49
|
-
[DEBUG] Session field: 106 (undefined), Type: undefined, Data: 0
|
|
50
|
-
[DEBUG] Session field: 107 (undefined), Type: undefined, Data: 65535
|
|
51
|
-
[DEBUG] Session field: 108 (undefined), Type: undefined, Data: 178
|
|
52
|
-
[DEBUG] Session field: 113 (stand_count), Type: uint16, Data: 65535
|
|
53
|
-
[DEBUG] Session field: 139 (avg_vam), Type: uint16, Data: 189
|
|
54
|
-
[DEBUG] Session field: 151 (undefined), Type: undefined, Data: 65535
|
|
55
|
-
[DEBUG] Session field: 169 (enhanced_avg_respiration_rate), Type: uint16, Data: 2295
|
|
56
|
-
[DEBUG] Session field: 170 (enhanced_max_respiration_rate), Type: uint16, Data: 4511
|
|
57
|
-
[DEBUG] Session field: 177 (undefined), Type: undefined, Data: 65535
|
|
58
|
-
[DEBUG] Session field: 178 (est_sweat_loss), Type: uint16, Data: 1455
|
|
59
|
-
[DEBUG] Session field: 179 (undefined), Type: undefined, Data: 65535
|
|
60
|
-
[DEBUG] Session field: 180 (enhanced_min_respiration_rate), Type: uint16, Data: 1040
|
|
61
|
-
[DEBUG] Session field: 183 (jump_count), Type: uint16, Data: 129
|
|
62
|
-
[DEBUG] Session field: 196 (resting_calories), Type: uint16, Data: 317
|
|
63
|
-
[DEBUG] Session field: 204 (undefined), Type: undefined, Data: 65535
|
|
64
|
-
[DEBUG] Session field: 0 (event), Type: event, Data: 8
|
|
65
|
-
[DEBUG] Session field: 1 (event_type), Type: event_type, Data: 1
|
|
66
|
-
[DEBUG] Session field: 5 (sport), Type: sport, Data: 2
|
|
67
|
-
[DEBUG] Session field: 6 (sub_sport), Type: sub_sport, Data: 9
|
|
68
|
-
[DEBUG] Session field: 16 (avg_heart_rate), Type: uint8, Data: 108
|
|
69
|
-
[DEBUG] Session field: 17 (max_heart_rate), Type: uint8, Data: 179
|
|
70
|
-
[DEBUG] Session field: 18 (avg_cadence), Type: uint8, Data: 255
|
|
71
|
-
[DEBUG] Session field: 19 (max_cadence), Type: uint8, Data: 255
|
|
72
|
-
[DEBUG] Session field: 24 (total_training_effect), Type: uint8, Data: 31
|
|
73
|
-
[DEBUG] Session field: 27 (event_group), Type: uint8, Data: 255
|
|
74
|
-
[DEBUG] Session field: 28 (trigger), Type: session_trigger, Data: 0
|
|
75
|
-
[DEBUG] Session field: 57 (avg_temperature), Type: sint8, Data: 8
|
|
76
|
-
[DEBUG] Session field: 58 (max_temperature), Type: sint8, Data: 16
|
|
77
|
-
[DEBUG] Session field: 81 (undefined), Type: undefined, Data: 0
|
|
78
|
-
[DEBUG] Session field: 92 (avg_fractional_cadence), Type: uint8, Data: 255
|
|
79
|
-
[DEBUG] Session field: 93 (max_fractional_cadence), Type: uint8, Data: 255
|
|
80
|
-
[DEBUG] Session field: 94 (total_fractional_cycles), Type: uint8, Data: 255
|
|
81
|
-
[DEBUG] Session field: 101 (avg_left_torque_effectiveness), Type: uint8, Data: 255
|
|
82
|
-
[DEBUG] Session field: 102 (avg_right_torque_effectiveness), Type: uint8, Data: 255
|
|
83
|
-
[DEBUG] Session field: 103 (avg_left_pedal_smoothness), Type: uint8, Data: 255
|
|
84
|
-
[DEBUG] Session field: 104 (avg_right_pedal_smoothness), Type: uint8, Data: 255
|
|
85
|
-
[DEBUG] Session field: 105 (avg_combined_pedal_smoothness), Type: uint8, Data: 255
|
|
86
|
-
[DEBUG] Session field: 109 (undefined), Type: undefined, Data: 255
|
|
87
|
-
[DEBUG] Session field: 111 (sport_index), Type: uint8, Data: 255
|
|
88
|
-
[DEBUG] Session field: 114 (avg_left_pco), Type: sint8, Data: 127
|
|
89
|
-
[DEBUG] Session field: 115 (avg_right_pco), Type: sint8, Data: 127
|
|
90
|
-
[DEBUG] Session field: 122 (avg_cadence_position), Type: uint8, Data: 255
|
|
91
|
-
[DEBUG] Session field: 123 (max_cadence_position), Type: uint8, Data: 255
|
|
92
|
-
[DEBUG] Session field: 137 (total_anaerobic_training_effect), Type: uint8, Data: 31
|
|
93
|
-
[DEBUG] Session field: 138 (undefined), Type: undefined, Data: 8
|
|
94
|
-
[DEBUG] Session field: 150 (min_temperature), Type: sint8, Data: 4
|
|
95
|
-
[DEBUG] Session field: 184 (undefined), Type: undefined, Data: 0
|
|
96
|
-
[DEBUG] Session field: 185 (undefined), Type: undefined, Data: 255
|
|
97
|
-
[DEBUG] Session field: 188 (primary_benefit), Type: uint8, Data: 6
|
|
98
|
-
[DEBUG] Session field: 192 (workout_feel), Type: uint8, Data: 255
|
|
99
|
-
[DEBUG] Session field: 193 (workout_rpe), Type: uint8, Data: 255
|
|
100
|
-
[DEBUG] Session field: 202 (undefined), Type: undefined, Data: 255
|
|
101
|
-
[DEBUG] Session field: 205 (undefined), Type: undefined, Data: 255
|
|
102
|
-
[DEBUG] Session field: 206 (undefined), Type: undefined, Data: 255
|
|
103
|
-
[DEBUG] Session field: 207 (undefined), Type: undefined, Data: 255
|
|
104
|
-
[DEBUG] Session field: 219 (undefined), Type: undefined, Data: 45
|
|
105
|
-
Parsed successfully
|
|
106
|
-
Session keys: [
|
|
107
|
-
'timestamp',
|
|
108
|
-
'start_time',
|
|
109
|
-
'start_position_lat',
|
|
110
|
-
'start_position_long',
|
|
111
|
-
'total_elapsed_time',
|
|
112
|
-
'total_timer_time',
|
|
113
|
-
'total_distance',
|
|
114
|
-
'nec_lat',
|
|
115
|
-
'nec_long',
|
|
116
|
-
'swc_lat',
|
|
117
|
-
'swc_long',
|
|
118
|
-
'end_position_lat',
|
|
119
|
-
'end_position_long',
|
|
120
|
-
'sport_profile_name',
|
|
121
|
-
'enhanced_avg_speed',
|
|
122
|
-
'enhanced_max_speed',
|
|
123
|
-
'training_load_peak',
|
|
124
|
-
'total_grit',
|
|
125
|
-
'avg_flow',
|
|
126
|
-
'message_index',
|
|
127
|
-
'total_calories',
|
|
128
|
-
'total_ascent',
|
|
129
|
-
'total_descent',
|
|
130
|
-
'first_lap_index',
|
|
131
|
-
'num_laps',
|
|
132
|
-
'left_right_balance',
|
|
133
|
-
'avg_vam',
|
|
134
|
-
'enhanced_avg_respiration_rate',
|
|
135
|
-
'enhanced_max_respiration_rate',
|
|
136
|
-
'est_sweat_loss',
|
|
137
|
-
'enhanced_min_respiration_rate',
|
|
138
|
-
'jump_count',
|
|
139
|
-
'resting_calories',
|
|
140
|
-
'event',
|
|
141
|
-
'event_type',
|
|
142
|
-
'sport',
|
|
143
|
-
'sub_sport',
|
|
144
|
-
'avg_heart_rate',
|
|
145
|
-
'max_heart_rate',
|
|
146
|
-
'total_training_effect',
|
|
147
|
-
'trigger',
|
|
148
|
-
'avg_temperature',
|
|
149
|
-
'max_temperature',
|
|
150
|
-
'total_anaerobic_training_effect',
|
|
151
|
-
'min_temperature',
|
|
152
|
-
'primary_benefit'
|
|
153
|
-
]
|
|
154
|
-
time_in_power_zone: undefined
|
package/scan_jumps.js
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import FitParser from './dist/fit-parser.js';
|
|
3
|
-
|
|
4
|
-
const content = fs.readFileSync('../sports-lib/samples/fit/jumps-mtb.fit');
|
|
5
|
-
const fitParser = new (FitParser.default || FitParser)({ force: true, mode: 'both' });
|
|
6
|
-
|
|
7
|
-
fitParser.parse(content, (error, data) => {
|
|
8
|
-
if (error) { console.error(error); return; }
|
|
9
|
-
|
|
10
|
-
if (data.jumps && data.jumps.length > 0) {
|
|
11
|
-
console.log(`Found ${data.jumps.length} jumps.`);
|
|
12
|
-
data.jumps.forEach((j, i) => {
|
|
13
|
-
// check for values close to 16.3038
|
|
14
|
-
const target = 16.3038;
|
|
15
|
-
const epsilon = 0.1;
|
|
16
|
-
|
|
17
|
-
Object.entries(j).forEach(([key, val]) => {
|
|
18
|
-
if (typeof val === 'number' && Math.abs(val - target) < epsilon) {
|
|
19
|
-
console.log(`MATCH INDEX ${i} KEY ${key}: ${val}`);
|
|
20
|
-
}
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
// Also just print field 7 ('speed' in new def)
|
|
24
|
-
console.log(`Jump ${i}: speed=${j.speed}`);
|
|
25
|
-
});
|
|
26
|
-
} else {
|
|
27
|
-
console.log('No jumps found.');
|
|
28
|
-
}
|
|
29
|
-
});
|
package/scripts/deep_probe.js
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import fs from 'fs'
|
|
2
|
-
import { createRequire } from 'module'
|
|
3
|
-
|
|
4
|
-
const require = createRequire(import.meta.url)
|
|
5
|
-
// Try to load from local build or node_modules
|
|
6
|
-
let FitParser
|
|
7
|
-
try {
|
|
8
|
-
FitParser = require('../dist/fit-parser.js').default
|
|
9
|
-
}
|
|
10
|
-
catch (e) {
|
|
11
|
-
FitParser = require('fit-file-parser').default
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const args = process.argv.slice(2)
|
|
15
|
-
if (args.length < 2) {
|
|
16
|
-
console.log('Usage: node deep_probe.js <path_to_fit_file> <search_value> [tolerance]')
|
|
17
|
-
console.log('Example: node deep_probe.js ../examples/jumps-mtb.fit 11 0.1')
|
|
18
|
-
process.exit(1)
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const filePath = args[0]
|
|
22
|
-
const targetValue = Number.parseFloat(args[1])
|
|
23
|
-
const tolerance = args[2] ? Number.parseFloat(args[2]) : 0.001
|
|
24
|
-
|
|
25
|
-
if (isNaN(targetValue)) {
|
|
26
|
-
console.error('Error: Search value must be a number')
|
|
27
|
-
process.exit(1)
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
try {
|
|
31
|
-
const content = fs.readFileSync(filePath)
|
|
32
|
-
const fitParser = new FitParser({
|
|
33
|
-
force: true,
|
|
34
|
-
speedUnit: 'm/s',
|
|
35
|
-
lengthUnit: 'm',
|
|
36
|
-
temperatureUnit: 'celsius',
|
|
37
|
-
elapsedRecordField: true,
|
|
38
|
-
mode: 'both',
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
function search(obj, path = []) {
|
|
42
|
-
if (!obj || typeof obj !== 'object')
|
|
43
|
-
return
|
|
44
|
-
|
|
45
|
-
Object.keys(obj).forEach((key) => {
|
|
46
|
-
const val = obj[key]
|
|
47
|
-
const newPath = [...path, key]
|
|
48
|
-
|
|
49
|
-
if (typeof val === 'number') {
|
|
50
|
-
if (Math.abs(val - targetValue) <= tolerance) {
|
|
51
|
-
console.log(`>>> FOUND MATCH at: ${newPath.join('.')} (Value: ${val})`)
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if (typeof val === 'object') {
|
|
56
|
-
search(val, newPath)
|
|
57
|
-
}
|
|
58
|
-
})
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
fitParser.parse(content, (error, data) => {
|
|
62
|
-
if (error) {
|
|
63
|
-
console.error(error)
|
|
64
|
-
}
|
|
65
|
-
else {
|
|
66
|
-
console.log(`Searching for value ${targetValue} (±${tolerance}) in ${filePath}...`)
|
|
67
|
-
search(data)
|
|
68
|
-
console.log('Search complete.')
|
|
69
|
-
}
|
|
70
|
-
})
|
|
71
|
-
}
|
|
72
|
-
catch (e) {
|
|
73
|
-
console.error('Error:', e.message)
|
|
74
|
-
}
|
package/scripts/inspect_fit.js
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import fs from 'fs'
|
|
2
|
-
import { createRequire } from 'module'
|
|
3
|
-
|
|
4
|
-
const require = createRequire(import.meta.url)
|
|
5
|
-
// Try to load from local build or node_modules
|
|
6
|
-
let FitParser
|
|
7
|
-
try {
|
|
8
|
-
FitParser = require('../dist/fit-parser.js').default
|
|
9
|
-
}
|
|
10
|
-
catch (e) {
|
|
11
|
-
FitParser = require('fit-file-parser').default
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const args = process.argv.slice(2)
|
|
15
|
-
if (args.length < 1) {
|
|
16
|
-
console.log('Usage: node inspect_fit.js <path_to_fit_file> [message_key]')
|
|
17
|
-
console.log('Example: node inspect_fit.js ../examples/jumps-mtb.fit jumps')
|
|
18
|
-
process.exit(1)
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const filePath = args[0]
|
|
22
|
-
const messageKey = args[1]
|
|
23
|
-
|
|
24
|
-
try {
|
|
25
|
-
const content = fs.readFileSync(filePath)
|
|
26
|
-
const fitParser = new FitParser({
|
|
27
|
-
force: true,
|
|
28
|
-
mode: 'both',
|
|
29
|
-
speedUnit: 'm/s',
|
|
30
|
-
lengthUnit: 'm',
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
fitParser.parse(content, (error, data) => {
|
|
34
|
-
if (error) {
|
|
35
|
-
console.error('Error parsing FIT file:', error)
|
|
36
|
-
return
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (messageKey) {
|
|
40
|
-
if (data[messageKey]) {
|
|
41
|
-
console.log(`\n=== ${messageKey.toUpperCase()} (${Array.isArray(data[messageKey]) ? data[messageKey].length : 'object'}) ===`)
|
|
42
|
-
console.log(JSON.stringify(data[messageKey], null, 2))
|
|
43
|
-
}
|
|
44
|
-
else if (data.activity && data.activity[messageKey]) {
|
|
45
|
-
console.log(`\n=== ACTIVITY.${messageKey.toUpperCase()} ===`)
|
|
46
|
-
console.log(JSON.stringify(data.activity[messageKey], null, 2))
|
|
47
|
-
}
|
|
48
|
-
else {
|
|
49
|
-
console.log(`\nMessage key '${messageKey}' not found in root or activity object.`)
|
|
50
|
-
console.log('Available keys:', Object.keys(data).join(', '))
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
else {
|
|
54
|
-
console.log('\n=== ROOT KEYS ===')
|
|
55
|
-
console.log(Object.keys(data).join('\n'))
|
|
56
|
-
|
|
57
|
-
// Print summary of counts
|
|
58
|
-
console.log('\n=== SUMMARY ===')
|
|
59
|
-
Object.keys(data).forEach((key) => {
|
|
60
|
-
if (Array.isArray(data[key])) {
|
|
61
|
-
console.log(`${key}: ${data[key].length} items`)
|
|
62
|
-
}
|
|
63
|
-
})
|
|
64
|
-
}
|
|
65
|
-
})
|
|
66
|
-
}
|
|
67
|
-
catch (e) {
|
|
68
|
-
console.error('Error reading file:', e.message)
|
|
69
|
-
}
|
package/tsconfig.cjs.json
DELETED
package/tsconfig.json
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "es6",
|
|
4
|
-
"rootDir": "./src",
|
|
5
|
-
"module": "Node16",
|
|
6
|
-
"strict": true,
|
|
7
|
-
"declaration": true,
|
|
8
|
-
"outDir": "./dist",
|
|
9
|
-
"esModuleInterop": true,
|
|
10
|
-
"forceConsistentCasingInFileNames": true,
|
|
11
|
-
"skipLibCheck": true
|
|
12
|
-
},
|
|
13
|
-
"include": ["src/**/*"],
|
|
14
|
-
"exclude": ["node_modules"]
|
|
15
|
-
|
|
16
|
-
}
|