Class: RoadToRubykaigi::SignalWindow
- Inherits:
-
Object
- Object
- RoadToRubykaigi::SignalWindow
- Defined in:
- lib/road_to_rubykaigi/signal_window.rb
Defined Under Namespace
Classes: StepCadence
Constant Summary collapse
- BUFFER_SECONDS =
0.5- READY_FILL_RATIO =
window must be 80% filled (by time) before considered ready
0.8
Instance Method Summary collapse
-
#axis_intensities ⇒ Object
Per-axis RMS spread.
- #buffer_sample(sample) ⇒ Object
- #cadence_hz ⇒ Object
- #cadence_ready? ⇒ Boolean
- #full ⇒ Object
- #full? ⇒ Boolean
-
#last_magnitude ⇒ Object
Raw acceleration magnitude of the latest sample: sqrt(x² + y² + z²).
-
#last_sample ⇒ Object
Raw [x, y, z] of the latest sample (before variance/intensity).
-
#last_vertical_acceleration(gravity) ⇒ Object
Signed vertical acceleration of the latest sample after removing gravity.
-
#mag_jerk ⇒ Object
Average absolute change in magnitude between consecutive samples.
-
#motion_intensity ⇒ Object
Returns how far samples in the window spread from their mean position (RMS distance across all 3 axes).
-
#tail(seconds:) ⇒ Object
Sub-window containing samples from the last ‘seconds`, for continuation detection.
Instance Method Details
#axis_intensities ⇒ Object
Per-axis RMS spread. Same unit as motion_intensity but kept separate so callers can compare how energy is distributed across axes.
78 79 80 |
# File 'lib/road_to_rubykaigi/signal_window.rb', line 78 def axis_intensities [0, 1, 2].map { |index| Math.sqrt(axis_variance(index)) } end |
#buffer_sample(sample) ⇒ Object
54 55 56 57 58 59 60 |
# File 'lib/road_to_rubykaigi/signal_window.rb', line 54 def buffer_sample(sample) now = Time.now @samples << { time: now, sample: sample } window_start = now - BUFFER_SECONDS @samples.shift while @samples.first[:time] < window_start @step_cadence.record(sample) end |
#cadence_hz ⇒ Object
62 |
# File 'lib/road_to_rubykaigi/signal_window.rb', line 62 def cadence_hz = @step_cadence.hz |
#cadence_ready? ⇒ Boolean
63 |
# File 'lib/road_to_rubykaigi/signal_window.rb', line 63 def cadence_ready? = @step_cadence.ready? |
#full ⇒ Object
115 116 117 |
# File 'lib/road_to_rubykaigi/signal_window.rb', line 115 def full self end |
#full? ⇒ Boolean
65 66 67 68 |
# File 'lib/road_to_rubykaigi/signal_window.rb', line 65 def full? return false if @samples.size < 2 (@samples.last[:time] - @samples.first[:time]) >= BUFFER_SECONDS * READY_FILL_RATIO end |
#last_magnitude ⇒ Object
Raw acceleration magnitude of the latest sample: sqrt(x² + y² + z²).
83 84 85 86 |
# File 'lib/road_to_rubykaigi/signal_window.rb', line 83 def last_magnitude sample = @samples.last[:sample] Math.sqrt(sample[0] ** 2 + sample[1] ** 2 + sample[2] ** 2) end |
#last_sample ⇒ Object
Raw [x, y, z] of the latest sample (before variance/intensity).
89 90 91 |
# File 'lib/road_to_rubykaigi/signal_window.rb', line 89 def last_sample @samples.last[:sample] end |
#last_vertical_acceleration(gravity) ⇒ Object
Signed vertical acceleration of the latest sample after removing gravity. Positive = accelerating upward, negative = downward.
95 96 97 98 99 100 101 102 103 104 |
# File 'lib/road_to_rubykaigi/signal_window.rb', line 95 def last_vertical_acceleration(gravity) sample = @samples.last[:sample] # Magnitude of the gravity reference vector (used to normalize the # projection below and as the resting 1g offset). gravity_magnitude = Math.sqrt(gravity[0] ** 2 + gravity[1] ** 2 + gravity[2] ** 2) # Sample projected onto the vertical axis, normalized to g units. projection = (sample[0] * gravity[0] + sample[1] * gravity[1] + sample[2] * gravity[2]) / gravity_magnitude # Subtract the resting offset. projection - gravity_magnitude end |
#mag_jerk ⇒ Object
Average absolute change in magnitude between consecutive samples. Walking/running produce sharp footstrike impacts (high jerk), while jumping produces smoother acceleration curves (low jerk).
109 110 111 112 113 |
# File 'lib/road_to_rubykaigi/signal_window.rb', line 109 def mag_jerk mags = @samples.map { |entry| s = entry[:sample]; Math.sqrt(s[0] ** 2 + s[1] ** 2 + s[2] ** 2) } deltas = mags.each_cons(2).map { |a, b| (b - a).abs } deltas.sum / deltas.size end |
#motion_intensity ⇒ Object
Returns how far samples in the window spread from their mean position (RMS distance across all 3 axes).
72 73 74 |
# File 'lib/road_to_rubykaigi/signal_window.rb', line 72 def motion_intensity Math.sqrt(axis_variance(0) + axis_variance(1) + axis_variance(2)) end |
#tail(seconds:) ⇒ Object
Sub-window containing samples from the last ‘seconds`, for continuation detection.
120 121 122 123 124 |
# File 'lib/road_to_rubykaigi/signal_window.rb', line 120 def tail(seconds:) return SignalWindow.new([]) if @samples.empty? cutoff = @samples.last[:time] - seconds SignalWindow.new(@samples.select { |entry| entry[:time] >= cutoff }) end |