Class: RoadToRubykaigi::CalibrationResult

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

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#continuation_thresholdObject (readonly)

Returns the value of attribute continuation_threshold

Returns:

  • (Object)

    the current value of continuation_threshold



2
3
4
# File 'lib/road_to_rubykaigi/calibration_result.rb', line 2

def continuation_threshold
  @continuation_threshold
end

#gravity_vectorObject (readonly)

Returns the value of attribute gravity_vector

Returns:

  • (Object)

    the current value of gravity_vector



2
3
4
# File 'lib/road_to_rubykaigi/calibration_result.rb', line 2

def gravity_vector
  @gravity_vector
end

#jump_sample_countObject (readonly)

Returns the value of attribute jump_sample_count

Returns:

  • (Object)

    the current value of jump_sample_count



2
3
4
# File 'lib/road_to_rubykaigi/calibration_result.rb', line 2

def jump_sample_count
  @jump_sample_count
end

#jump_v_maxObject (readonly)

Returns the value of attribute jump_v_max

Returns:

  • (Object)

    the current value of jump_v_max



2
3
4
# File 'lib/road_to_rubykaigi/calibration_result.rb', line 2

def jump_v_max
  @jump_v_max
end

#start_thresholdObject (readonly)

Returns the value of attribute start_threshold

Returns:

  • (Object)

    the current value of start_threshold



2
3
4
# File 'lib/road_to_rubykaigi/calibration_result.rb', line 2

def start_threshold
  @start_threshold
end

#static_sample_countObject (readonly)

Returns the value of attribute static_sample_count

Returns:

  • (Object)

    the current value of static_sample_count



2
3
4
# File 'lib/road_to_rubykaigi/calibration_result.rb', line 2

def static_sample_count
  @static_sample_count
end

#walk_cadenceObject (readonly)

Returns the value of attribute walk_cadence

Returns:

  • (Object)

    the current value of walk_cadence



2
3
4
# File 'lib/road_to_rubykaigi/calibration_result.rb', line 2

def walk_cadence
  @walk_cadence
end

#walk_cadence_sample_countObject (readonly)

Returns the value of attribute walk_cadence_sample_count

Returns:

  • (Object)

    the current value of walk_cadence_sample_count



2
3
4
# File 'lib/road_to_rubykaigi/calibration_result.rb', line 2

def walk_cadence_sample_count
  @walk_cadence_sample_count
end

#walk_intensityObject (readonly)

Returns the value of attribute walk_intensity

Returns:

  • (Object)

    the current value of walk_intensity



2
3
4
# File 'lib/road_to_rubykaigi/calibration_result.rb', line 2

def walk_intensity
  @walk_intensity
end

Class Method Details

.from_samples(static:, walk:, jump:) ⇒ Object

Derives calibrated values from per-phase collected samples.



14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/road_to_rubykaigi/calibration_result.rb', line 14

def self.from_samples(static:, walk:, jump:)
  # Use p95 rather than max so a single stray spike during "still" doesn't
  # inflate every downstream threshold.
  sorted_static = static[:intensities].sort
  noise_ceiling = sorted_static[(sorted_static.size * 0.95).floor] || 0.0
  # Noise ceiling * 2.5 as the threshold separating noise from walking.
  # Stays above noise even in short-window valleys between steps.
  #   2.5 is an empirical factor derived from real calibration data.
  start_threshold = noise_ceiling * 2.5
  # Continuation uses a short window (fast stop detection) which is noise-sensitive,
  # so use a higher threshold for noise tolerance.
  continuation_threshold = noise_ceiling * 5.0
  gravity_vector = mean_vector(static[:raw_samples])
  new(
    start_threshold:,
    continuation_threshold:,
    walk_cadence:     median(walk[:cadences]), # Median walking step cadence in Hz, representing individual step frequency.
    walk_intensity:   median(walk[:intensities]), # Used by the intensity-boost path that lifts in-place running (elevated intensity, walk-level cadence).
    gravity_vector:,
    jump_v_max:       max_vertical_acceleration(jump[:raw_samples], gravity_vector),

    static_sample_count: static[:intensities].size,
    walk_cadence_sample_count: walk[:cadences].size,
    jump_sample_count: jump[:raw_samples].size,
  )
end

.max_vertical_acceleration(samples, gravity) ⇒ Object

Max upward acceleration (in g, gravity subtracted) observed across jump samples.



52
53
54
55
56
57
# File 'lib/road_to_rubykaigi/calibration_result.rb', line 52

def self.max_vertical_acceleration(samples, gravity)
  gravity_magnitude = Math.sqrt(gravity[0] ** 2 + gravity[1] ** 2 + gravity[2] ** 2)
  samples.map { |sample|
    (sample[0] * gravity[0] + sample[1] * gravity[1] + sample[2] * gravity[2]) / gravity_magnitude - gravity_magnitude
  }.max || 0.0
end

.mean_vector(samples) ⇒ Object

Averaged [x, y, z] of the resting accelerometer, used as the gravity reference.



47
48
49
# File 'lib/road_to_rubykaigi/calibration_result.rb', line 47

def self.mean_vector(samples)
  samples.transpose.map { |values| values.sum / values.size }
end

.median(values) ⇒ Object



41
42
43
44
# File 'lib/road_to_rubykaigi/calibration_result.rb', line 41

def self.median(values)
  sorted = values.sort
  sorted[sorted.size / 2] || 0.0
end

Instance Method Details

#saveObject



59
60
61
62
63
64
65
66
67
68
# File 'lib/road_to_rubykaigi/calibration_result.rb', line 59

def save
  Config.save_calibration(
    start_threshold: start_threshold.round(6),
    continuation_threshold: continuation_threshold.round(6),
    walk_cadence: walk_cadence.round(6),
    walk_intensity: walk_intensity.round(6),
    gravity_vector: gravity_vector.map { |value| value.round(6) },
    jump_v_max: jump_v_max.round(6),
  )
end