Module: TrainingZones
- Included in:
- Calcpace
- Defined in:
- lib/calcpace/training_zones.rb
Overview
Module for deriving personalized training zones from VO2max
Training paces invert the Daniels & Gilbert (1979) velocity equation:
VO2 = -4.60 + 0.182258 * v + 0.000104 * v² (v in m/min)
Solving the quadratic for v at a target %VO2max gives the running velocity for each training intensity (Daniels' Running Formula).
Heart rate zones use the Karvonen method (Heart Rate Reserve):
target = hr_rest + pct * (hr_max - hr_rest)
Defined Under Namespace
Constant Summary collapse
- TRAINING_INTENSITIES =
Training intensities as fraction of VO2max (Daniels' Running Formula)
{ easy: { low: 0.59, high: 0.74 }, marathon: { low: 0.75, high: 0.84 }, threshold: { low: 0.83, high: 0.88 }, interval: { low: 0.95, high: 1.00 }, repetition: { low: 1.05, high: 1.10 } }.freeze
- PACE_UNIT_METERS =
Metres per pace unit — pace bands can be expressed per km or per mile
{ km: 1000.0, mi: Converter::Distance::MI_TO_METERS }.freeze
- HR_ZONE_BOUNDARIES =
Heart-rate zone boundaries as fractions of the range being split: Heart Rate Reserve in #hr_zones (Karvonen) and HRmax in #hr_zones_from_max
[0.50, 0.60, 0.70, 0.80, 0.90, 1.00].freeze
Instance Method Summary collapse
-
#hr_zones(hr_max:, hr_rest:) ⇒ Array<HrZone>
Computes the five Karvonen heart-rate training zones.
-
#hr_zones_from_max(hr_max:) ⇒ Array<HrZone>
Computes five heart-rate zones from maximum heart rate only (%HRmax method).
-
#training_paces(vo2max, unit: :km) ⇒ Hash{Symbol => PaceBand}
Derives training pace bands from a VO2max value.
-
#training_paces_from_race(race, time, unit: :km, distance_unit: nil) ⇒ Hash{Symbol => PaceBand}
Derives training pace bands from a recent race result.
Instance Method Details
#hr_zones(hr_max:, hr_rest:) ⇒ Array<HrZone>
Computes the five Karvonen heart-rate training zones
target_bpm = hr_rest + pct * (hr_max - hr_rest)
117 118 119 120 121 122 123 124 |
# File 'lib/calcpace/training_zones.rb', line 117 def hr_zones(hr_max:, hr_rest:) max = hr_max.to_f rest = hr_rest.to_f check_heart_rates(max, rest) reserve = max - rest build_hr_zones(HR_ZONE_BOUNDARIES.map { |pct| (rest + (pct * reserve)).round }) end |
#hr_zones_from_max(hr_max:) ⇒ Array<HrZone>
Computes five heart-rate zones from maximum heart rate only (%HRmax method)
Fallback for athletes who don't know their resting heart rate — the classic percent-of-max model used as default by most sports watches:
target_bpm = pct * hr_max
Prefer #hr_zones (Karvonen) when resting heart rate is available.
140 141 142 143 144 145 |
# File 'lib/calcpace/training_zones.rb', line 140 def hr_zones_from_max(hr_max:) max = hr_max.to_f check_positive(max, 'Maximum heart rate') build_hr_zones(HR_ZONE_BOUNDARIES.map { |pct| (pct * max).round }) end |
#training_paces(vo2max, unit: :km) ⇒ Hash{Symbol => PaceBand}
Derives training pace bands from a VO2max value
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
# File 'lib/calcpace/training_zones.rb', line 48 def training_paces(vo2max, unit: :km) check_positive(vo2max.to_f, 'VO2max') meters = pace_unit_meters(unit) TRAINING_INTENSITIES.transform_values do |band| slow = pace_seconds_at_pct(vo2max.to_f, band[:low], meters) fast = pace_seconds_at_pct(vo2max.to_f, band[:high], meters) PaceBand.new( slow_seconds: slow, fast_seconds: fast, slow_clock: convert_to_clocktime(slow), fast_clock: convert_to_clocktime(fast) ) end end |
#training_paces_from_race(race, time, unit: :km, distance_unit: nil) ⇒ Hash{Symbol => PaceBand}
Derives training pace bands from a recent race result
Convenience wrapper: estimates VO2max via Daniels & Gilbert (see Vo2maxEstimator#estimate_vo2max) and derives the bands from it.
91 92 93 94 95 96 97 98 99 100 101 102 103 |
# File 'lib/calcpace/training_zones.rb', line 91 def training_paces_from_race(race, time, unit: :km, distance_unit: nil) # Numeric strings ('10') keep working as distances; only non-numeric input # (race names) falls through to the RACE_DISTANCES lookup numeric = race.is_a?(Numeric) ? race : Float(race, exception: false) distance_km = if numeric normalize_distance_km(numeric, distance_unit || :km) else reject_distance_unit_with_race_name!(distance_unit, race) race_distance(race) end training_paces(estimate_vo2max(distance_km, time), unit: unit) end |