Added utility to compute phased loop offsets.

Change-Id: If9dfe61e20a8e93af39c1cfb2459eb5fbb1cbd3b
diff --git a/aos/common/util/phased_loop.h b/aos/common/util/phased_loop.h
index fbfe954..958bd01 100644
--- a/aos/common/util/phased_loop.h
+++ b/aos/common/util/phased_loop.h
@@ -27,6 +27,28 @@
     Reset();
   }
 
+  // Updates the offset and interval.
+  void set_interval_and_offset(const monotonic_clock::duration interval,
+                               const monotonic_clock::duration offset) {
+    interval_ = interval;
+    offset_ = offset;
+    CHECK_GE(offset_, monotonic_clock::duration(0));
+    CHECK_GT(interval_, monotonic_clock::duration(0));
+    CHECK_LT(offset_, interval_);
+  }
+
+  // Computes the offset given an interval and a time that we should trigger.
+  static monotonic_clock::duration OffsetFromIntervalAndTime(
+      const monotonic_clock::duration interval,
+      const monotonic_clock::time_point monotonic_trigger) {
+    CHECK_GT(interval, monotonic_clock::duration(0));
+    return monotonic_trigger.time_since_epoch() -
+           (monotonic_trigger.time_since_epoch() / interval) * interval +
+           ((monotonic_trigger.time_since_epoch() >= monotonic_clock::zero())
+                ? monotonic_clock::zero()
+                : interval);
+  }
+
   // Resets the count of skipped iterations.
   // Iterate(monotonic_now) will return 1 and set sleep_time() to something
   // within interval of monotonic_now.
@@ -54,7 +76,7 @@
   monotonic_clock::time_point sleep_time() const { return last_time_; }
 
  private:
-  const monotonic_clock::duration interval_, offset_;
+  monotonic_clock::duration interval_, offset_;
 
   // The time we most recently slept until.
   monotonic_clock::time_point last_time_ = monotonic_clock::epoch();
diff --git a/aos/common/util/phased_loop_test.cc b/aos/common/util/phased_loop_test.cc
index b013c2e..b9d6e52 100644
--- a/aos/common/util/phased_loop_test.cc
+++ b/aos/common/util/phased_loop_test.cc
@@ -150,6 +150,29 @@
   EXPECT_EQ(InMs(1), loop.sleep_time());
 }
 
+// Tests OffsetFromIntervalAndTime for various edge conditions.
+TEST_F(PhasedLoopTest, OffsetFromIntervalAndTimeTest) {
+  PhasedLoop loop(milliseconds(1000), milliseconds(300));
+
+  EXPECT_EQ(milliseconds(1),
+            loop.OffsetFromIntervalAndTime(milliseconds(1000), InMs(1001)));
+
+  EXPECT_EQ(milliseconds(0),
+            loop.OffsetFromIntervalAndTime(milliseconds(1000), InMs(1000)));
+
+  EXPECT_EQ(milliseconds(0),
+            loop.OffsetFromIntervalAndTime(milliseconds(1000), InMs(0)));
+
+  EXPECT_EQ(milliseconds(999),
+            loop.OffsetFromIntervalAndTime(milliseconds(1000), InMs(-1)));
+
+  EXPECT_EQ(milliseconds(7),
+            loop.OffsetFromIntervalAndTime(milliseconds(1000), InMs(19115007)));
+
+  EXPECT_EQ(milliseconds(7), loop.OffsetFromIntervalAndTime(milliseconds(1000),
+                                                            InMs(-19115993)));
+}
+
 // Tests that passing invalid values to the constructor dies correctly.
 TEST_F(PhasedLoopDeathTest, InvalidValues) {
   EXPECT_DEATH(PhasedLoop(milliseconds(1), milliseconds(2)),