Class: RoadToRubykaigi::JumpDetector
- Inherits:
-
Object
- Object
- RoadToRubykaigi::JumpDetector
- Defined in:
- lib/road_to_rubykaigi/jump_detector.rb
Overview
Detects squat-jumps from the accelerometer stream.
Axis-agnostic: consumes the gravity-compensated vertical acceleration instead of any single raw axis. vertical_acceleration = projection of the sample onto the gravity vector, minus |gravity|. So rest = 0, upward proper acceleration > 0, downward (or free fall) < 0.
Rule: loaded hold + bottom turnover. Running strides briefly peak the vertical acceleration above the loaded threshold at each footstrike. A squat-jump holds it above the threshold noticeably longer. Fire at the bottom of that span (slope becomes clearly negative), whether the signal is still above the threshold or has just crossed back below.
Firing at the bottom of the load (not at landing or at full takeoff) minimizes latency.
The loaded span must start from an up-crossing (a prior sample at or below the threshold) inside the buffer. Otherwise a stationary sensor reading near 0 could drift above threshold and accumulate an unbounded “loaded hold” that passes the duration gate on its own.
Constant Summary collapse
- LOADED_THRESHOLD =
vertical_acceleration above this counts as “loaded” (body under extra g-load)
0.20- LOADED_MIN_SECONDS =
loaded span qualifying as squat hold
0.2- TAKEOFF_SLOPE_MAX =
g/s — fall slope indicating takeoff turnover
-1.0 # g/s — fall slope indicating takeoff turnover
- SLOPE_WINDOW_SECONDS =
0.08- LOADED_END_GRACE_SECONDS =
allow fire shortly past the span’s last above-threshold point
0.15- COOLDOWN_SECONDS =
min gap between consecutive fires (covers takeoff→landing)
0.8- SAMPLE_BUFFER_SECONDS =
retain samples long enough to cover an elongated squat hold
1.2- MIN_SAMPLES_FOR_ANALYSIS =
slope + hold both need a few samples to be meaningful
5
Instance Method Summary collapse
- #detect(sample:) ⇒ Object
-
#initialize(gravity:) ⇒ JumpDetector
constructor
A new instance of JumpDetector.
Constructor Details
#initialize(gravity:) ⇒ JumpDetector
Returns a new instance of JumpDetector.
33 34 35 36 37 38 |
# File 'lib/road_to_rubykaigi/jump_detector.rb', line 33 def initialize(gravity:) @gravity = gravity @gravity_magnitude = Math.sqrt(gravity[0] ** 2 + gravity[1] ** 2 + gravity[2] ** 2) @last_samples = [] # [{time:, vertical_acceleration:}] sliding window @last_jump_time = nil end |
Instance Method Details
#detect(sample:) ⇒ Object
40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
# File 'lib/road_to_rubykaigi/jump_detector.rb', line 40 def detect(sample:) now = Time.now @last_samples << { time: now, vertical_acceleration: vertical_acceleration(sample) } cutoff = now - SAMPLE_BUFFER_SECONDS @last_samples.shift while !@last_samples.empty? && @last_samples.first[:time] < cutoff if !buffer_ready? || !squat_takeoff? || cooling_down?(now) false else @last_jump_time = now debug_log('JUMP_FIRED') true end end |