class PostRunner::SleepCycle
A sleep cycle consists of several sleep phases. This class is used to gather and store the relevant data of a sleep cycle. Data is analzyed and stored with a one minute granularity. Time values are stored as minutes past the zero_idx_time.
Attributes
Public Class Methods
Create a new SleepCycle
record. @param zero_idx_time [Time] This is the time of the 0-th minute. All
time values are stored as minutes past this time.
@param start_idx
[Fixnum] Time when the sleep cycle starts. We may start
with an appromated value that gets fine tuned later on.
@param prev_cycle
[SleepCycle] A reference to the preceding sleep cycle
or nil if this is the first cycle of the analyzed period.
# File lib/postrunner/SleepCycle.rb, line 57 def initialize(zero_idx_time, start_idx, prev_cycle = nil) @zero_idx_time = zero_idx_time @start_idx = start_idx # These values will be determined later. @end_idx = nil # Every sleep cycle has at most one high/low heart rate transition and # one low/high transition. These variables store the time of these # transitions or nil if the transition does not exist. Every cycle must # have at least one of these transitions to be a valid cycle. @high_low_trans_idx = @low_high_trans_idx = nil @prev_cycle = prev_cycle # Register this cycle as successor of the previous cycle. prev_cycle.next_cycle = self if prev_cycle @next_cycle = nil # Array holding the sleep phases of this cycle @phases = [] # A hash with the total durations (in secods) of the various sleep # phases. @total_seconds = Hash.new(0) end
Public Instance Methods
Initially, we use the high/low heart rate transition to mark the end of the cycle. But it’s really the end of the REM phase that marks the end of a sleep cycle. If we find a REM phase, we use its end to adjust the sleep cycle boundaries. @param phases [Array] List of symbols that describe the sleep phase at
at the minute corresponding to the Array index.
# File lib/postrunner/SleepCycle.rb, line 102 def adjust_cycle_boundaries(phases) end_of_rem_phase_idx = nil @start_idx.upto(@end_idx) do |i| end_of_rem_phase_idx = i if phases[i] == :rem end if end_of_rem_phase_idx # We have found a REM phase. Adjust the end_idx of this cycle # accordingly. @end_idx = end_of_rem_phase_idx if @next_cycle # If we have a successor phase, we also adjust the start. @next_cycle.start_idx = end_of_rem_phase_idx + 1 end end end
Gather a list of SleepPhase
objects that describe the sequence of sleep phases in the provided Array. @param phases [Array] List of symbols that describe the sleep phase at
at the minute corresponding to the Array index.
# File lib/postrunner/SleepCycle.rb, line 122 def detect_phases(phases) @phases = [] current_phase = phases[0] current_phase_start = @start_idx @start_idx.upto(@end_idx) do |i| if (current_phase && current_phase != phases[i]) || i == @end_idx # We found a transition in the sequence. Create a SleepPhase object # that describes the prepvious segment and add it to the @phases # list. @phases << (p = SleepPhase.new(idx_to_time(current_phase_start), idx_to_time(i == @end_idx ? i + 1 : i), current_phase)) # Add the duration of the phase to the corresponding sum in the # @total_seconds Hash. @total_seconds[current_phase] += p.duration # Update the variables that track the start and kind of the # currently read phase. current_phase_start = i current_phase = phases[i] end end end
The start time of the cycle as Time object @return [Time]
# File lib/postrunner/SleepCycle.rb, line 80 def from_time idx_to_time(@start_idx) end
Check if the cycle has a deep sleep phase. @return [Boolean] True of one of the phases is NREM3 phase. False
otherwise.
# File lib/postrunner/SleepCycle.rb, line 160 def has_deep_sleep_phase? # A real deep sleep phase must be at least 10 minutes long. @phases.each do |p| return true if p.phase == :nrem3 && p.duration > 10 * 60 end false end
Check if any of the previous cycles that are directly attached have a deep sleep cycle. @return [Boolean] True if it has a leading sleep cycle.
# File lib/postrunner/SleepCycle.rb, line 172 def has_leading_deep_sleep_phase? return false if @prev_cycle.nil? || @start_idx != @prev_cycle.end_idx + 1 @prev_cycle.has_deep_sleep_phase? || @prev_cycle.has_leading_deep_sleep_phase? end
Check if any of the trailing cycles that are directly attached have a deep sleep cycle. @return [Boolean] True if it has a trailing sleep cycle.
# File lib/postrunner/SleepCycle.rb, line 182 def has_trailing_deep_sleep_phase? return false if @next_cycle.nil? || @end_idx + 1 != @next_cycle.start_idx @next_cycle.has_deep_sleep_phase? || @next_cycle.has_trailing_deep_sleep_phase? end
Check if this cycle is really a sleep cycle or not. A sleep cycle must have at least one deep sleep phase or must be part of a directly attached series of cycles that contain a deep sleep phase. @return [Boolean] True if not a sleep cycle, false otherwise.
# File lib/postrunner/SleepCycle.rb, line 151 def is_wake_cycle? !has_deep_sleep_phase? && !has_leading_deep_sleep_phase? && !has_trailing_deep_sleep_phase? end
The end time of the cycle as Time object. @return [Time]
# File lib/postrunner/SleepCycle.rb, line 86 def to_time idx_to_time(@end_idx + 1) end
Remove this cycle from the cycle chain.
# File lib/postrunner/SleepCycle.rb, line 91 def unlink @prev_cycle.next_cycle = @next_cycle if @prev_cycle @next_cycle.prev_cycle = @prev_cycle if @next_cycle end
Private Instance Methods
# File lib/postrunner/SleepCycle.rb, line 191 def idx_to_time(idx) return nil unless idx @zero_idx_time + 60 * idx end