Files
Stride/docs/fitness.md
T

75 lines
3.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.