class PatronusFati::Presence

This class holds two hours worth of presence data at minutely resolution. Tests can be done to see whether or not whatever this is tracking was present at a specific time or during a specific time interval.

Attributes

current_presence[RW]
first_seen[RW]
last_presence[RW]
window_start[RW]

Public Class Methods

new() click to toggle source
# File lib/patronus_fati/presence.rb, line 34
def initialize
  self.current_presence = BitField.new(WINDOW_INTERVALS)
  self.last_presence = BitField.new(WINDOW_INTERVALS)
  self.window_start = current_window_start
end

Public Instance Methods

bit_for_time(reference_window, timestamp) click to toggle source

Translate a timestamp relative to the provided reference window into an appropriate bit within our bit field.

# File lib/patronus_fati/presence.rb, line 10
def bit_for_time(reference_window, timestamp)
  offset = timestamp - reference_window
  raise ArgumentError if offset < 0 || offset >= WINDOW_LENGTH
  (offset / INTERVAL_DURATION) + 1
end
current_bit_offset() click to toggle source

Get the bit representing our current interval within the window

# File lib/patronus_fati/presence.rb, line 17
def current_bit_offset
  bit_for_time(window_start, Time.now.to_i)
end
current_window_start() click to toggle source

Returns the unix timestamp of the beginning of the current window.

# File lib/patronus_fati/presence.rb, line 22
def current_window_start
  ts = Time.now.to_i
  ts - (ts % WINDOW_LENGTH)
end
dead?() click to toggle source

Returns true if we have no data points indicating we've seen the presence of this instance in the entirety of our time window.

# File lib/patronus_fati/presence.rb, line 29
def dead?
  rotate_presence
  current_presence.bits == 0 && last_presence.bits == 0
end
last_visible() click to toggle source

Provides the beginning of the last interval when the tracked object was seen. This could probably be optimized with a B tree search or the like but this is more than enough for now.

# File lib/patronus_fati/presence.rb, line 47
def last_visible
  rotate_presence

  return nil if dead?

  if (bit = current_presence.highest_bit_set)
    time_for_bit(window_start, bit)
  else
    time_for_bit(last_window_start, last_presence.highest_bit_set)
  end
end
last_window_start() click to toggle source
# File lib/patronus_fati/presence.rb, line 40
def last_window_start
  window_start - WINDOW_LENGTH
end
mark_visible() click to toggle source

Mark the current interval as having been seen in the presence field. Will handle rotation if the window has slipped.

# File lib/patronus_fati/presence.rb, line 61
def mark_visible
  rotate_presence

  set_first_seen unless first_seen
  self.current_presence.set_bit(current_bit_offset)
end
rotate_presence() click to toggle source

Should be called before reading or writing from/to the current_presence to ensure it is pointing at the appropriate bitfield. When we shift into a new bit window, this method will move the current window into the old one, and reset the current bit field.

# File lib/patronus_fati/presence.rb, line 72
def rotate_presence
  cws = current_window_start
  return if window_start == cws

  self.last_presence = current_presence
  self.window_start = cws
  self.current_presence = BitField.new(WINDOW_INTERVALS)
end
set_first_seen() click to toggle source

Set the time we first saw whatever we're tracking to be the beginning of the current interval. This prevents negative durations in the event we only see it once.

# File lib/patronus_fati/presence.rb, line 84
def set_first_seen
  cur_time = Time.now.to_i
  self.first_seen = cur_time - (cur_time % INTERVAL_DURATION)
end
time_for_bit(reference_window, bit) click to toggle source

Translate a bit into an absolute unix time relative to the reference window

# File lib/patronus_fati/presence.rb, line 91
def time_for_bit(reference_window, bit)
  raise ArgumentError if bit <= 0 || bit > WINDOW_INTERVALS
  reference_window + (INTERVAL_DURATION * (bit - 1))
end
visible_since?(unix_time) click to toggle source

Checks to see if the presence of the tracked object has been visible at all since the provided time. Currently this is dependent on visible_at? and can perform at most WINDOW_INTERVALS - 1 calls.

This could be significantly sped up by a direct bit field check against both the presence fields.

# File lib/patronus_fati/presence.rb, line 102
def visible_since?(unix_time)
  rotate_presence

  return false unless (lv = last_visible)
  unix_time <= lv
end
visible_time() click to toggle source

Returns the duration in seconds of how long the specific object was absolutely seen. One additional interval duration is added to this length as we consider to have seen the tracked object for the entire duration of the interval not the length from the start of one interval to the start of the last interval, which makes logical sense (1 bit set is 1 interval duration, not zero seconds).

# File lib/patronus_fati/presence.rb, line 115
def visible_time
  (last_visible + INTERVAL_DURATION) - first_seen if first_seen && last_visible
end