Class: PostRunner::HRV_Analyzer

Inherits:
Object
  • Object
show all
Defined in:
lib/postrunner/HRV_Analyzer.rb

Overview

This class analyzes the heart rate variablity based on the R-R intervals in the given FIT file. It can compute RMSSD and a HRV score if the data quality is good enough.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(fit_file) ⇒ HRV_Analyzer

Create a new HRV_Analyzer object.

Parameters:

  • fit_file (Fit4Ruby::Activity)

    FIT file to analyze.



26
27
28
29
# File 'lib/postrunner/HRV_Analyzer.rb', line 26

def initialize(fit_file)
  @fit_file = fit_file
  collect_rr_intervals
end

Instance Attribute Details

#rr_intervalsObject (readonly)

Returns the value of attribute rr_intervals.



22
23
24
# File 'lib/postrunner/HRV_Analyzer.rb', line 22

def rr_intervals
  @rr_intervals
end

#timestampsObject (readonly)

Returns the value of attribute timestamps.



22
23
24
# File 'lib/postrunner/HRV_Analyzer.rb', line 22

def timestamps
  @timestamps
end

Instance Method Details

#has_hrv_data?Boolean

The method can be used to check if we have valid HRV data. The FIT file must have HRV data and the measurement duration must be at least 30 seconds.

Returns:

  • (Boolean)


34
35
36
# File 'lib/postrunner/HRV_Analyzer.rb', line 34

def has_hrv_data?
  !@fit_file.hrv.empty? && total_duration > 30.0
end

#lnrmssdx20(start_time = 0.0, duration = nil) ⇒ Object

The RMSSD value is not very easy to memorize. Alternatively, we can multiply the natural logarithm of RMSSD by -20. This usually results in values between 1.0 (for untrained) and 100.0 (for higly trained) athletes. Values larger than 100.0 are rare but possible.

Parameters:

  • start_time (Float) (defaults to: 0.0)

    Determines at what time mark (in seconds) the computation should start.

  • duration (Float) (defaults to: nil)

    The duration of the total inteval in seconds to be considered for the computation. This value should be larger then 30 seconds to produce meaningful values.



91
92
93
# File 'lib/postrunner/HRV_Analyzer.rb', line 91

def lnrmssdx20(start_time = 0.0, duration = nil)
  -20.0 * Math.log(rmssd(start_time, duration))
end

#lnrmssdx20_1sigmaObject

This method is similar to lnrmssdx20 but it tries to search the data for the best time period to compute the lnrmssdx20 value from.



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/postrunner/HRV_Analyzer.rb', line 97

def lnrmssdx20_1sigma
  # Create a new Array that consists of rr_intervals and timestamps
  # tuples.
  set = []
  0.upto(@rr_intervals.length - 1) do |i|
    set << [ @rr_intervals[i] ? @rr_intervals[i] : 0.0, @timestamps[i] ]
  end

  percentiles = Percentiles.new(set)
  # Compile a list of all tuples with rr_intervals that are outside of the
  # PT84 (aka +1sigma range. Sort the list by time.
  not_1sigma = percentiles.not_tp_x(84.13).sort { |e1, e2| e1[1] <=> e2[1] }

  # Then find the largest time gap in that list. So all the values in that
  # gap are within TP84.
  gap_start = gap_end = 0
  last = nil
  not_1sigma.each do |e|
    if last
      if (e[1] - last) > (gap_end - gap_start)
        gap_start = last
        gap_end = e[1]
      end
    end
    last = e[1]
  end
  # That gap should be at least 30 seconds long. Otherwise we'll just use
  # all the values.
  return lnrmssdx20 if gap_end - gap_start < 30

  lnrmssdx20(gap_start, gap_end - gap_start)
end

#rmssd(start_time = 0.0, duration = nil) ⇒ Object

Compute the root mean square of successive differences.

Parameters:

  • start_time (Float) (defaults to: 0.0)

    Determines at what time mark (in seconds) the computation should start.

  • duration (Float) (defaults to: nil)

    The duration of the total inteval in seconds to be considered for the computation. This value should be larger then 30 seconds to produce meaningful values.



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
# File 'lib/postrunner/HRV_Analyzer.rb', line 49

def rmssd(start_time = 0.0, duration = nil)
  # Find the start index based on the requested interval start time.
  start_idx = 0
  @timestamps.each do |ts|
    break if ts >= start_time
    start_idx += 1
  end
  # Find the end index based on the requested interval duration.
  if duration
    end_time = start_time + duration
    end_idx = start_idx
    while end_idx < (@timestamps.length - 1) &&
          @timestamps[end_idx] < end_time
      end_idx += 1
    end
  else
    end_idx = -1
  end

  last_i = nil
  sum = 0.0
  cnt = 0
  @rr_intervals[start_idx..end_idx].each do |i|
    if i && last_i
      sum += (last_i - i) ** 2.0
      cnt += 1
    end
    last_i = i
  end

  Math.sqrt(sum / cnt)
end

#total_durationObject

Return the total duration of all measured intervals in seconds.



39
40
41
# File 'lib/postrunner/HRV_Analyzer.rb', line 39

def total_duration
  @timestamps[-1]
end