Files
Stride/docs/fitness.md
T

3.7 KiB
Raw Blame History

Running fitness

Open Fitness in the navigation. Choose automatic or manual five-zone settings in Settings. Existing recordings are scored immediately; no Strava connection or download is needed. Run detail pages show the same score.

Effort v1

Recorded moving minutes in Z1–Z5 have weights 1, 2, 3, 4 and 5 respectively. An exact boundary belongs to the higher zone. Scores use their own scale and do not reproduce Strava numbers. The account's supplied boundaries are 122, 152, 167 and 181 bpm (181 belongs to Z5 to avoid leaving a gap after Z4's 180).

Three setting modes are supported:

  • Automatic: maximum HR is 220 minus age, updated on birthdays, or 190 without a birthday.
  • Manual maximum: the supplied maximum HR is fixed until edited.
  • Custom: four manually supplied boundaries are fixed until edited.

The first two modes generate boundaries at 65%, 80%, 87% and 95% of maximum HR, rounded to the nearest bpm. These are explicit Stride defaults and may differ from Strava's unpublished zone boundary calculation. No peak-HR detection from recordings is performed. Existing three-band Stats settings remain independent.

The calculation uses persisted analysis_summary.intervals[].hr_seconds, which exclude recording breaks, stops and suspect HR. Usable seconds must cover at least 80% of the run's timer duration. The remainder is not extrapolated. Without adequate coverage the run remains unscored. Perceived effort is not used as a fallback because it has a different scale.

Daily model

All recorded runs for the owner are grouped by their local calendar day. Daily load is the sum of scored effort. Fitness and fatigue start at zero at the earliest imported run and update every day, including rest days:

fitness += (load - fitness) * (1 - exp(-1 / 42))
fatigue += (load - fatigue) * (1 - exp(-1 / 7))
form = fitness - fatigue

The first 126 days (three fitness time constants) are marked provisional. An unscored run marks that day and all subsequent estimates provisional: the model continues using known effort only, while the day's complete load remains unavailable. Missing imports cannot be detected. Other sports are excluded. Values are calculated with full precision, rounded for display. Range controls trim only the displayed series.

Historical backfill

The graph replays the complete stored history on each visit. Imports, deleted runs, timezone changes and zone edits therefore recalculate the history automatically. The open page checks for changes on focus and every five minutes while visible. There is no additional score cache. The 8da41b79e632 database migration adds the private user.hr_zones JSON configuration. Run flask --app run db upgrade before starting the updated app. Current zones apply to all dates; historical changes in zones are not reconstructed. This retrospective recalculation is a deliberate difference from Strava's documented future-only handling of subsequent zone edits.

For older imports without current recording summaries, use the existing resumable command (take the normal database backup before operational maintenance):

python -m flask --app run backfill-analysis --owner-id OWNER_ID

If a current-version summary was damaged or lacks intervals despite usable samples, add --force to rebuild it. This regenerates the app's existing analysis summaries, charts and routes as well. Runs without usable original samples remain unscored.

Local data audit during implementation: 18 running activities, all with current recording intervals; 17 meet the 80% HR coverage requirement (one has 78.5%). The supplied five zones were saved in custom mode and history replayed over 109 calendar days. A SQLite backup was taken before applying the configuration migration.