@sports-alliance/sports-lib
v11.0.3
Published
A Library to for importing / exporting and processing GPX, TCX, FIT and JSON files from services such as Strava, Movescount, Garmin, Polar etc
Downloads
4,107
Readme
Sports Lib
A Library for processing GPX, TCX, FIT and JSON files from services such as Strava, Movescount, Garmin, Polar etc
About
Sports Lib tries to achieve a common domain model and lib for sport activity formats such as GPX, TCX, FIT and other popular formats.
Currently the support is limited to the main formats: GPX, TCX, FIT and JSON*
*JSON is for specific services while GPX, TCX, FIT should be compatible with the most common services, such as Strava, Movescount, Garmin, Polar and any other service that supports the above formats.
Release Notes
- Breaking change (next major):
Effort Pacenow uses pace semantics (min/km) instead of speed semantics (m/s).Effort Pacealso hasAverage/Minimum/Maximumstat types and pace unit variants followingpaceUnits.
Install
Install via npm
npm install @sports-alliance/sports-lib --save
Examples
GPX
import {SportsLib} from '@sports-alliance/sports-lib';
import {DOMParser} from 'xmldom'
// For GPX you need a string
const gpxString = 'Some string from a file etc';
SportsLib.importFromGPX(gpxString, DOMParser).then((event)=>{
// do Stuff with the file
const distance = event.getDistance();
const duration = event.getDuration();
});TCX
import {SportsLib} from '@sports-alliance/sports-lib';
// For TCX you need a string
const tcxString = 'Some string from a file etc';
SportsLib.importFromTCX((new DOMParser()).parseFromString(tcxString, 'application/xml')).then((event)=>{
// do Stuff with the file
const distance = event.getDistance();
const duration = event.getDuration();
});FIT
import {SportsLib} from '@sports-alliance/sports-lib';
// For FIT you need an ArrayBuffer (binary)
SportsLib.importFromFit(arrayBuffer).then((event)=>{
// do Stuff with the file
const distance = event.getDistance();
const duration = event.getDuration();
});FIT device_info mode
Some FIT files emit device_info rows every second for the same device identity, which can significantly increase payload size.
ActivityParsingOptions exposes deviceInfoMode:
raw(default): keep alldevice_inforows (backwards-compatible behavior).changes: collapse contiguous timestamp-only repeats and keep first + last item for each contiguous identical run.
import { SportsLib, ActivityParsingOptions } from '@sports-alliance/sports-lib';
const options = new ActivityParsingOptions({
deviceInfoMode: 'changes'
});
SportsLib.importFromFit(arrayBuffer, options).then(event => {
// event.getFirstActivity().creator.devices is compacted by state changes
});summary mode is intentionally not exposed in this version.
Stream includeTypes filter (FIT/TCX/GPX)
ActivityParsingOptions also supports stream allowlisting under streams.includeTypes.
includeTypesmissing or[]: current behavior (no filtering).includeTypesprovided: strict final stream output for FIT/TCX/GPX importers.- Values must be canonical
Data*.typestrings (for example:Distance,Heart Rate,Pace). - Unknown types throw a parsing error.
- Derived requests are supported: required internal dependencies are resolved automatically and then removed from final output unless explicitly requested.
import { SportsLib, ActivityParsingOptions } from '@sports-alliance/sports-lib';
// Raw-only request
const rawOnlyOptions = new ActivityParsingOptions({
streams: { includeTypes: ['Distance', 'Heart Rate'] }
});
// Derived request (Pace): dependencies (like Speed) are handled internally
const derivedOnlyOptions = new ActivityParsingOptions({
streams: { includeTypes: ['Pace'] }
});
SportsLib.importFromFit(arrayBuffer, rawOnlyOptions).then(event => {
// activity streams contain only Distance + Heart Rate
});
SportsLib.importFromFit(arrayBuffer, derivedOnlyOptions).then(event => {
// activity streams contain only Pace
});Data Coverage & Calculation Reference
Data Coverage Overview
Sports Lib currently exposes a broad metric surface from the public data barrel (src/data/index.ts) and concrete data classes (static type declarations).
- Exported data modules from
src/data/index.ts - Concrete canonical metric type strings (
Data*.type) from exportedDataclasses - Minimum/Maximum/Average family types
- Unit variant types (
... in ...) - Zone/target types
The library exposes these metrics through streams, stats, laps/events, and event summaries.
Canonical type strings are the same values used by ActivityParsingOptions.streams.includeTypes.
Some legacy metric types include intentional whitespace in their token (for example Steps), so copy tokens exactly.
Time metric semantics:
Duration(unit:s): active duration (timer time).Timer time(unit:s): explicit timer/active time.Elapsed time(unit:s): wall-clock elapsed time (includes pauses).Pause Time(unit:s): explicit paused time, computed asmax(ElapsedTime - TimerTime, 0).Moving time(unit:s): movement-only time; separate from pause semantics.
High-level metric domains include:
- Core streams/stats: time, distance, speed, pace, swim pace, heart rate, cadence, power, altitude, grade, vertical metrics
- Zones and targets: heart-rate/power/speed zone durations and zone targets
- Device/context: battery, pressure, satellites, sensor/pod flags, fused location flags, device metadata
- Performance analytics: normalized power, power curve, FTP, IF, TSS, critical power, W', power work
- Running/cycling/swim dynamics: ground contact, stance balance, oscillation, ratio, SWOLF, efficiency-related metrics
- Jump analytics: jump count/events and min/max/avg families for jump height, distance, speed, score, rotations, hang time
Calculations / Derivations
The following formulas describe how missing streams/stats are computed in:
src/events/utilities/activity.utilities.tssrc/events/utilities/grade-calculator/grade-calculator.tssrc/events/utilities/tss/tss-calculator.tssrc/events/utilities/helpers.ts
- Distance, GNSS distance, speed, pace, swim pace
Distance[t] = Distance[t-1] + geodesic(Position[t-1], Position[t])
Speed[t] = (Distance[t] - Distance[t-1]) / deltaTimeSeconds
Pace (sec/km) = 1000 / Speed(m/s)
Swim Pace (sec/100m) = 100 / Speed(m/s)- Distance/GNSS distance are generated from latitude/longitude when missing.
- Speed is generated from distance deltas and time deltas.
- Unit stream variants are derived via helper conversion factors (km/h, mph, ft/s, m/min, knots, min/mi, min/100yd, miles).
- Altitude smoothing, grade, grade smoothing, grade-adjusted speed/pace
AltitudeSmooth = medianFilter(11) -> lowPassFilter
Grade(%) = clamp((deltaAltitude / deltaDistance) * 100, -50, +50)
with lookAheadDistance = 10 m, rounded to 0.1
GradeSmooth = KalmanFilter(R=0.01, Q=0.5) over Grade
GradeAdjustedSpeed = Speed * (kA + kB*g + kC*g^2 + kD*g^3 + kE*g^4 + kF*g^5)
where:
kA=1
kB=0.029290920646623777
kC=0.0018083953212790634
kD=4.0662425671715924e-7
kE=-3.686186584867523e-7
kF=-2.6628107325930747e-9
GradeAdjustedPace = 1000 / GradeAdjustedSpeed- Left/right split and stance balance
PowerRight = Power * (RightBalance / 100)
PowerLeft = Power * (LeftBalance / 100)
StanceTimeBalanceRight = 100 - StanceTimeBalanceLeft- Generic stat families and ascent/descent gain-loss
Average = sum(filteredFiniteValues) / count
Maximum = max(filteredFiniteValues)
Minimum = min(filteredFiniteValues)
Ascent/Loss uses thresholded step accumulation (default minDiff = 2):
- Gain: accumulate positive deltas when previous + minDiff <= next
- Loss: accumulate negative deltas when previous - minDiff >= next- Cadence minimum/average exclude zero values.
- Grade max/min/avg prefers
Grade Smoothwhen present.
- Power analytics (NP, power curve, FTP, IF, CP/W')
NormalizedPower (NP):
- Build ~30s buffered means from power-by-time samples
- Raise each mean to the 4th power
NP = 4th_root(average(mean30s^4))
PowerCurve(duration d): max rolling mean power over window d
FTP = round(0.95 * best_20min_power)
IF = NP / FTP
Critical Power model (Monod-Scherrer on duration 180..1200s):
x = 1 / timeSeconds
y = power
slope = (n*sumXY - sumX*sumY) / (n*sumXX - sumX^2) = W'
intercept = (sumY - slope*sumX) / n = CP- Training Stress Score (TSS) methods and priority
Priority order:
- POWER -> HR -> PACE/SWIM_PACE -> MET
POWER TSS:
IF = NP / FTP
EffectiveDuration = max(durationWithoutPauses - 29, 0)
TSS = (100 * EffectiveDuration * NP * IF) / (FTP * 3600)HR TSS:
- Banister TRIMP when resting HR is available
- Edwards-zone fallback otherwise
PACE TSS (running/trail groups):
- Uses Minetti running-cost grade adjustment, then NP-like 30s/4th-power normalization
Cost(g) = 155.4*g^5 - 30.4*g^4 - 43.3*g^3 + 46.3*g^2 + 19.5*g + 3.6
AdjustedSpeed = Speed * (3.6 / Cost(g))
TSS = 100 * (duration/3600) * IF^2SWIM_PACE TSS:
IF = SwimSpeed / ThresholdSwimSpeed
TSS = 100 * (duration/3600) * IF^3MET TSS:
METScore = (3600 * Energy) / (Weight * Duration)
IF = METScore / ThresholdMET
TSS = 100 * (duration/3600) * IF^2- SWOLF, moving time fallback, power work, battery, jumps
SWOLF(poolLength) = round((secondsPerMeter + strokesPerMeter) * poolLength, 1)
where:
secondsPerMeter = (secondsPer100m / 100)
strokesPerMeter = ((strokesPerMinute * (secondsPer100m / 60)) / 100)
PowerWork(kJ) = round((AveragePower * MovingTimeSeconds) / 1000)
BatteryConsumption = max(BatteryCharge) - min(BatteryCharge)
BatteryLifeEstimation = ((activityDurationSeconds * 100) / BatteryConsumption)- Moving time fallback order: lap moving time -> speed-threshold integration -> timer time fallback.
- Jump families (height, distance, speed, rotations, score, hang time) compute min/max/avg from jump events when available.
- Event-level aggregation behavior
- Single-activity event: copies activity stats directly to event stats.
- Multi-activity event:
- Sums duration, pause, distance, ascent, descent, energy.
- Aggregates zone durations by summation.
- Averages many average-like stats using iterative pairwise averaging.
- Aggregates power curves by duration-wise maxima (power and W/kg), then recomputes CP/W'.
Full Metric Catalog (Appendix)
Generated from modules re-exported by src/data/index.ts, then resolved to each module's concrete static type declarations.
Core & Context Types
Steps(leading space intentionally preserved; legacy alias)Absolute Pressure(unit:hpa)Accumulated Power(unit:watts)Active LapActive LengthsActivity TypesAerobic Training EffectAge(unit:years)Air Power(unit:watt)Alti Baro ProfileAltitude(unit:m)Altitude (Stryd)Altitude GPS(unit:m)Altitude SmoothAnaerobic Training EffectAscentAscent TimeAuto LapAuto Lap Distance(unit:m)Auto Lap DurationAuto PauseBattery Charge(unit:%)Battery Consumption(unit:%)Battery Current(unit:mA)Battery Life Est.Battery Voltage(unit:V)Bike PodCadence(unit:rpm)CriticalPowerCycling Avg Seated Power(unit:watt)Cycling Avg Standing Power(unit:watt)Cycling Max Seated Power(unit:watt)Cycling Max Standing Power(unit:watt)Cycling Seated Time(unit:s)Cycling Standing Time(unit:s)Depth(unit:m)DescentDescent TimeDescriptionDevice LocationDevice NamesDistance(unit:m)Distance (Stryd)Duration(unit:s)Elapsed time(unit:s)Effort PaceEHPEEnabled Navigation SystemsEnd AltitudeEnd PositionEnergy(unit:KCal)EPOC(unit:ml/kg)Est Sweat Loss(unit:ml)EVPEFeelingFitness Age(unit:years)FlowFoot PodForm PowerFTPFused AltitudeFused LocationGenderGNSS DistanceGradeGrade Adjusted Pace(unit:min/km)Grade Adjusted SpeedGrade SmoothGritGround Contact Time(unit:ms)Ground Contact Time Balance LeftGround Contact Time Balance RightGround Time(unit:ms)Heart Rate(unit:bpm)Heart Rate UsedHeight(unit:m)IBI(unit:ms)Jump CountJump DistanceJump EventJump ScoreLatitude(unit:degrees)Left BalanceLeft Pedal Smoothness(unit:%)Left Torque Effectiveness(unit:%)Leg Spring Stiffness(unit:"KN/m")Leg Stiffness(unit:"KN/m")Longitude(unit:degrees)Moving timeNumber of SamplesNumber of SatellitesPace(unit:min/km)Pause TimePeak EPOCPeak Training EffectPool Length(unit:m)PositionPower(unit:watt)Power Down EventPower Intensity FactorPower LeftPower NormalizedPower Pedal Smoothness LeftPower Pedal Smoothness RightPower PodPower RightPower Torque Effectiveness LeftPower Torque Effectiveness RightPower Up EventPower Work(unit:kJ)PowerCurvePowerWattsPerKgPrimary BenefitRated Perceived ExertionRecovery TimeResting Calories(unit:kcal)Rider Position Change EventRight BalanceRight Pedal Smoothness(unit:%)Right Torque Effectiveness(unit:%)RotationsSatellite 5 Best SNRSea Level Pressure(unit:hpa)Speed(unit:m/s)Speed (Stryd)Sport Profile NameStance Time(unit:ms)Stance Time Balance LeftStance Time Balance RightStart EventStart PositionStarting AltitudeStep LengthStepsStop ALL EventStop EventSwim Pace(unit:min/100m)Temperature(unit:°C)TimeTimer timeTotal CyclesTotal FlowTotal GritTraining Load PeakTraining Stress ScoreTraining Stress Score MethodVertical Oscillation(unit:mm)Vertical Ratio(unit:%)Vertical Speed(unit:m/s)VO2 MaxWeight(unit:kg)WPrime
Zone & Target Types
Distance TargetHeart Rate Zone Five DurationHeart Rate Zone Four DurationHeart Rate Zone One DurationHeart Rate Zone Seven DurationHeart Rate Zone Six DurationHeart Rate Zone TargetHeart Rate Zone Three DurationHeart Rate Zone Two DurationPower Zone Five DurationPower Zone Four DurationPower Zone One DurationPower Zone Seven DurationPower Zone Six DurationPower Zone TargetPower Zone Three DurationPower Zone Two DurationSpeed Zone Five DurationSpeed Zone Four DurationSpeed Zone One DurationSpeed Zone Seven DurationSpeed Zone Six DurationSpeed Zone TargetSpeed Zone Three DurationSpeed Zone Two DurationTime Target
Minimum/Maximum/Average Families
Average Absolute PressureAverage Air PowerAverage AltitudeAverage CadenceAverage Effort PaceAverage Effort Pace in minutes per mileAverage EHPEAverage EVPEAverage FlowAverage Grade Adjusted PaceAverage Grade Adjusted Pace in minutes per mile(unit:min/m)Average Grade Adjusted SpeedAverage Grade Adjusted Speed in feet per minuteAverage Grade Adjusted Speed in feet per secondAverage Grade Adjusted Speed in kilometers per hourAverage Grade Adjusted Speed in knotsAverage Grade Adjusted Speed in meters per minuteAverage Grade Adjusted Speed in miles per hourAverage GritAverage Ground Contact Time(unit:ms)Average Heart RateAverage Jump DistanceAverage Jump Hang TimeAverage Jump Height(unit:m)Average Jump RotationsAverage Jump ScoreAverage Jump SpeedAverage jump speed in feet per minuteAverage jump speed in feet per secondAverage jump speed in kilometers per hourAverage jump speed in knotsAverage jump speed in meters per minuteAverage jump speed in miles per hourAverage Number of SatellitesAverage PaceAverage pace in minutes per mileAverage PowerAverage Respiration Rate(unit:br/min)Average Satellite 5 Best SNRAverage SpeedAverage speed in feet per minuteAverage speed in feet per secondAverage speed in kilometers per hourAverage speed in knotsAverage speed in meters per minuteAverage speed in miles per hourAverage Stride LengthAverage Stroke CountAverage Stroke DistanceAverage Swim PaceAverage swim pace in minutes per 100 yardAverage SWOLF 25mAverage SWOLF 50mAverage TemperatureAverage VAM(unit:m/h)Average Vertical Oscillation(unit:mm)Average Vertical SpeedAverage vertical speed in feet per hourAverage vertical speed in feet per minuteAverage vertical speed in feet per secondAverage vertical speed in kilometers per hourAverage vertical speed in meters per hourAverage vertical speed in meters per minuteAverage vertical speed in miles per hourMaximum Absolute PressureMaximum Air PowerMaximum AltitudeMaximum CadenceMaximum Depth(unit:m)Maximum Effort PaceMaximum Effort Pace in minutes per mileMaximum EHPEMaximum EVPEMaximum Grade Adjusted PaceMaximum Grade Adjusted Pace in minutes per mile(unit:min/m)Maximum Grade Adjusted SpeedMaximum Grade Adjusted Speed in feet per minuteMaximum Grade Adjusted Speed in feet per secondMaximum Grade Adjusted Speed in kilometers per hourMaximum Grade Adjusted Speed in knotsMaximum Grade Adjusted Speed in meters per minuteMaximum Grade Adjusted Speed in miles per hourMaximum Ground Contact Time(unit:ms)Maximum Heart RateMaximum HR Setting(unit:bpm)Maximum Jump DistanceMaximum Jump Hang TimeMaximum Jump Height(unit:m)Maximum Jump RotationsMaximum Jump ScoreMaximum Jump SpeedMaximum jump speed in feet per minuteMaximum jump speed in feet per secondMaximum jump speed in kilometers per hourMaximum jump speed in knotsMaximum jump speed in meters per minuteMaximum jump speed in miles per hourMaximum Number of SatellitesMaximum PaceMaximum pace in minutes per mileMaximum PowerMaximum Respiration Rate(unit:br/min)Maximum Satellite 5 Best SNRMaximum SpeedMaximum speed in feet per minuteMaximum speed in feet per secondMaximum speed in kilometers per hourMaximum speed in knotsMaximum speed in meters per minuteMaximum speed in miles per hourMaximum Swim PaceMaximum swim pace in minutes per 100 yardMaximum TemperatureMaximum Vertical Oscillation(unit:mm)Maximum Vertical SpeedMaximum vertical speed in feet per hourMaximum vertical speed in feet per minuteMaximum vertical speed in feet per secondMaximum vertical speed in kilometers per hourMaximum vertical speed in meters per hourMaximum vertical speed in meters per minuteMaximum vertical speed in miles per hourMinimum Absolute PressureMinimum Air PowerMinimum AltitudeMinimum CadenceMinimum Effort PaceMinimum Effort Pace in minutes per mileMinimum EHPEMinimum EVPEMinimum Grade Adjusted PaceMinimum Grade Adjusted pace in minutes per mile(unit:min/m)Minimum Grade Adjusted SpeedMinimum Grade Adjusted Speed in feet per minuteMinimum Grade Adjusted Speed in feet per secondMinimum Grade Adjusted Speed in kilometers per hourMinimum Grade Adjusted Speed in knotsMinimum Grade Adjusted Speed in meters per minuteMinimum Grade Adjusted Speed in miles per hourMinimum Ground Contact Time(unit:ms)Minimum Heart RateMinimum Jump DistanceMinimum Jump Hang TimeMinimum Jump Height(unit:m)Minimum Jump RotationsMinimum Jump ScoreMinimum Jump SpeedMinimum jump speed in feet per minuteMinimum jump speed in feet per secondMinimum jump speed in kilometers per hourMinimum jump speed in knotsMinimum jump speed in meters per minuteMinimum jump speed in miles per hourMinimum Number of SatellitesMinimum PaceMinimum pace in minutes per mileMinimum PowerMinimum Respiration Rate(unit:br/min)Minimum Satellite 5 Best SNRMinimum SpeedMinimum speed in feet per minuteMinimum speed in feet per secondMinimum speed in kilometers per hourMinimum speed in knotMinimum speed in meters per minuteMinimum speed in miles per hourMinimum Swim PaceMinimum swim pace in minutes per 100 yardMinimum TemperatureMinimum Vertical Oscillation(unit:mm)Minimum Vertical SpeedMinimum vertical speed in feet per hourMinimum vertical speed in feet per minuteMinimum vertical speed in feet per secondMinimum vertical speed in kilometers per hourMinimum vertical speed in meters per hourMinimum vertical speed in meters per minuteMinimum vertical speed in miles per hour
Unit Variant Types
Distance in miles(unit:mi)Effort Pace in minutes per mileGNSS Distance in miles(unit:mi)Grade Adjusted Pace in minutes per mile(unit:min/m)Grade Adjusted Speed in feet per minute(unit:ft/min)Grade Adjusted Speed in feet per second(unit:ft/s)Grade Adjusted Speed in kilometers per hour(unit:km/h)Grade Adjusted Speed in knots(unit:kn)Grade Adjusted Speed in meters per minute(unit:m/min)Grade Adjusted Speed in miles per hour(unit:mph)Pace in minutes per mile(unit:min/m)Speed in feet per minute(unit:ft/min)Speed in feet per second(unit:ft/s)Speed in kilometers per hour(unit:km/h)Speed in knots(unit:kn)Speed in meters per minute(unit:m/min)Speed in miles per hour(unit:mph)Swim Pace in minutes per 100 yard(unit:min/100yrd)Vertical speed in feet per hour(unit:ft/h)Vertical speed in feet per minute(unit:ft/min)Vertical speed in feet per second(unit:ft/s)Vertical speed in kilometers per hour(unit:km/h)Vertical speed in meters per hour(unit:m/h)Vertical speed in meters per minute(unit:m/min)Vertical speed in miles per hour(unit:mph)
Export
// Get an event as seen above
const gpxString = await new EventExporterGPX().getAsString(event);
// Send the blob
const blob = new Blob(
[jsonString],
{type: new EventExporterGPX().fileType},
);Example converting a FIT file to GPX
Thanks to @guikeller
import fs from 'fs';
import sportsLibPkg from '@sports-alliance/sports-lib';
import exporterPkg from '@sports-alliance/sports-lib/lib/events/adapters/exporters/exporter.gpx.js'
const { SportsLib } = sportsLibPkg;
const { EventExporterGPX } = exporterPkg;
// Input and output file path
const inputFilePath = '/tmp/test.fit';
const outputGpxFilePath = '/tmp/test.gpx';
// reads the FIT file into memory
const inputFile = fs.readFileSync(inputFilePath, null);
if (!inputFile || !inputFile.buffer) {
console.error('Ooops, could not read the inputFile or it does not exists, see details below');
console.error(JSON.stringify(inputFilePath));
return;
}
const inputFileBuffer = inputFile.buffer;
// uses lib to read the FIT file
SportsLib.importFromFit(inputFileBuffer).then((event) => {
// convert to gpx
const gpxPromise = new EventExporterGPX().getAsString(event);
gpxPromise.then((gpxString) => {
// writes the gpx to file
fs.writeFileSync(outputGpxFilePath, gpxString, (wError) => {
if (wError) {
console.error('Ooops, something went wrong while saving the GPX file, see details below.');
console.error(JSON.stringify(wError));
}
});
// all done, celebrate!
console.log('Converted FIT file to GPX successfully!');
console.log('GPX file saved here: ' + outputGpxFilePath);
}).catch((cError) => {
console.error('Ooops, something went wrong while converting the FIT file, see details below');
console.error(JSON.stringify(cError));
});
});