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

Classes: HrZone, PaceBand

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
HR_ZONE_BOUNDARIES =

Heart-rate zone boundaries as fractions of Heart Rate Reserve (Karvonen)

[0.50, 0.60, 0.70, 0.80, 0.90, 1.00].freeze

Instance Method Summary collapse

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)

Examples:

calc.hr_zones(hr_max: 190, hr_rest: 55).last.max_bpm #=> 190

Parameters:

  • hr_max (Numeric)

    maximum heart rate in bpm (must be > 0)

  • hr_rest (Numeric)

    resting heart rate in bpm (must be > 0 and < hr_max)

Returns:

  • (Array<HrZone>)

    five contiguous zones from Z1 (50–60% HRR) to Z5 (90–100% HRR)

Raises:



86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/calcpace/training_zones.rb', line 86

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
  points  = HR_ZONE_BOUNDARIES.map { |pct| (rest + (pct * reserve)).round }

  points.each_cons(2).with_index(1).map do |(min_bpm, max_bpm), zone|
    HrZone.new(zone: zone, min_bpm: min_bpm, max_bpm: max_bpm)
  end
end

#training_paces(vo2max) ⇒ Hash{Symbol => PaceBand}

Derives training pace bands from a VO2max value

Examples:

calc.training_paces(50.0)[:threshold].fast_clock #=> "00:04:15"

Parameters:

  • vo2max (Numeric)

    VO2max in ml/kg/min (must be > 0)

Returns:

  • (Hash{Symbol => PaceBand})

    keys: :easy, :marathon, :threshold, :interval, :repetition — paces per km

Raises:



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/calcpace/training_zones.rb', line 41

def training_paces(vo2max)
  check_positive(vo2max.to_f, 'VO2max')

  TRAINING_INTENSITIES.transform_values do |band|
    slow = pace_seconds_at_pct(vo2max.to_f, band[:low])
    fast = pace_seconds_at_pct(vo2max.to_f, band[:high])

    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(distance_km, time) ⇒ 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.

Examples:

calc.training_paces_from_race(10.0, '00:40:00')[:easy].slow_clock #=> "00:05:42"

Parameters:

  • distance_km (Numeric)

    race distance in kilometres (must be > 0)

  • time (String, Integer)

    finish time as "HH:MM:SS" / "MM:SS" or total seconds

Returns:

  • (Hash{Symbol => PaceBand})

    same shape as #training_paces

Raises:



70
71
72
# File 'lib/calcpace/training_zones.rb', line 70

def training_paces_from_race(distance_km, time)
  training_paces(estimate_vo2max(distance_km, time))
end