Effort Pace uses pace semantics (min/km), not speed semantics (m/s). Its average, minimum, maximum, and unit-variant metric types follow paceUnits.
Sports Lib currently exposes a broad metric surface from the public data barrel (src/data/index.ts) and concrete data classes (static type declarations).
src/data/index.tsData*.type) from exported Data classes... in ...)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 as max(ElapsedTime - TimerTime, 0).Moving time (unit: s): movement-only time; separate from pause semantics.High-level metric domains include:
Ground Contact Time is the canonical Sports Lib terminology. FIT protocol names remain importer details:
| Source field | Canonical metric | Source unit | Stored unit |
|---|---|---|---|
FIT stance_time / avg_stance_time |
Ground Contact Time / Average Ground Contact Time |
ms |
ms |
FIT stance_time_percent / avg_stance_time_percent |
Ground Contact Time Percentage / Average Ground Contact Time Percentage |
% |
% |
FIT stance_time_balance / avg_stance_time_balance |
Ground Contact Time Balance Left and Ground Contact Time Balance Right |
% |
% |
Suunto GroundContactTime |
Ground Contact Time |
s |
ms |
Suunto LeftGroundContactBalance / RightGroundContactBalance |
Ground Contact Time Balance Left / Ground Contact Time Balance Right |
% |
% |
Suunto FlightTime |
Running Flight Time |
s |
ms |
Suunto ContactTimeRatio |
Contact Time to Flight Time Ratio |
% |
% |
FIT ground-contact-time percentage is the fraction of the running cycle spent in ground contact. Suunto contact time to flight time ratio is a separate source measurement and may exceed 100%; Sports Lib neither aliases nor converts it to FIT ground-contact-time percentage. Running flight time is also distinct from jump hang time and Stryd ground time. Suunto non-positive flight-time and contact-time-ratio values are treated as missing provider sentinels; the corpus contains running files with all-zero flight samples and null flight summaries.
Suunto running-dynamics summaries accept both header objects ({ Avg, Min, Max }) and window arrays containing that
object. Deprecated Stance Time classes remain loadable for historical native JSON, but FIT imports emit only the
canonical Ground Contact Time streams. Existing persisted activities must be reparsed from their FIT or Suunto source
files to gain metrics newly supported here; no existing canonical values require migration.
Sports Lib exposes provider-neutral scalar Data* classes for Health and sleep values. These classes define stable
canonical type tokens, units, display formatting, aliases, DataStore enumeration, dynamic loading, and native JSON
round trips. They do not add a provider transport, a Health record container, or a sleep-session container. A consumer
decides whether a value belongs in a stream, statistic, Health source record, or sleep session.
Numeric classes in the Health and sleep catalog accept finite numbers only, including reused activity primitives such
as steps, distance, altitude, heart rate, weight, VO₂ max, and fitness age. NaN and infinite values are rejected before
JSON serialization. Score, count, category, and duration units remain available through getUnit(), while count, score,
category, and duration display units intentionally omit a suffix. Duration display uses the standard Sports Lib
human-readable duration formatter.
Each Health and sleep scalar supports a storage-safe round trip through its canonical JSON object. toJSON() emits one
canonical type key, while the concrete class's fromJSON() validates that exact single-key shape before constructing the
metric. Alias keys and objects with additional properties are rejected instead of being persisted ambiguously.
const stored = JSON.stringify(new DataSleepDuration(28_800).toJSON());
const restored = DataSleepDuration.fromJSON(JSON.parse(stored));
The provider-neutral Health catalog is:
| Canonical type | Stored unit | Display behavior |
|---|---|---|
Steps |
count |
Rounded count, no suffix |
Wheelchair Pushes |
count |
Rounded count, no suffix |
Distance |
m |
Existing meter/kilometer distance display |
Wheelchair Push Distance |
m |
Existing meter/kilometer distance display |
Floors Climbed |
count |
Rounded count, no suffix |
Active Duration |
s |
Human-readable duration |
Moderate Intensity Duration |
s |
Human-readable duration |
Vigorous Intensity Duration |
s |
Human-readable duration |
Altitude |
m |
Existing rounded altitude display |
Active Energy |
kcal |
Rounded kilocalories |
Basal Energy |
kcal |
Rounded kilocalories |
Total Energy |
kcal |
Rounded kilocalories |
Heart Rate |
bpm |
Existing rounded heart-rate display |
Resting Heart Rate |
bpm |
Rounded beats per minute |
Heart Rate Variability |
ms |
One decimal; displayed as HRV |
Blood Oxygen Saturation |
% |
One decimal; displayed as SpO₂ |
Respiration Rate |
br/min |
One decimal |
Stress Level |
score |
One decimal, no suffix |
Stress State |
category |
Source category, no suffix |
Stress Duration |
s |
Human-readable duration |
Body Energy |
% |
One decimal; Body Battery is an alias |
Body Energy Change |
% |
Signed, one decimal |
Recovery Score |
score |
One decimal, no suffix |
Weight |
kg |
Canonical one-decimal display; Body Weight is an alias |
Body Mass Index |
kg/m² |
One decimal; displayed as BMI |
Body Fat |
% |
One decimal |
Body Water |
% |
One decimal |
Muscle Mass |
kg |
One decimal |
Bone Mass |
kg |
Two decimals |
Systolic Blood Pressure |
mmHg |
Rounded millimeters of mercury |
Diastolic Blood Pressure |
mmHg |
Rounded millimeters of mercury |
Pulse Rate |
bpm |
Rounded beats per minute |
Skin Temperature Deviation |
°C |
Signed, one decimal |
VO2 Max |
ml/kg/min |
Two decimals; displayed as VO₂ Max |
Fitness Age |
years |
Numeric years |
Mass display for planned exercise loads uses the existing canonical DataWeight primitive in kilograms, not a new
planned-workout metric or event-stat field. WeightUnits.Pounds asks
DynamicDataLoader.getUnitBasedDataFromDataInstance for a display-only DataWeight instance; its visible value uses
the exact 0.45359237 kg/lb conversion and displays to one decimal place. WeightUnits.Kilograms and older settings
without weightUnits keep kilograms, independently of distance preference. The instance's getValue(), getUnit(),
getType() and toJSON() remain canonical kilograms. This additive display change needs no source
reparse, derived-summary regeneration, or persisted-data migration.
DataWeight.fromDisplayValue(value, units) converts a user-entered kg or lb number back to canonical kilograms; callers
still validate workout-specific load bounds before persistence. No consumer needs its own kg/lb conversion constant.
Other planned-workout scalars already use the existing duration (seconds), distance (metres), heart rate (bpm), power
(watts), speed (m/s), pace (seconds per distance), cadence (rpm), and power work (kJ) classes. A rowing 500 m split is
composed from duration and distance display; repetitions and relative percentages remain recipe semantics rather than
recorded Data* metrics. Sports Lib does not store schedules, exercise sets, provider delivery, or account state.
Sleep sessions use the following aggregate types:
| Canonical type | Stored unit | Source meaning |
|---|---|---|
Sleep Duration |
s |
Total sleep duration |
Sleep In-Bed Duration |
s |
Total time in bed |
Deep Sleep Duration |
s |
Deep-stage duration |
Light Sleep Duration |
s |
Light-stage duration |
REM Sleep Duration |
s |
Rapid-eye-movement-stage duration |
Awake Sleep Duration |
s |
Awake duration within the session |
Unmeasurable Sleep Duration |
s |
Provider-marked unmeasurable duration |
Unknown Sleep Duration |
s |
Unknown-stage duration |
Sleep Score |
score |
One-decimal provider-normalized score |
Average Sleep Heart Rate |
bpm |
Average sleep heart rate |
Minimum Sleep Heart Rate |
bpm |
Minimum sleep heart rate |
Sleep Resting Heart Rate |
bpm |
Sleep-session resting heart rate |
Average Sleep HRV |
ms |
Average sleep HRV |
Overnight HRV |
ms |
Provider overnight HRV aggregate |
Sleep HRV Sample Count |
count |
Rounded contributing-sample count |
Maximum Sleep Blood Oxygen Saturation |
% |
Maximum sleep SpO₂; displayed as Maximum Sleep SpO₂ |
Average Sleep Respiration Rate |
br/min |
Average sleep respiration |
Sleep respiration, SpO₂, and HRV sample series use the generic Respiration Rate, Blood Oxygen Saturation, and
Heart Rate Variability classes. Sleep-qualified classes are reserved for aggregates so a sample is not confused with
a whole-session statistic. Canonical Health snake-case identifiers and the documented sleep_* identifiers are loader
aliases; sleep vital aggregates additionally accept their corresponding vitals.* field paths. Serialization always
writes the canonical type shown above. Adding the classes alone does not change previously stored activities, so no
activity reparse is required.
FIT imports attach each dive_summary to the session or lap identified by its native reference_mesg and
reference_index. Message order is irrelevant, lap summaries are never promoted to their activity, and missing summary
fields are not calculated from record streams. The parser applies FIT profile scaling to depth and bottom-time fields
before Sports Lib receives and stores those values without further conversion.
Native dive-summary statistics are:
Average Depth (m), Maximum Depth (m), Surface Interval (s), Bottom Time (s), and Dive NumberDive Descent Time (s), Dive Ascent Time (s), and Dive Hang Time (s)Average Dive Ascent Rate, Average Dive Descent Rate, Maximum Dive Ascent Rate, and
Maximum Dive Descent Rate (m/s)Starting CNS Load, Ending CNS Load, Starting N2 Load, and Ending N2 Load (%)Oxygen Toxicity (OTUs), Average Pressure SAC (bar/min), Average Volume SAC (L/min), and
Average RMV (L/min)Native record streams are Depth and Next Stop Depth (m), Next Stop Time, Time to Surface,
No-Decompression Limit, and Air Time Remaining (s), CNS Load and N2 Load (%), Pressure SAC (bar/min),
Volume SAC and RMV (L/min), PO2 (%, displayed as PO₂), and Dive Ascent Rate (m/s). These streams retain
only samples present in the source file: Sports Lib does not fill, smooth, clamp, or derive them. In particular,
Air Time Remaining preserves every non-invalid unsigned FIT value exactly as decoded. Multi-gas and tank messages
are exposed as ordered ActivityInterface.getDiveSourceRecords() records: gases retain message-index flags, mixture
contents, status, and mode; tank summaries retain their timestamps, sensor IDs, pressures, and volume used; and tank
updates retain their timestamps, sensor IDs, and pressures. They are not flattened into scalar statistics, linked to
one another, or derived into consumption values. They round-trip as structured
ActivityJSONInterface.diveSourceRecords data rather than numeric metrics.
Presentation preserves the FIT profile precision: depth values and dive rates use three decimal places,
pressure/volume SAC and RMV use two, and PO₂ uses two rather than the generic one-decimal percentage format. The first
swim-pace preference selects a single coherent dive unit family: /100m keeps depth and rates in m and m/s, while
/100yd converts depth and rates to ft and ft/s. Canonical stored values and serialized JSON remain in the FIT
profile units above.
The presentation-only exported variants are Average Depth in feet, Next Stop Depth in feet, Dive ascent rate in feet per second, Average dive ascent rate in feet per second, Maximum dive ascent rate in feet per second,
Average dive descent rate in feet per second, and Maximum dive descent rate in feet per second.
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.tsDistance[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)
1000 depth scale before import; Sports Lib stores record depth and next-stop
depth directly in meters without another conversion. Canonical depth streams and maximum depth stats remain meters.
The first swimPaceUnits preference selects display variants: Swim Pace keeps meters,
while Swim Pace in minutes per 100 yard selects feet.maximum speed -> minimum pace); grade-adjusted pace follows
the same rule while retaining the existing raw-speed bounds. Explicit pace stats are never replaced. Native JSON
applies this hydration after canonical and legacy stat keys have been resolved, so older speed-only summaries gain the
same in-memory behavior without requiring a stored-data migration.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
PowerRight = Power * (RightBalance / 100)
PowerLeft = Power * (LeftBalance / 100)
GroundContactTimeBalanceRight = 100 - GroundContactTimeBalanceLeft
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
Activity and lap min/max/average derivation uses one explicit internal policy registry. It currently covers altitude,
heart rate, cadence, stroke rate, speed, effort pace, grade-adjusted speed, grade, vertical speed, power, air power,
absolute pressure, EVPE/EHPE, satellite SNR/count, temperature, ground contact time and its percentage, running flight
time, contact-time-to-flight-time ratio, leg stiffness, vertical oscillation/ratio, stamina, and potential stamina.
Registering a metric relationship in DataStore alone does not opt a new family into stream-summary generation.
Every target is filled independently. An imported minimum, maximum, or average remains authoritative while missing siblings may still be derived.
Only finite samples that pass the family policy contribute. No summary is added when no eligible samples exist.
Heart-rate, cadence, and stroke-rate averages are rounded to whole values. Cadence and stroke-rate minimums and averages exclude zero. Ground-contact-time percentages must be greater than zero and at most 100; running flight time and contact-time-to-flight-time ratio accept non-negative values.
Altitude and grade prefer their smoothed streams when present. Their summary families remain excluded for Diving activity types.
Existing laps use their activity's normalized 1 Hz streams. Lap windows are half-open ([start, end)), assigning an
exact shared boundary to the next lap; a lap ending at the activity endpoint includes that final endpoint. Overlapping
laps are evaluated independently.
Generated lap summaries store canonical stats only. Unit variants are resolved by consumers from the canonical stat and current unit settings; activity unit-stat generation remains unchanged for compatibility.
Native JSON restoration remains snapshot-based and does not regenerate these lap summaries from included streams. Newly imported source files, or callers that explicitly run activity stat generation, receive the additional lap summaries. Existing persisted activities therefore require source-file reparsing when their JSON has neither the summaries nor the source streams needed for explicit regeneration.
Terrain ascent/descent, altitude min/max/avg, and grade min/max/avg are intentionally excluded for the Diving activity group (Diving, Scuba Diving, Free Diving, Snorkeling, and Mermaiding), whether present in a source summary, restored from native JSON, regenerated into an all-diving event summary, or otherwise derived from streams. Mixed event summaries aggregate these metrics only from non-diving activities. Their vertical movement is represented by depth, not terrain elevation.
Cadence and stroke-rate minimum/average values exclude zero values.
Grade max/min/avg prefers Grade Smooth when present.
Cadence and stroke-rate semantics are activity-aware:
Cadence uses revolutions per minute (rpm) for activities such as running and cycling.Stroke Rate uses strokes or paddle cycles per minute (spm) for Swimming, Open Water Swimming, Rowing,
Indoor Rowing, Kayaking, Canoeing, Paddling, and Stand Up Paddling.Activity Types stat is present,
while other summary semantics remain opt-in. Applications can call
normalizeActivityMetricSemanticsForStats(summary, contributingActivityTypes) after hydration. This opt-in boundary
canonicalizes unambiguous stroke-rate summaries and removes terrain summaries only for homogeneous Diving-group
projections, keeping application persistence concerns outside Sports Lib while reusing its centralized sport-family
policy.avgCadence key for storage compatibility, but its in-memory value is
DataStrokeRate and its unit is spm.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
samplePowerCurveAtDuration samples exact points or interpolates in reciprocal-duration (1/t) space. Interpolation
requires neighboring durations within the default 1.25 ratio (configurable up to the hard maximum of 2), keeps the
strongest duplicate, and never extrapolates. comparePowerCurveWindows reports recent/reference retention percentage
and its percentage-point delta from 100 while normalizing each input curve only once.
Parsing does not infer CriticalPower or WPrime from one activity. The deprecated
ActivityUtilities.calculateCriticalPowerAndWPrime helper remains temporarily available as a low-level
Monod-Scherrer calculation for callers who deliberately supply a maximal test curve; its output is not evidence that an
arbitrary workout measured current athlete capacity. Existing CriticalPower and WPrime data classes and historical
JSON remain readable.
Durability Evidence is a compact, versioned activity stat. Running, cycling, standard mountain biking, and open-water
evidence compares output/heart-rate efficiency across fixed early and late time halves after warm-up/cool-down exclusion.
Enduro MTB and Downhill Cycling remain part of the mountain-biking activity family, but their whole-activity context is
not comparable through this steady aerobic protocol; they persist explicit unsupported-context evidence instead of a
durability result. Pool evidence compares the outer thirds of like-for-like active lengths using the dominant stroke and
pool length. Timelines and source streams are never stored in the stat; a deterministic protocol-input fingerprint
invalidates stale evidence, including earlier gravity-MTB evidence, and ineligible activities retain an explicit reason
and coverage instead of zero values. This policy remains protocol version 1 and does not change the persisted shape.
Three Dimensional Strain Evidence is a legacy, versioned activity stat retained only so historical native JSON remains
readable. FIT, TCX, GPX, and provider parsing no longer generates or regenerates it: one workout cannot establish a
current CP/W′/Pmax capacity model. Reprocessing an original source therefore retains its power evidence but produces no
new strain stat. Historic v1 and v2 records remain readable and excluded from event summaries.
For the literature provenance, complete equations, estimator gates, external-algorithm comparison, validation boundary, and known limitations, read the three-dimensional power and training-response model guide.
buildPowerDurationEnvelope accepts dated activity power curves and samples their maximum envelope at fixed
short-duration and CP/W′ anchors. fitThreeDimensionalCapacityModel then applies evidence, fit-quality, and
leave-one-anchor-out stability gates. The default contract requires at least three distinct activities spanning 14
days, sufficient 2–20-minute evidence, and sufficient 1–30-second evidence. It returns a complete model only for
ready; partial can expose a stable CP while withholding unstable W′ and dependent Pmax, or expose stable CP/W′
while withholding Pmax and the complete model.
Diagnostics distinguish usable input curves (sourceCount) from the distinct activities that actually supplied the
retained sustained and short-duration envelope points (criticalPowerContributingSourceCount and
maximumPowerContributingSourceCount). These contributor counts reveal when a component is concentrated in one
workout. Whole-source removal diagnostics also report how much CP and W′ change when each sustained-envelope
contributor is removed. These values do not impose an additional readiness threshold. Anchor and contributor coverage
is reported even when an earlier CP/W′ quality gate stops the fit.
The estimator also rejects the characteristic 1–3-second arithmetic-decay signature of one isolated power sample and includes a 720-second point in newly generated default power curves. Rejected short points are reported separately from malformed points.
Inputs must all belong to one exact canonical activity type. Activity groups are not a pooling boundary: Cycling,
Indoor Cycling, Running, rowing types, and every other power-bearing type maintain independent histories. Every
curve must also predate effectiveDate, so accidentally supplied future evidence returns invalid-input instead of
changing an older capacity snapshot.
The consuming application owns window selection and persistence. A practical policy is to fit each Monday from the previous 42 completed UTC days, use that snapshot for workouts until the next Monday, and retain the input dates, source IDs, source fingerprint, result, and diagnostics. Do not use a later snapshot to rescore an earlier workout as though that capacity had been known at the time.
import {
calculateThreeDimensionalStrain,
fitThreeDimensionalCapacityModel,
type DatedActivityPowerCurve,
type ThreeDimensionalPowerSample
} from '@sports-alliance/sports-lib';
function scoreWorkout(
history: readonly DatedActivityPowerCurve[],
workoutPower: readonly ThreeDimensionalPowerSample[],
snapshotEffectiveDate: string
) {
const capacity = fitThreeDimensionalCapacityModel(history, {
effectiveDate: snapshotEffectiveDate
});
if (capacity.status !== 'ready' || !capacity.model) {
return { capacity, strain: null };
}
return {
capacity,
strain: calculateThreeDimensionalStrain(workoutPower, capacity.model)
};
}
An activity's strain must be calculated with the ready snapshot effective on that activity's date. Aggregate only ready workout results, keep the CP, W′, and Pmax components separate, and never substitute FTP, historic parser CP/W′, another activity type, or a future model when capacity is unavailable.
Three-dimensional impulse-response utilities:
fitThreeParameterCriticalPowerModel fits the Morton three-parameter power-duration model from a mean-max curve,
returning CP, W′, Pmax, fit quality, and convergence state. It needs several distinct maximal-duration efforts; a
mathematically successful fit is not proof those efforts represent current capacity.calculateThreeDimensionalStrain takes continuous power plus those externally chosen model parameters. It uses W′
balance, maximum power available (MPA), and the CP/W′/Pmax power allocation to return total strain and the three
components. It never fills missing power samples: insufficient coverage, missing power, and power above Pmax leave
scores unavailable.wPrimeBalanceTiming: 'after-sample'; consumers must persist both choices. When observed power exceeds MPA, the
strain calculation floors MPA at observed power so the strain coefficient remains at or below one.calculateThreeDimensionalImpulseResponse applies independent exponential fitness-fatigue responses to the three
strain series using caller-supplied parameters.The three-dimensional power and training-response model guide distinguishes the published response model, the authors' illustrative R fitter, and Sports Lib's chronological validation contract.
fitThreeDimensionalImpulseResponseParameters calibrates three independent fitness-fatigue responses from
pre-aggregated, date-keyed CP, W′, and Pmax strain loads. The three outputs represent distinct energy-system-specific
responses; a strong CP fit does not validate W′ or Pmax. The model is a training-response model, not an FTP estimate
and not a source of generic fitness, fatigue, gains, or time constants.
Observations must be independent performance measurements from a stable, documented test protocol. Do not use a power-curve self-fit, a device estimate, or another value derived from the same activities used to produce the strain loads: that would let the model validate against its own input. Omitted dates within the supplied history are rest days and are zero-filled, so do not omit a date to represent unknown or incomplete activity data. The latest observations for each output are held out chronologically, so the fitter evaluates whether the learned response predicts later testing instead of merely describing the history used to fit it.
The fitter returns ready only for outputs that pass this held-out quality gate. partial, poor-fit, and
insufficient-evidence do not supply predictive parameters for their unavailable outputs; invalid-input indicates
that the supplied data cannot be interpreted safely. It also withholds a model with no measurable training response,
time constants at the configured search boundary, or a nonpositive baseline or predicted daily performance. Defaults
are data-sufficiency, numerical-bound, physical-plausibility, and validation safeguards—not population parameters.
Aggregate the three strain components from eligible activities of one canonical activity type into one
ThreeDimensionalDailyStrainLoad per calendar day. Include every date with an activity of that type; rest days may be
omitted because they are zero-filled. Record independent CP, W′, and Pmax test results on their actual date using the
same type-specific protocol. Do not represent an unparsed or unavailable activity as a rest day. The default policy
requires at least 16 observations per output, including 12 fitting observations, four latest held-out observations, and
a 56-day fitting span.
import {
fitThreeDimensionalImpulseResponseParameters,
type ThreeDimensionalDailyStrainLoad,
type ThreeDimensionalPerformanceObservation
} from '@sports-alliance/sports-lib';
function calibrateFromRetainedHistory(
dailyLoads: readonly ThreeDimensionalDailyStrainLoad[],
observations: readonly ThreeDimensionalPerformanceObservation[]
) {
// dailyLoads contains one summed strain record per activity date.
// observations contains independently administered CP/W′/Pmax test results.
const calibration = fitThreeDimensionalImpulseResponseParameters(dailyLoads, observations);
return {
calibration,
criticalPowerParameters:
calibration.criticalPower.status === 'ready' ? calibration.criticalPower.parameters : null
};
}
Persist calibration data in the consuming application, not as a sports-lib storage contract: retain the raw daily
loads, independent observations, test-protocol and version metadata, result, and diagnostics. Retain all history from
the first test onward, inspect held-out error after every fit, and recalibrate when new independent test results arrive.
Persist a component's parameters as predictive parameters only when its individual status is ready; retain non-ready
results and diagnostics for audit. A top-level partial result may still contain valid parameters for one or two
components.
Priority order:
Motorized and Adaptive Mobility activities do not receive library-calculated TSS, even when calculation inputs are
available. A source-provided TSS remains available and is labeled IMPORTED; no durability evidence is generated for
either group.
POWER TSS:
IF = NP / FTP
EffectiveDuration = max(durationWithoutPauses - 29, 0)
TSS = (100 * EffectiveDuration * NP * IF) / (FTP * 3600)
HR TSS:
PACE TSS (running/trail groups):
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^2
SWIM_PACE TSS:
IF = SwimSpeed / ThresholdSwimSpeed
TSS = 100 * (duration/3600) * IF^3
MET TSS:
METScore = (3600 * Energy) / (Weight * Duration)
IF = METScore / ThresholdMET
TSS = 100 * (duration/3600) * IF^2
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)
SWOLF uses Average Stroke Rate; the naming change does not alter the calculation.
Recovery Time from the chronologically final activity. Recovery is a post-activity estimate,
so it is not summed, averaged, maximized, or promoted from an earlier activity when the final activity omits it.Generated from modules re-exported by src/data/index.ts, then resolved to each module's concrete static type declarations.
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)Beginning Potential Stamina (unit: %)Bike PodCadence (unit: rpm)Contact Time to Flight Time Ratio (unit: %)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)Depth in feet (unit: ft)DescentDescent TimeDescriptionDevice LocationDevice NamesDistance (unit: m)Distance (Stryd)Durability EvidenceFIT Training File References (nonnumeric, explicit metadata reader only)FIT Workout Definitions (nonnumeric, explicit metadata reader only)SuuntoPlus Guide References (nonnumeric, explicit metadata reader only)Duration (unit: s)Elapsed time (unit: s)Effort PaceEHPEEnabled Navigation SystemsEnd AltitudeEnd PositionEnding Potential Stamina (unit: %)Energy (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 Contact Time Percentage (unit: %)Ground Time (unit: ms)Heart Rate (unit: bpm)Heart Rate UsedHeight (unit: m)IBI (unit: ms)IntensityImpact Loading Rate Balance LeftImpact Loading Rate Balance RightJump CountJump DistanceJump EventJump ScoreLatitude (unit: degrees)Left Pedal Smoothness (unit: %)Left Torque Effectiveness (unit: %)Leg Spring Stiffness (unit: "KN/m")Leg Spring Stiffness Balance LeftLeg Spring Stiffness Balance RightLeg Stiffness (unit: "KN/m")Longitude (unit: degrees)Metabolic Calories (unit: kcal)Moving timeNumber of SamplesNumber of SatellitesPace (unit: min/km)Pause TimePeak EPOCPeak Training EffectPool Length (unit: m)PositionPower (unit: watt)Power Balance LeftPower Balance RightPower 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)PowerCurvePowerWattsPerKgPotential Stamina (unit: %)Primary BenefitRated Perceived ExertionRecovery TimeResting Calories (unit: kcal)Rider Position Change EventRight Pedal Smoothness (unit: %)Right Torque Effectiveness (unit: %)RotationsRunning Flight Time (unit: ms)Satellite 5 Best SNRSea Level Pressure (unit: hpa)Speed (unit: m/s)Speed (Stryd)Sport Profile NameStamina (unit: %)Stance Time (deprecated historical compatibility token; unit: ms)Stance Time Balance Left (deprecated historical compatibility token)Stance Time Balance Right (deprecated historical compatibility token)Start EventStart PositionStarting AltitudeStep LengthSteps (unit: count)Stop ALL EventStop EventStroke Rate (unit: spm)Swim Pace (unit: min/100m)Temperature (unit: °C)TimeTimer timeThree Dimensional Strain EvidenceTotal CyclesTotal FlowTotal GritTraining Load PeakTraining Stress ScoreTraining Stress Score MethodVertical Oscillation (unit: mm)Vertical Oscillation Balance LeftVertical Oscillation Balance RightVertical Ratio (unit: %)Vertical Speed (unit: m/s)VO2 Max (unit: ml/kg/min)Weight (unit: kg)WPrimeActive Duration (unit: s)Active Energy (unit: kcal)Average Sleep Heart Rate (unit: bpm)Average Sleep HRV (unit: ms)Average Sleep Respiration Rate (unit: br/min)Awake Sleep Duration (unit: s)Basal Energy (unit: kcal)Blood Oxygen Saturation (unit: %)Body Energy (unit: %)Body Energy Change (unit: %)Body Fat (unit: %)Body Mass Index (unit: kg/m²)Body Water (unit: %)Bone Mass (unit: kg)Deep Sleep Duration (unit: s)Diastolic Blood Pressure (unit: mmHg)Floors Climbed (unit: count)Heart Rate Variability (unit: ms)Light Sleep Duration (unit: s)Maximum Sleep Blood Oxygen Saturation (unit: %)Minimum Sleep Heart Rate (unit: bpm)Moderate Intensity Duration (unit: s)Muscle Mass (unit: kg)Overnight HRV (unit: ms)Pulse Rate (unit: bpm)Recovery Score (unit: score)REM Sleep Duration (unit: s)Respiration Rate (unit: br/min)Resting Heart Rate (unit: bpm)Skin Temperature Deviation (unit: °C)Sleep Duration (unit: s)Sleep HRV Sample Count (unit: count)Sleep In-Bed Duration (unit: s)Sleep Resting Heart Rate (unit: bpm)Sleep Score (unit: score)Stress Duration (unit: s)Stress Level (unit: score)Stress State (unit: category)Systolic Blood Pressure (unit: mmHg)Total Energy (unit: kcal)Unknown Sleep Duration (unit: s)Unmeasurable Sleep Duration (unit: s)Vigorous Intensity Duration (unit: s)Wheelchair Push Distance (unit: m)Wheelchair Pushes (unit: count)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 TargetAverage Absolute PressureAverage Air PowerAverage AltitudeAverage CadenceAverage Contact Time to Flight Time Ratio (unit: %)Average 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 Ground Contact Time Percentage (unit: %)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 Potential Stamina (unit: %)Average Respiration Rate (unit: br/min)Average Running Flight Time (unit: ms)Average Satellite 5 Best SNRAverage SpeedAverage Stamina (unit: %)Average 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 Stroke Rate (unit: spm)Average Swim PaceAverage swim pace in minutes per 100 yardAverage SWOLF 25mAverage SWOLF 50mAverage TemperatureAverage VAM (unit: m/h; FIT avg_vam source values in m/s are converted to this public unit)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 Contact Time to Flight Time Ratio (unit: %)Maximum Depth (unit: m)Maximum Depth in feet (unit: ft)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 Ground Contact Time Percentage (unit: %)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 Potential Stamina (unit: %)Maximum Respiration Rate (unit: br/min)Maximum Running Flight Time (unit: ms)Maximum Satellite 5 Best SNRMaximum SpeedMaximum Stamina (unit: %)Maximum 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 Stroke Rate (unit: spm)Maximum 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 Contact Time to Flight Time Ratio (unit: %)Minimum 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 Ground Contact Time Percentage (unit: %)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 Potential Stamina (unit: %)Minimum Respiration Rate (unit: br/min)Minimum Running Flight Time (unit: ms)Minimum Satellite 5 Best SNRMinimum SpeedMinimum Stamina (unit: %)Minimum 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 Stroke Rate (unit: spm)Minimum 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 hourDistance 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)