Module: ClimbFactor

Defined in:
lib/climb_factor/physiology.rb,
lib/climb_factor.rb,
lib/climb_factor/geometry.rb,
lib/climb_factor/filtering.rb,
lib/climb_factor/low_level_math.rb

Overview

For the cr and cw functions, see Minetti, jap.physiology.org/content/93/3/1039.full

Defined Under Namespace

Modules: CfMath, Filtering, Geom, Phys

Class Method Summary collapse

Class Method Details

.estimate(hv, nominal_distance: nil, filtering: 200.0) ⇒ Object



6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/climb_factor.rb', line 6

def self.estimate(hv,nominal_distance:nil,filtering:200.0)
  # inputs:
  #   All inputs are in units of meters.
  #   hv = list of [horizontal,vertical] coordinate pairs
  #   nominal_distance = if supplied, scale horizontal length of course to equal this value
  #   filtering = get rid of bogus fluctuations in vertical data that occur on horizontal scales less than this
  # output:
  #   cf = a floating-point number representing a climb factor, expressed as a percentage
  hv = hv.map do |a|
    unless a.all? { |v| v.is_a?(Numeric) }
      raise ArgumentError, "error in type of input, got #{a.inspect}, should be pairs of Floats"
    end
    a.map(&:to_f)
  end
  hv = Filtering.resample_and_filter_hv(hv,filtering)
  rescale = 1.0
  if !nominal_distance.nil? then
    rescale = nominal_distance/(hv.last[0]-hv[0][0])
  end
  stats,hv = integrate_gain_and_energy(hv,rescale)
  c,h = stats['c'],stats['h'] # energy cost in joules and (possibly rescaled) horiz distance in meters
  cf = 100.0*(c-h*Phys.minetti(0.0))/c
  return cf
end

.integrate_gain_and_energy(hv, rescale, body_mass: 1.0) ⇒ Object



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/climb_factor.rb', line 31

def self.integrate_gain_and_energy(hv,rescale,body_mass:1.0)
  # integrate to find total gain, slope distance, and energy burned
  # returns [stats,hv], where:
  #   stats = {'c'=>c,'d'=>d,'gain'=>gain,'i_rms'=>i_rms,...}
  #   hv = modified copy of input hv, with predicted times added, if we have the necessary data
  v = 0 # total vertical distance (=0 at end of a loop)
  d = 0 # total distance along the slope
  gain = 0 # total gain
  c = 0 # cost in joules
  first = true
  old_h = 0
  old_v = 0
  i_sum = 0.0
  i_sum_sq = 0.0
  iota_sum = 0.0
  iota_sum_sq = 0.0
  baumel_si = 0.0 # compute this directly as a check
  t = 0.0 # integrated time, in seconds
  h_reintegrated = 0.0 # if rescale!=1, this should be the same as nominal_h
  k = 0
  hv.each { |a|
    h,v = a
    if !first then
      dh = (h-old_h)*rescale
      dv = v-old_v
      dd = Math::sqrt(dh*dh+dv*dv)
      h_reintegrated = h_reintegrated+dh
      d = d+dd
      if dv>0 then gain=gain+dv end
      i=0
      if dh>0 then i=dv/dh end
      if dh>0 then baumel_si=baumel_si+dv**2/dh end
      # In the following, weight by dh, although normally this doesn't matter because we make the
      # h intervals constant before this point.
      i_sum = i_sum + i*dh
      i_sum_sq = i_sum_sq + i*i*dh
      dc = dd*body_mass*Phys.minetti(i)
          # in theory it matters whether we use dd or dh here; I think from Minetti's math it's dd
      c = c+dc
      #if not ($split_energy_at.nil?) and d-dd<$split_energy_at_m and d>$split_energy_at_m then
      #   $stderr.print "at d=#{$split_energy_at}, energy=#{(c*0.0002388459).round} kcals\n"
      #   # fixme -- implement this in a better way
      #end
      k=k+1
    end
    old_h = h
    old_v = v
    first = false
  }
  n = hv.length-1.0
  h = h_reintegrated # should equal $nominal_h; may differ from hv.last[0]-hv[0][0] if rescale!=1
  i_rms = Math::sqrt(i_sum_sq/h - (i_sum/h)**2)
  i_mean = (hv.last[1]-hv[0][1])/h
  i0,c0,c2,b0,b1,b2 = Phys.minetti_quadratic_coeffs()
  e_q = h*body_mass*(b0+b1*i_mean+b2*i_rms)
  cf = (c-h*body_mass*Phys.minetti(0.0))/c
  stats = {'c'=>c,'h'=>h,'d'=>d,'gain'=>gain,'i_rms'=>i_rms,'i_mean'=>i_mean,'e_q'=>e_q,
          'cf'=>cf,'baumel_si'=>baumel_si,
          't'=>t}
  return [stats,hv]
end