Class: RoadToRubykaigi::SignalWindow::StepCadence

Inherits:
Data
  • Object
show all
Defined in:
lib/road_to_rubykaigi/signal_window.rb

Constant Summary collapse

WINDOW_SECONDS =
2.0
MIN_STEP_INTERVAL_SECONDS =

min gap between steps (caps cadence at ~6.7Hz)

0.15
MIN_SAMPLES_FOR_LOCAL_MAXIMUM =

need prev/current/next to detect a local maximum

3

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#recent_magnitudesObject (readonly)

Returns the value of attribute recent_magnitudes

Returns:

  • (Object)

    the current value of recent_magnitudes



6
7
8
# File 'lib/road_to_rubykaigi/signal_window.rb', line 6

def recent_magnitudes
  @recent_magnitudes
end

Instance Method Details

#hzObject

Step cadence in Hz: count of local maxima in the recent magnitudes divided by the window duration. Walking produce one peak per footstrike, so cadence tracks step frequency.



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/road_to_rubykaigi/signal_window.rb', line 21

def hz
  return 0.0 if recent_magnitudes.size < MIN_SAMPLES_FOR_LOCAL_MAXIMUM

  magnitudes = recent_magnitudes.map { |entry| entry[:magnitude] }
  mean = magnitudes.sum / magnitudes.size
  step_count = 0
  last_step_time = nil
  (1...(recent_magnitudes.size - 1)).each do |i|
    time = recent_magnitudes[i][:time]
    magnitude = recent_magnitudes[i][:magnitude]
    prev_magnitude = recent_magnitudes[i - 1][:magnitude]
    next_magnitude = recent_magnitudes[i + 1][:magnitude]
    is_local_maximum = magnitude > prev_magnitude && magnitude > next_magnitude
    above_mean = magnitude > mean # only count bumps stronger than the average
    enough_gap_from_last_step = last_step_time.nil? || (time - last_step_time) >= MIN_STEP_INTERVAL_SECONDS
    if is_local_maximum && above_mean && enough_gap_from_last_step
      step_count += 1
      last_step_time = time
    end
  end

  duration = recent_magnitudes.last[:time] - recent_magnitudes.first[:time]
  return 0.0 if duration <= 0 # guard against division by zero (just in case)

  step_count / duration
end

#ready?Boolean

Returns:

  • (Boolean)


48
49
50
51
# File 'lib/road_to_rubykaigi/signal_window.rb', line 48

def ready?
  return false if recent_magnitudes.size < MIN_SAMPLES_FOR_LOCAL_MAXIMUM
  (recent_magnitudes.last[:time] - recent_magnitudes.first[:time]) >= WINDOW_SECONDS * READY_FILL_RATIO
end

#record(sample) ⇒ Object



11
12
13
14
15
16
17
# File 'lib/road_to_rubykaigi/signal_window.rb', line 11

def record(sample)
  magnitude = Math.sqrt(sample[0] ** 2 + sample[1] ** 2 + sample[2] ** 2)
  now = Time.now
  recent_magnitudes << { time: now, magnitude: magnitude }
  window_start = now - WINDOW_SECONDS
  recent_magnitudes.shift while recent_magnitudes.first[:time] < window_start
end