Add remote timestamps and queue_index to context

This gives us the knobs to expose the remote timestamps and queue_index
to anything receiving the events.  The first use case is the logger.  It
can now log forwarded entries *without* having to make the
message_gateway responsible for logging this data.

Change-Id: Ie34dd040d270f4fa90ecd6e463069e1adca1818a
diff --git a/aos/controls/control_loop-tmpl.h b/aos/controls/control_loop-tmpl.h
index f937bb4..349a55c 100644
--- a/aos/controls/control_loop-tmpl.h
+++ b/aos/controls/control_loop-tmpl.h
@@ -58,7 +58,7 @@
       // If the driver's station reports being disabled, we're probably not
       // actually going to send motor values regardless of what the FPGA
       // reports.
-      last_pwm_sent_ = robot_state_fetcher_.context().monotonic_sent_time;
+      last_pwm_sent_ = robot_state_fetcher_.context().monotonic_event_time;
     }
   }
 
diff --git a/aos/events/event_loop.cc b/aos/events/event_loop.cc
index af5a3e5..ee3365d 100644
--- a/aos/events/event_loop.cc
+++ b/aos/events/event_loop.cc
@@ -23,8 +23,10 @@
     : event_loop_(event_loop),
       channel_(channel),
       timing_(event_loop_->ChannelIndex(channel)) {
-  context_.monotonic_sent_time = monotonic_clock::min_time;
-  context_.realtime_sent_time = realtime_clock::min_time;
+  context_.monotonic_event_time = monotonic_clock::min_time;
+  context_.monotonic_remote_time = monotonic_clock::min_time;
+  context_.realtime_event_time = realtime_clock::min_time;
+  context_.realtime_remote_time = realtime_clock::min_time;
   context_.queue_index = 0xffffffff;
   context_.size = 0;
   context_.data = nullptr;
diff --git a/aos/events/event_loop.h b/aos/events/event_loop.h
index bd0c4d4..5edecf1 100644
--- a/aos/events/event_loop.h
+++ b/aos/events/event_loop.h
@@ -28,14 +28,25 @@
 // Struct available on Watchers, Fetchers, Timers, and PhasedLoops with context
 // about the current message.
 struct Context {
-  // Time that the message was sent, or the timer was triggered.
-  monotonic_clock::time_point monotonic_sent_time;
-  // Realtime the message was sent.  This is set to min_time for Timers and
-  // PhasedLoops.
-  realtime_clock::time_point realtime_sent_time;
+  // Time that the message was sent on this node, or the timer was triggered.
+  monotonic_clock::time_point monotonic_event_time;
+  // Realtime the message was sent on this node.  This is set to min_time for
+  // Timers and PhasedLoops.
+  realtime_clock::time_point realtime_event_time;
+
+  // For a single-node configuration, these two are identical to *_event_time.
+  // In a multinode configuration, these are the times that the message was
+  // sent on the original node.
+  monotonic_clock::time_point monotonic_remote_time;
+  realtime_clock::time_point realtime_remote_time;
+
   // The rest are only valid for Watchers and Fetchers.
   // Index in the queue.
   uint32_t queue_index;
+  // Index into the remote queue.  Useful to determine if data was lost.  In a
+  // single-node configuration, this will match queue_index.
+  uint32_t remote_queue_index;
+
   // Size of the data sent.
   size_t size;
   // Pointer to the data.
@@ -94,23 +105,60 @@
   // Sends a message without copying it.  The users starts by copying up to
   // size() bytes into the data backed by data().  They then call Send to send.
   // Returns true on a successful send.
+  // If provided, monotonic_remote_time, realtime_remote_time, and
+  // remote_queue_index are attached to the message and are available in the
+  // context on the read side.  If they are not populated, the read side will
+  // get the sent times instead.
   virtual void *data() = 0;
   virtual size_t size() = 0;
-  bool Send(size_t size);
+  bool Send(size_t size,
+            aos::monotonic_clock::time_point monotonic_remote_time =
+                aos::monotonic_clock::min_time,
+            aos::realtime_clock::time_point realtime_remote_time =
+                aos::realtime_clock::min_time,
+            uint32_t remote_queue_index = 0xffffffffu);
 
   // Sends a single block of data by copying it.
-  bool Send(const void *data, size_t size);
+  // The remote arguments have the same meaning as in Send above.
+  bool Send(const void *data, size_t size,
+            aos::monotonic_clock::time_point monotonic_remote_time =
+                aos::monotonic_clock::min_time,
+            aos::realtime_clock::time_point realtime_remote_time =
+                aos::realtime_clock::min_time,
+            uint32_t remote_queue_index = 0xffffffffu);
 
   const Channel *channel() const { return channel_; }
 
+  // Returns the time_points that the last message was sent at.
+  aos::monotonic_clock::time_point monotonic_sent_time() const {
+    return monotonic_sent_time_;
+  }
+  aos::realtime_clock::time_point realtime_sent_time() const {
+    return realtime_sent_time_;
+  }
+  // Returns the queue index that this was sent with.
+  uint32_t sent_queue_index() const { return sent_queue_index_; }
+
  protected:
   EventLoop *event_loop() { return event_loop_; }
 
+  aos::monotonic_clock::time_point monotonic_sent_time_ =
+      aos::monotonic_clock::min_time;
+  aos::realtime_clock::time_point realtime_sent_time_ =
+      aos::realtime_clock::min_time;
+  uint32_t sent_queue_index_ = 0xffffffff;
+
  private:
   friend class EventLoop;
 
-  virtual bool DoSend(const void *data, size_t size) = 0;
-  virtual bool DoSend(size_t size) = 0;
+  virtual bool DoSend(const void *data, size_t size,
+                      aos::monotonic_clock::time_point monotonic_remote_time,
+                      aos::realtime_clock::time_point realtime_remote_time,
+                      uint32_t remote_queue_index) = 0;
+  virtual bool DoSend(size_t size,
+                      aos::monotonic_clock::time_point monotonic_remote_time,
+                      aos::realtime_clock::time_point realtime_remote_time,
+                      uint32_t remote_queue_index) = 0;
 
   EventLoop *event_loop_;
   const Channel *channel_;
diff --git a/aos/events/event_loop_param_test.cc b/aos/events/event_loop_param_test.cc
index c1256f3..e1e05a2 100644
--- a/aos/events/event_loop_param_test.cc
+++ b/aos/events/event_loop_param_test.cc
@@ -59,8 +59,10 @@
   EXPECT_FALSE(fetcher.Fetch());
   EXPECT_EQ(fetcher.get(), nullptr);
 
-  EXPECT_EQ(fetcher.context().monotonic_sent_time, monotonic_clock::min_time);
-  EXPECT_EQ(fetcher.context().realtime_sent_time, realtime_clock::min_time);
+  EXPECT_EQ(fetcher.context().monotonic_event_time, monotonic_clock::min_time);
+  EXPECT_EQ(fetcher.context().monotonic_remote_time, monotonic_clock::min_time);
+  EXPECT_EQ(fetcher.context().realtime_event_time, realtime_clock::min_time);
+  EXPECT_EQ(fetcher.context().realtime_remote_time, realtime_clock::min_time);
   EXPECT_EQ(fetcher.context().queue_index, 0xffffffffu);
   EXPECT_EQ(fetcher.context().size, 0u);
   EXPECT_EQ(fetcher.context().data, nullptr);
@@ -76,14 +78,17 @@
 
   const chrono::milliseconds kEpsilon(100);
 
-  EXPECT_GE(fetcher.context().monotonic_sent_time,
-            loop2->monotonic_now() - kEpsilon);
-  EXPECT_LE(fetcher.context().monotonic_sent_time,
-            loop2->monotonic_now() + kEpsilon);
-  EXPECT_GE(fetcher.context().realtime_sent_time,
-            loop2->realtime_now() - kEpsilon);
-  EXPECT_LE(fetcher.context().realtime_sent_time,
-            loop2->realtime_now() + kEpsilon);
+  const aos::monotonic_clock::time_point monotonic_now = loop2->monotonic_now();
+  const aos::realtime_clock::time_point realtime_now = loop2->realtime_now();
+  EXPECT_EQ(fetcher.context().monotonic_event_time,
+            fetcher.context().monotonic_remote_time);
+  EXPECT_EQ(fetcher.context().realtime_event_time,
+            fetcher.context().realtime_remote_time);
+
+  EXPECT_GE(fetcher.context().monotonic_event_time, monotonic_now - kEpsilon);
+  EXPECT_LE(fetcher.context().monotonic_event_time, monotonic_now + kEpsilon);
+  EXPECT_GE(fetcher.context().realtime_event_time, realtime_now - kEpsilon);
+  EXPECT_LE(fetcher.context().realtime_event_time, realtime_now + kEpsilon);
   EXPECT_EQ(fetcher.context().queue_index, 0x0u);
   EXPECT_EQ(fetcher.context().size, 20u);
   EXPECT_NE(fetcher.context().data, nullptr);
@@ -512,12 +517,14 @@
 
   auto test_timer = loop->AddTimer([this, &times, &expected_times, &loop]() {
     times.push_back(loop->monotonic_now());
-    EXPECT_EQ(loop->context().realtime_sent_time, realtime_clock::min_time);
+    EXPECT_EQ(loop->context().monotonic_remote_time, monotonic_clock::min_time);
+    EXPECT_EQ(loop->context().realtime_event_time, realtime_clock::min_time);
+    EXPECT_EQ(loop->context().realtime_remote_time, realtime_clock::min_time);
     EXPECT_EQ(loop->context().queue_index, 0xffffffffu);
     EXPECT_EQ(loop->context().size, 0u);
     EXPECT_EQ(loop->context().data, nullptr);
 
-    expected_times.push_back(loop->context().monotonic_sent_time);
+    expected_times.push_back(loop->context().monotonic_event_time);
     if (times.size() == kCount) {
       this->Exit();
     }
@@ -684,7 +691,7 @@
 TEST_P(AbstractEventLoopTest, MessageSendTime) {
   auto loop1 = MakePrimary();
   auto loop2 = Make();
-  auto sender = loop1->MakeSender<TestMessage>("/test");
+  auto sender = loop2->MakeSender<TestMessage>("/test");
   auto fetcher = loop2->MakeFetcher<TestMessage>("/test");
 
   auto test_timer = loop1->AddTimer([&sender]() {
@@ -694,12 +701,32 @@
     ASSERT_TRUE(msg.Send(builder.Finish()));
   });
 
-  loop2->MakeWatcher("/test", [&loop2](const TestMessage &msg) {
-    // Confirm that the data pointer makes sense from a watcher.
-    EXPECT_GT(&msg, loop2->context().data);
+  bool triggered = false;
+  loop1->MakeWatcher("/test", [&triggered, &loop1](const TestMessage &msg) {
+    // Confirm that the data pointer makes sense from a watcher, and all the
+    // timestamps look right.
+    EXPECT_GT(&msg, loop1->context().data);
+    EXPECT_EQ(loop1->context().monotonic_remote_time,
+              loop1->context().monotonic_event_time);
+    EXPECT_EQ(loop1->context().realtime_remote_time,
+              loop1->context().realtime_event_time);
+
+    const aos::monotonic_clock::time_point monotonic_now =
+        loop1->monotonic_now();
+    const aos::realtime_clock::time_point realtime_now =
+        loop1->realtime_now();
+
+    EXPECT_LE(loop1->context().monotonic_event_time, monotonic_now);
+    EXPECT_LE(loop1->context().realtime_event_time, realtime_now);
+    EXPECT_GE(loop1->context().monotonic_event_time + chrono::milliseconds(500),
+              monotonic_now);
+    EXPECT_GE(loop1->context().realtime_event_time + chrono::milliseconds(500),
+              realtime_now);
+
     EXPECT_LT(&msg, reinterpret_cast<void *>(
-                        reinterpret_cast<char *>(loop2->context().data) +
-                        loop2->context().size));
+                        reinterpret_cast<char *>(loop1->context().data) +
+                        loop1->context().size));
+    triggered = true;
   });
 
   test_timer->Setup(loop1->monotonic_now() + ::std::chrono::seconds(1));
@@ -707,18 +734,25 @@
   EndEventLoop(loop1.get(), ::std::chrono::seconds(2));
   Run();
 
+  EXPECT_TRUE(triggered);
+
   EXPECT_TRUE(fetcher.Fetch());
 
   monotonic_clock::duration monotonic_time_offset =
-      fetcher.context().monotonic_sent_time -
+      fetcher.context().monotonic_event_time -
       (loop1->monotonic_now() - ::std::chrono::seconds(1));
   realtime_clock::duration realtime_time_offset =
-      fetcher.context().realtime_sent_time -
+      fetcher.context().realtime_event_time -
       (loop1->realtime_now() - ::std::chrono::seconds(1));
 
+  EXPECT_EQ(fetcher.context().realtime_event_time,
+            fetcher.context().realtime_remote_time);
+  EXPECT_EQ(fetcher.context().monotonic_event_time,
+            fetcher.context().monotonic_remote_time);
+
   EXPECT_TRUE(monotonic_time_offset > ::std::chrono::milliseconds(-500))
       << ": Got "
-      << fetcher.context().monotonic_sent_time.time_since_epoch().count()
+      << fetcher.context().monotonic_event_time.time_since_epoch().count()
       << " expected " << loop1->monotonic_now().time_since_epoch().count();
   // Confirm that the data pointer makes sense.
   EXPECT_GT(fetcher.get(), fetcher.context().data);
@@ -728,16 +762,16 @@
                 fetcher.context().size));
   EXPECT_TRUE(monotonic_time_offset < ::std::chrono::milliseconds(500))
       << ": Got "
-      << fetcher.context().monotonic_sent_time.time_since_epoch().count()
+      << fetcher.context().monotonic_event_time.time_since_epoch().count()
       << " expected " << loop1->monotonic_now().time_since_epoch().count();
 
   EXPECT_TRUE(realtime_time_offset > ::std::chrono::milliseconds(-500))
       << ": Got "
-      << fetcher.context().realtime_sent_time.time_since_epoch().count()
+      << fetcher.context().realtime_event_time.time_since_epoch().count()
       << " expected " << loop1->realtime_now().time_since_epoch().count();
   EXPECT_TRUE(realtime_time_offset < ::std::chrono::milliseconds(500))
       << ": Got "
-      << fetcher.context().realtime_sent_time.time_since_epoch().count()
+      << fetcher.context().realtime_event_time.time_since_epoch().count()
       << " expected " << loop1->realtime_now().time_since_epoch().count();
 }
 
@@ -768,9 +802,13 @@
           [&times, &expected_times, &loop1, this](int count) {
             EXPECT_EQ(count, 1);
             times.push_back(loop1->monotonic_now());
-            expected_times.push_back(loop1->context().monotonic_sent_time);
+            expected_times.push_back(loop1->context().monotonic_event_time);
 
-            EXPECT_EQ(loop1->context().realtime_sent_time,
+            EXPECT_EQ(loop1->context().monotonic_remote_time,
+                      monotonic_clock::min_time);
+            EXPECT_EQ(loop1->context().realtime_event_time,
+                      realtime_clock::min_time);
+            EXPECT_EQ(loop1->context().realtime_remote_time,
                       realtime_clock::min_time);
             EXPECT_EQ(loop1->context().queue_index, 0xffffffffu);
             EXPECT_EQ(loop1->context().size, 0u);
@@ -1145,6 +1183,89 @@
   EXPECT_TRUE(happened);
 }
 
+// Tests that a raw watcher and raw fetcher can receive messages from a raw
+// sender with remote times filled out.
+TEST_P(AbstractEventLoopTest, RawRemoteTimes) {
+  auto loop1 = Make();
+  auto loop2 = MakePrimary();
+  auto loop3 = Make();
+
+  const std::string kData("971 is the best");
+
+  const aos::monotonic_clock::time_point monotonic_remote_time =
+      aos::monotonic_clock::time_point(chrono::seconds(1501));
+  const aos::realtime_clock::time_point realtime_remote_time =
+      aos::realtime_clock::time_point(chrono::seconds(3132));
+
+  std::unique_ptr<aos::RawSender> sender =
+      loop1->MakeRawSender(loop1->configuration()->channels()->Get(1));
+
+  std::unique_ptr<aos::RawFetcher> fetcher =
+      loop3->MakeRawFetcher(loop3->configuration()->channels()->Get(1));
+
+  loop2->OnRun([&]() {
+    EXPECT_TRUE(sender->Send(kData.data(), kData.size(), monotonic_remote_time,
+                             realtime_remote_time));
+  });
+
+  bool happened = false;
+  loop2->MakeRawWatcher(
+      loop2->configuration()->channels()->Get(1),
+      [this, monotonic_remote_time, realtime_remote_time, &fetcher, &happened](
+          const Context &context, const void * /*message*/) {
+        happened = true;
+        EXPECT_EQ(monotonic_remote_time, context.monotonic_remote_time);
+        EXPECT_EQ(realtime_remote_time, context.realtime_remote_time);
+
+        ASSERT_TRUE(fetcher->Fetch());
+        EXPECT_EQ(monotonic_remote_time,
+                  fetcher->context().monotonic_remote_time);
+        EXPECT_EQ(realtime_remote_time,
+                  fetcher->context().realtime_remote_time);
+
+        this->Exit();
+      });
+
+  EXPECT_FALSE(happened);
+  Run();
+  EXPECT_TRUE(happened);
+}
+
+// Tests that a raw sender fills out sent data.
+TEST_P(AbstractEventLoopTest, RawSenderSentData) {
+  auto loop1 = MakePrimary();
+
+  const std::string kData("971 is the best");
+
+  std::unique_ptr<aos::RawSender> sender =
+      loop1->MakeRawSender(loop1->configuration()->channels()->Get(1));
+
+  const aos::monotonic_clock::time_point monotonic_now =
+      loop1->monotonic_now();
+  const aos::realtime_clock::time_point realtime_now =
+      loop1->realtime_now();
+
+  EXPECT_TRUE(sender->Send(kData.data(), kData.size()));
+
+  EXPECT_GE(sender->monotonic_sent_time(), monotonic_now);
+  EXPECT_LE(sender->monotonic_sent_time(),
+            monotonic_now + chrono::milliseconds(100));
+  EXPECT_GE(sender->realtime_sent_time(), realtime_now);
+  EXPECT_LE(sender->realtime_sent_time(),
+            realtime_now + chrono::milliseconds(100));
+  EXPECT_EQ(sender->sent_queue_index(), 0u);
+
+  EXPECT_TRUE(sender->Send(kData.data(), kData.size()));
+
+  EXPECT_GE(sender->monotonic_sent_time(), monotonic_now);
+  EXPECT_LE(sender->monotonic_sent_time(),
+            monotonic_now + chrono::milliseconds(100));
+  EXPECT_GE(sender->realtime_sent_time(), realtime_now);
+  EXPECT_LE(sender->realtime_sent_time(),
+            realtime_now + chrono::milliseconds(100));
+  EXPECT_EQ(sender->sent_queue_index(), 1u);
+}
+
 // Tests that not setting up nodes results in no node.
 TEST_P(AbstractEventLoopTest, NoNode) {
   auto loop1 = Make();
diff --git a/aos/events/event_loop_tmpl.h b/aos/events/event_loop_tmpl.h
index 0b485c6..ef223a9 100644
--- a/aos/events/event_loop_tmpl.h
+++ b/aos/events/event_loop_tmpl.h
@@ -59,7 +59,7 @@
     const monotonic_clock::time_point monotonic_time = result.second;
     const float latency =
         std::chrono::duration_cast<std::chrono::duration<float>>(
-            monotonic_time - context_.monotonic_sent_time)
+            monotonic_time - context_.monotonic_event_time)
             .count();
     timing_.latency.Add(latency);
     return true;
@@ -74,7 +74,7 @@
     const monotonic_clock::time_point monotonic_time = result.second;
     const float latency =
         std::chrono::duration_cast<std::chrono::duration<float>>(
-            monotonic_time - context_.monotonic_sent_time)
+            monotonic_time - context_.monotonic_event_time)
             .count();
     timing_.latency.Add(latency);
     return true;
@@ -82,8 +82,12 @@
   return false;
 }
 
-inline bool RawSender::Send(size_t size) {
-  if (DoSend(size)) {
+inline bool RawSender::Send(
+    size_t size, aos::monotonic_clock::time_point monotonic_remote_time,
+    aos::realtime_clock::time_point realtime_remote_time,
+    uint32_t remote_queue_index) {
+  if (DoSend(size, monotonic_remote_time, realtime_remote_time,
+             remote_queue_index)) {
     timing_.size.Add(size);
     timing_.sender->mutate_count(timing_.sender->count() + 1);
     return true;
@@ -91,8 +95,13 @@
   return false;
 }
 
-inline bool RawSender::Send(const void *data, size_t size) {
-  if (DoSend(data, size)) {
+inline bool RawSender::Send(
+    const void *data, size_t size,
+    aos::monotonic_clock::time_point monotonic_remote_time,
+    aos::realtime_clock::time_point realtime_remote_time,
+    uint32_t remote_queue_index) {
+  if (DoSend(data, size, monotonic_remote_time, realtime_remote_time,
+             remote_queue_index)) {
     timing_.size.Add(size);
     timing_.sender->mutate_count(timing_.sender->count() + 1);
     return true;
@@ -106,8 +115,10 @@
   CHECK_NOTNULL(timing_.timer);
   const monotonic_clock::time_point monotonic_start_time = get_time();
 
-  event_loop_->context_.monotonic_sent_time = event_time;
-  event_loop_->context_.realtime_sent_time = realtime_clock::min_time;
+  event_loop_->context_.monotonic_event_time = event_time;
+  event_loop_->context_.monotonic_remote_time = monotonic_clock::min_time;
+  event_loop_->context_.realtime_remote_time =
+      event_loop_->context_.realtime_event_time = realtime_clock::min_time;
   event_loop_->context_.queue_index = 0xffffffffu;
   event_loop_->context_.size = 0;
   event_loop_->context_.data = nullptr;
@@ -138,8 +149,10 @@
   const monotonic_clock::time_point monotonic_start_time = get_time();
 
   // Update the context to hold the desired wakeup time.
-  event_loop_->context_.monotonic_sent_time = phased_loop_.sleep_time();
-  event_loop_->context_.realtime_sent_time = realtime_clock::min_time;
+  event_loop_->context_.monotonic_event_time = phased_loop_.sleep_time();
+  event_loop_->context_.monotonic_remote_time = monotonic_clock::min_time;
+  event_loop_->context_.realtime_remote_time =
+      event_loop_->context_.realtime_event_time = realtime_clock::min_time;
   event_loop_->context_.queue_index = 0xffffffffu;
   event_loop_->context_.size = 0;
   event_loop_->context_.data = nullptr;
@@ -150,7 +163,7 @@
   {
     const float start_latency =
         std::chrono::duration_cast<std::chrono::duration<float>>(
-            monotonic_start_time - event_loop_->context_.monotonic_sent_time)
+            monotonic_start_time - event_loop_->context_.monotonic_event_time)
             .count();
     timing_.wakeup_latency.Add(start_latency);
   }
@@ -193,7 +206,7 @@
     {
       const float start_latency =
           std::chrono::duration_cast<std::chrono::duration<float>>(
-              monotonic_start_time - context.monotonic_sent_time)
+              monotonic_start_time - context.monotonic_event_time)
               .count();
       wakeup_latency_.Add(start_latency);
     }
diff --git a/aos/events/logging/log_cat.cc b/aos/events/logging/log_cat.cc
index c146945..9c75076 100644
--- a/aos/events/logging/log_cat.cc
+++ b/aos/events/logging/log_cat.cc
@@ -62,7 +62,7 @@
             // replaying it.
             std::cout << channel->name()->c_str() << ' '
                       << channel->type()->c_str() << " at "
-                      << context.monotonic_sent_time << ": "
+                      << context.monotonic_event_time << ": "
                       << aos::FlatbufferToJson(
                              channel->schema(),
                              static_cast<const uint8_t *>(message))
diff --git a/aos/events/logging/logger.cc b/aos/events/logging/logger.cc
index 01f75ec..e38c476 100644
--- a/aos/events/logging/logger.cc
+++ b/aos/events/logging/logger.cc
@@ -164,9 +164,9 @@
   MessageHeader::Builder message_header_builder(*fbb);
   message_header_builder.add_channel_index(channel_index);
   message_header_builder.add_monotonic_sent_time(
-      context.monotonic_sent_time.time_since_epoch().count());
+      context.monotonic_event_time.time_since_epoch().count());
   message_header_builder.add_realtime_sent_time(
-      context.realtime_sent_time.time_since_epoch().count());
+      context.realtime_event_time.time_since_epoch().count());
 
   message_header_builder.add_queue_index(context.queue_index);
 
@@ -212,7 +212,7 @@
         CHECK(!f.written);
 
         // TODO(james): Write tests to exercise this logic.
-        if (f.fetcher->context().monotonic_sent_time <
+        if (f.fetcher->context().monotonic_event_time <
             last_synchronized_time_) {
           // Write!
           flatbuffers::FlatBufferBuilder fbb(f.fetcher->context().size +
@@ -410,7 +410,7 @@
     std::pair<monotonic_clock::time_point, int> oldest_channel_index =
         PopOldestChannel();
     const monotonic_clock::time_point monotonic_now =
-        event_loop_->context().monotonic_sent_time;
+        event_loop_->context().monotonic_event_time;
     CHECK(monotonic_now == oldest_channel_index.first)
         << ": Now " << monotonic_now.time_since_epoch().count()
         << " trying to send "
diff --git a/aos/events/shm_event_loop.cc b/aos/events/shm_event_loop.cc
index 0f933f9..0ca713f 100644
--- a/aos/events/shm_event_loop.cc
+++ b/aos/events/shm_event_loop.cc
@@ -35,7 +35,7 @@
 }
 std::string ShmPath(const Channel *channel) {
   CHECK(channel->has_type());
-  return ShmFolder(channel) + channel->type()->str() + ".v0";
+  return ShmFolder(channel) + channel->type()->str() + ".v1";
 }
 
 class MMapedQueue {
@@ -193,11 +193,21 @@
     // TODO(austin): Get behind and make sure it dies both here and with
     // Fetch.
     ipc_lib::LocklessQueue::ReadResult read_result = lockless_queue_.Read(
-        actual_queue_index_.index(), &context_.monotonic_sent_time,
-        &context_.realtime_sent_time, &context_.size,
-        reinterpret_cast<char *>(data_storage_.get()));
+        actual_queue_index_.index(), &context_.monotonic_event_time,
+        &context_.realtime_event_time, &context_.monotonic_remote_time,
+        &context_.realtime_remote_time, &context_.remote_queue_index,
+        &context_.size, reinterpret_cast<char *>(data_storage_.get()));
     if (read_result == ipc_lib::LocklessQueue::ReadResult::GOOD) {
       context_.queue_index = actual_queue_index_.index();
+      if (context_.remote_queue_index == 0xffffffffu) {
+        context_.remote_queue_index = context_.queue_index;
+      }
+      if (context_.monotonic_remote_time == aos::monotonic_clock::min_time) {
+        context_.monotonic_remote_time = context_.monotonic_event_time;
+      }
+      if (context_.realtime_remote_time == aos::realtime_clock::min_time) {
+        context_.realtime_remote_time = context_.realtime_event_time;
+      }
       context_.data = reinterpret_cast<char *>(data_storage_.get()) +
                       lockless_queue_.message_data_size() - context_.size;
       actual_queue_index_ = actual_queue_index_.Increment();
@@ -231,12 +241,22 @@
       return false;
     }
 
-    ipc_lib::LocklessQueue::ReadResult read_result =
-        lockless_queue_.Read(queue_index.index(), &context_.monotonic_sent_time,
-                             &context_.realtime_sent_time, &context_.size,
-                             reinterpret_cast<char *>(data_storage_.get()));
+    ipc_lib::LocklessQueue::ReadResult read_result = lockless_queue_.Read(
+        queue_index.index(), &context_.monotonic_event_time,
+        &context_.realtime_event_time, &context_.monotonic_remote_time,
+        &context_.realtime_remote_time, &context_.remote_queue_index,
+        &context_.size, reinterpret_cast<char *>(data_storage_.get()));
     if (read_result == ipc_lib::LocklessQueue::ReadResult::GOOD) {
       context_.queue_index = queue_index.index();
+      if (context_.remote_queue_index == 0xffffffffu) {
+        context_.remote_queue_index = context_.queue_index;
+      }
+      if (context_.monotonic_remote_time == aos::monotonic_clock::min_time) {
+        context_.monotonic_remote_time = context_.monotonic_event_time;
+      }
+      if (context_.realtime_remote_time == aos::realtime_clock::min_time) {
+        context_.realtime_remote_time = context_.realtime_event_time;
+      }
       context_.data = reinterpret_cast<char *>(data_storage_.get()) +
                       lockless_queue_.message_data_size() - context_.size;
       actual_queue_index_ = queue_index.Increment();
@@ -326,14 +346,25 @@
 
   void *data() override { return lockless_queue_sender_.Data(); }
   size_t size() override { return lockless_queue_sender_.size(); }
-  bool DoSend(size_t length) override {
-    lockless_queue_sender_.Send(length);
+  bool DoSend(size_t length,
+              aos::monotonic_clock::time_point monotonic_remote_time,
+              aos::realtime_clock::time_point realtime_remote_time,
+              uint32_t remote_queue_index) override {
+    lockless_queue_sender_.Send(
+        length, monotonic_remote_time, realtime_remote_time, remote_queue_index,
+        &monotonic_sent_time_, &realtime_sent_time_, &sent_queue_index_);
     lockless_queue_.Wakeup(event_loop()->priority());
     return true;
   }
 
-  bool DoSend(const void *msg, size_t length) override {
-    lockless_queue_sender_.Send(reinterpret_cast<const char *>(msg), length);
+  bool DoSend(const void *msg, size_t length,
+              aos::monotonic_clock::time_point monotonic_remote_time,
+              aos::realtime_clock::time_point realtime_remote_time,
+              uint32_t remote_queue_index) override {
+    lockless_queue_sender_.Send(reinterpret_cast<const char *>(msg), length,
+                                monotonic_remote_time, realtime_remote_time,
+                                remote_queue_index, &monotonic_sent_time_,
+                                &realtime_sent_time_, &sent_queue_index_);
     lockless_queue_.Wakeup(event_loop()->priority());
     // TODO(austin): Return an error if we send too fast.
     return true;
@@ -370,7 +401,7 @@
 
       if (has_new_data_) {
         event_.set_event_time(
-            simple_shm_fetcher_.context().monotonic_sent_time);
+            simple_shm_fetcher_.context().monotonic_event_time);
         event_loop_->AddEvent(&event_);
       }
     }
diff --git a/aos/events/shm_event_loop_test.cc b/aos/events/shm_event_loop_test.cc
index f2c730b..d33f496 100644
--- a/aos/events/shm_event_loop_test.cc
+++ b/aos/events/shm_event_loop_test.cc
@@ -27,11 +27,11 @@
     }
 
     // Clean up anything left there before.
-    unlink((FLAGS_shm_base + "/test/aos.TestMessage.v0").c_str());
-    unlink((FLAGS_shm_base + "/test1/aos.TestMessage.v0").c_str());
-    unlink((FLAGS_shm_base + "/test2/aos.TestMessage.v0").c_str());
-    unlink((FLAGS_shm_base + "/test2/aos.TestMessage.v0").c_str());
-    unlink((FLAGS_shm_base + "/aos/aos.timing.Report.v0").c_str());
+    unlink((FLAGS_shm_base + "/test/aos.TestMessage.v1").c_str());
+    unlink((FLAGS_shm_base + "/test1/aos.TestMessage.v1").c_str());
+    unlink((FLAGS_shm_base + "/test2/aos.TestMessage.v1").c_str());
+    unlink((FLAGS_shm_base + "/test2/aos.TestMessage.v1").c_str());
+    unlink((FLAGS_shm_base + "/aos/aos.timing.Report.v1").c_str());
   }
 
   ~ShmEventLoopTestFactory() { FLAGS_override_hostname = ""; }
diff --git a/aos/events/simulated_event_loop.cc b/aos/events/simulated_event_loop.cc
index cd04c9f..578e151 100644
--- a/aos/events/simulated_event_loop.cc
+++ b/aos/events/simulated_event_loop.cc
@@ -86,8 +86,9 @@
     watchers_.erase(std::find(watchers_.begin(), watchers_.end(), watcher));
   }
 
-  // Sends the message to all the connected receivers and fetchers.
-  void Send(std::shared_ptr<SimulatedMessage> message);
+  // Sends the message to all the connected receivers and fetchers.  Returns the
+  // sent queue index.
+  uint32_t Send(std::shared_ptr<SimulatedMessage> message);
 
   // Unregisters a fetcher.
   void UnregisterFetcher(SimulatedFetcher *fetcher);
@@ -150,15 +151,23 @@
 
   size_t size() override { return simulated_channel_->max_size(); }
 
-  bool DoSend(size_t length) override {
+  bool DoSend(size_t length,
+              aos::monotonic_clock::time_point monotonic_remote_time,
+              aos::realtime_clock::time_point realtime_remote_time,
+              uint32_t remote_queue_index) override {
     CHECK_LE(length, size()) << ": Attempting to send too big a message.";
-    message_->context.monotonic_sent_time = event_loop_->monotonic_now();
-    message_->context.realtime_sent_time = event_loop_->realtime_now();
+    message_->context.monotonic_event_time = event_loop_->monotonic_now();
+    message_->context.monotonic_remote_time = monotonic_remote_time;
+    message_->context.remote_queue_index = remote_queue_index;
+    message_->context.realtime_event_time = event_loop_->realtime_now();
+    message_->context.realtime_remote_time = realtime_remote_time;
     CHECK_LE(length, message_->context.size);
     message_->context.size = length;
 
     // TODO(austin): Track sending too fast.
-    simulated_channel_->Send(message_);
+    sent_queue_index_ = simulated_channel_->Send(message_);
+    monotonic_sent_time_ = event_loop_->monotonic_now();
+    realtime_sent_time_ = event_loop_->realtime_now();
 
     // Drop the reference to the message so that we allocate a new message for
     // next time.  Otherwise we will continue to reuse the same memory for all
@@ -167,7 +176,10 @@
     return true;
   }
 
-  bool DoSend(const void *msg, size_t size) override {
+  bool DoSend(const void *msg, size_t size,
+              aos::monotonic_clock::time_point monotonic_remote_time,
+              aos::realtime_clock::time_point realtime_remote_time,
+              uint32_t remote_queue_index) override {
     CHECK_LE(size, this->size()) << ": Attempting to send too big a message.";
 
     // This is wasteful, but since flatbuffers fill from the back end of the
@@ -179,7 +191,8 @@
     // data segment.
     memcpy(message_->data() + simulated_channel_->max_size() - size, msg, size);
 
-    return Send(size);
+    return Send(size, monotonic_remote_time, realtime_remote_time,
+                remote_queue_index);
   }
 
  private:
@@ -232,6 +245,15 @@
   void SetMsg(std::shared_ptr<SimulatedMessage> msg) {
     msg_ = msg;
     context_ = msg_->context;
+    if (context_.remote_queue_index == 0xffffffffu) {
+      context_.remote_queue_index = context_.queue_index;
+    }
+    if (context_.monotonic_remote_time == aos::monotonic_clock::min_time) {
+      context_.monotonic_remote_time = context_.monotonic_event_time;
+    }
+    if (context_.realtime_remote_time == aos::realtime_clock::min_time) {
+      context_.realtime_remote_time = context_.realtime_event_time;
+    }
   }
 
   // Internal method for Simulation to add a message to the buffer.
@@ -527,7 +549,7 @@
   // Messages are queued in order.  If we are the first, add ourselves.
   // Otherwise, don't.
   if (msgs_.size() == 0) {
-    event_.set_event_time(message->context.monotonic_sent_time);
+    event_.set_event_time(message->context.monotonic_event_time);
     simulated_event_loop_->AddEvent(&event_);
 
     DoSchedule(event_time);
@@ -541,12 +563,23 @@
 
   const monotonic_clock::time_point monotonic_now =
       simulated_event_loop_->monotonic_now();
-  DoCallCallback([monotonic_now]() { return monotonic_now; },
-                 msgs_.front()->context);
+  Context context = msgs_.front()->context;
+
+  if (context.remote_queue_index == 0xffffffffu) {
+    context.remote_queue_index = context.queue_index;
+  }
+  if (context.monotonic_remote_time == aos::monotonic_clock::min_time) {
+    context.monotonic_remote_time = context.monotonic_event_time;
+  }
+  if (context.realtime_remote_time == aos::realtime_clock::min_time) {
+    context.realtime_remote_time = context.realtime_event_time;
+  }
+
+  DoCallCallback([monotonic_now]() { return monotonic_now; }, context);
 
   msgs_.pop_front();
   if (msgs_.size() != 0) {
-    event_.set_event_time(msgs_.front()->context.monotonic_sent_time);
+    event_.set_event_time(msgs_.front()->context.monotonic_event_time);
     simulated_event_loop_->AddEvent(&event_);
 
     DoSchedule(event_.event_time());
@@ -579,8 +612,9 @@
   return ::std::move(fetcher);
 }
 
-void SimulatedChannel::Send(std::shared_ptr<SimulatedMessage> message) {
-  message->context.queue_index = next_queue_index_.index();
+uint32_t SimulatedChannel::Send(std::shared_ptr<SimulatedMessage> message) {
+  const uint32_t queue_index = next_queue_index_.index();
+  message->context.queue_index = queue_index;
   message->context.data =
       message->data() + channel()->max_size() - message->context.size;
   next_queue_index_ = next_queue_index_.Increment();
@@ -594,6 +628,8 @@
   for (auto &fetcher : fetchers_) {
     fetcher->Enqueue(message);
   }
+
+  return queue_index;
 }
 
 void SimulatedChannel::UnregisterFetcher(SimulatedFetcher *fetcher) {
diff --git a/aos/ipc_lib/lockless_queue.cc b/aos/ipc_lib/lockless_queue.cc
index 3fb506b..903150b 100644
--- a/aos/ipc_lib/lockless_queue.cc
+++ b/aos/ipc_lib/lockless_queue.cc
@@ -552,16 +552,30 @@
   return &message->data[0];
 }
 
-void LocklessQueue::Sender::Send(const char *data, size_t length) {
+void LocklessQueue::Sender::Send(
+    const char *data, size_t length,
+    aos::monotonic_clock::time_point monotonic_remote_time,
+    aos::realtime_clock::time_point realtime_remote_time,
+    uint32_t remote_queue_index,
+    aos::monotonic_clock::time_point *monotonic_sent_time,
+    aos::realtime_clock::time_point *realtime_sent_time,
+    uint32_t *queue_index) {
   CHECK_LE(length, size());
   // Flatbuffers write from the back of the buffer to the front.  If we are
   // going to write an explicit chunk of memory into the buffer, we need to
   // adhere to this convention and place it at the end.
   memcpy((reinterpret_cast<char *>(Data()) + size() - length), data, length);
-  Send(length);
+  Send(length, monotonic_remote_time, realtime_remote_time, remote_queue_index,
+       monotonic_sent_time, realtime_sent_time, queue_index);
 }
 
-void LocklessQueue::Sender::Send(size_t length) {
+void LocklessQueue::Sender::Send(
+    size_t length, aos::monotonic_clock::time_point monotonic_remote_time,
+    aos::realtime_clock::time_point realtime_remote_time,
+    uint32_t remote_queue_index,
+    aos::monotonic_clock::time_point *monotonic_sent_time,
+    aos::realtime_clock::time_point *realtime_sent_time,
+    uint32_t *queue_index) {
   const size_t queue_size = memory_->queue_size();
   CHECK_LE(length, size());
 
@@ -572,6 +586,11 @@
   Message *const message = memory_->GetMessage(scratch_index);
 
   message->header.length = length;
+  // Pass these through.  Any alternative behavior can be implemented out a
+  // layer.
+  message->header.remote_queue_index = remote_queue_index;
+  message->header.monotonic_remote_time = monotonic_remote_time;
+  message->header.realtime_remote_time = realtime_remote_time;
 
   while (true) {
     const QueueIndex actual_next_queue_index =
@@ -625,6 +644,15 @@
 
     message->header.monotonic_sent_time = ::aos::monotonic_clock::now();
     message->header.realtime_sent_time = ::aos::realtime_clock::now();
+    if (monotonic_sent_time != nullptr) {
+      *monotonic_sent_time = message->header.monotonic_sent_time;
+    }
+    if (realtime_sent_time != nullptr) {
+      *realtime_sent_time = message->header.realtime_sent_time;
+    }
+    if (queue_index != nullptr) {
+      *queue_index = next_queue_index.index();
+    }
 
     // Before we are fully done filling out the message, update the Sender state
     // with the new index to write.  This re-uses the barrier for the
@@ -676,8 +704,10 @@
 LocklessQueue::ReadResult LocklessQueue::Read(
     uint32_t uint32_queue_index,
     ::aos::monotonic_clock::time_point *monotonic_sent_time,
-    ::aos::realtime_clock::time_point *realtime_sent_time, size_t *length,
-    char *data) {
+    ::aos::realtime_clock::time_point *realtime_sent_time,
+    ::aos::monotonic_clock::time_point *monotonic_remote_time,
+    ::aos::realtime_clock::time_point *realtime_remote_time,
+    uint32_t *remote_queue_index, size_t *length, char *data) {
   const size_t queue_size = memory_->queue_size();
 
   // Build up the QueueIndex.
@@ -751,6 +781,13 @@
   // make length be from either end.
   *monotonic_sent_time = m->header.monotonic_sent_time;
   *realtime_sent_time = m->header.realtime_sent_time;
+  if (m->header.remote_queue_index == 0xffffffffu) {
+    *remote_queue_index = queue_index.index();
+  } else {
+    *remote_queue_index = m->header.remote_queue_index;
+  }
+  *monotonic_remote_time = m->header.monotonic_remote_time;
+  *realtime_remote_time = m->header.realtime_remote_time;
   memcpy(data, &m->data[0], message_data_size());
   *length = m->header.length;
 
diff --git a/aos/ipc_lib/lockless_queue.h b/aos/ipc_lib/lockless_queue.h
index d539386..976f758 100644
--- a/aos/ipc_lib/lockless_queue.h
+++ b/aos/ipc_lib/lockless_queue.h
@@ -65,6 +65,13 @@
     // fails.
     ::aos::monotonic_clock::time_point monotonic_sent_time;
     ::aos::realtime_clock::time_point realtime_sent_time;
+    // Timestamps of the message from the remote node.  These are transparently
+    // passed through.
+    ::aos::monotonic_clock::time_point monotonic_remote_time;
+    ::aos::realtime_clock::time_point realtime_remote_time;
+
+    // Queue index from the remote node.
+    uint32_t remote_queue_index;
 
     size_t length;
   } header;
@@ -146,7 +153,9 @@
   ReadResult Read(uint32_t queue_index,
                   ::aos::monotonic_clock::time_point *monotonic_sent_time,
                   ::aos::realtime_clock::time_point *realtime_sent_time,
-                  size_t *length, char *data);
+                  ::aos::monotonic_clock::time_point *monotonic_remote_time,
+                  ::aos::realtime_clock::time_point *realtime_remote_time,
+                  uint32_t *remote_queue_index, size_t *length, char *data);
 
   // Returns the index to the latest queue message.  Returns empty_queue_index()
   // if there are no messages in the queue.  Do note that this index wraps if
@@ -195,10 +204,26 @@
     // Note: calls to Data() are expensive enough that you should cache it.
     size_t size();
     void *Data();
-    void Send(size_t length);
+    void Send(size_t length,
+              aos::monotonic_clock::time_point monotonic_remote_time =
+                  aos::monotonic_clock::min_time,
+              aos::realtime_clock::time_point realtime_remote_time =
+                  aos::realtime_clock::min_time,
+              uint32_t remote_queue_index = 0xffffffff,
+              aos::monotonic_clock::time_point *monotonic_sent_time = nullptr,
+              aos::realtime_clock::time_point *realtime_sent_time = nullptr,
+              uint32_t *queue_index = nullptr);
 
     // Sends up to length data.  Does not wakeup the target.
-    void Send(const char *data, size_t length);
+    void Send(const char *data, size_t length,
+              aos::monotonic_clock::time_point monotonic_remote_time =
+                  aos::monotonic_clock::min_time,
+              aos::realtime_clock::time_point realtime_remote_time =
+                  aos::realtime_clock::min_time,
+              uint32_t remote_queue_index = 0xffffffff,
+              aos::monotonic_clock::time_point *monotonic_sent_time = nullptr,
+              aos::realtime_clock::time_point *realtime_sent_time = nullptr,
+              uint32_t *queue_index = nullptr);
 
    private:
     friend class LocklessQueue;
diff --git a/aos/ipc_lib/lockless_queue_death_test.cc b/aos/ipc_lib/lockless_queue_death_test.cc
index e3d6c5e..213c9e4 100644
--- a/aos/ipc_lib/lockless_queue_death_test.cc
+++ b/aos/ipc_lib/lockless_queue_death_test.cc
@@ -584,12 +584,16 @@
         while (true) {
           ::aos::monotonic_clock::time_point monotonic_sent_time;
           ::aos::realtime_clock::time_point realtime_sent_time;
+          ::aos::monotonic_clock::time_point monotonic_remote_time;
+          ::aos::realtime_clock::time_point realtime_remote_time;
+          uint32_t remote_queue_index;
           char read_data[1024];
           size_t length;
 
           LocklessQueue::ReadResult read_result =
-              queue.Read(i, &monotonic_sent_time, &realtime_sent_time, &length,
-                         &(read_data[0]));
+              queue.Read(i, &monotonic_sent_time, &realtime_sent_time,
+                         &monotonic_remote_time, &realtime_remote_time,
+                         &remote_queue_index, &length, &(read_data[0]));
 
           if (read_result != LocklessQueue::ReadResult::GOOD) {
             break;
diff --git a/aos/ipc_lib/lockless_queue_test.cc b/aos/ipc_lib/lockless_queue_test.cc
index b9cb54d..109c2ea 100644
--- a/aos/ipc_lib/lockless_queue_test.cc
+++ b/aos/ipc_lib/lockless_queue_test.cc
@@ -260,6 +260,9 @@
     // Read a result from 5 in the past.
     ::aos::monotonic_clock::time_point monotonic_sent_time;
     ::aos::realtime_clock::time_point realtime_sent_time;
+    ::aos::monotonic_clock::time_point monotonic_remote_time;
+    ::aos::realtime_clock::time_point realtime_remote_time;
+    uint32_t remote_queue_index;
     char read_data[1024];
     size_t length;
 
@@ -271,7 +274,8 @@
     }
     LocklessQueue::ReadResult read_result =
         queue.Read(index.index(), &monotonic_sent_time, &realtime_sent_time,
-                   &length, &(read_data[0]));
+                   &monotonic_remote_time, &realtime_remote_time,
+                   &remote_queue_index, &length, &(read_data[0]));
 
     // This should either return GOOD, or TOO_OLD if it is before the start of
     // the queue.
diff --git a/aos/ipc_lib/queue_racer.cc b/aos/ipc_lib/queue_racer.cc
index 350350c..9bb0a70 100644
--- a/aos/ipc_lib/queue_racer.cc
+++ b/aos/ipc_lib/queue_racer.cc
@@ -251,6 +251,9 @@
        i < (1 + write_wrap_count) * num_messages_ * num_threads_; ++i) {
     ::aos::monotonic_clock::time_point monotonic_sent_time;
     ::aos::realtime_clock::time_point realtime_sent_time;
+    ::aos::monotonic_clock::time_point monotonic_remote_time;
+    ::aos::realtime_clock::time_point realtime_remote_time;
+    uint32_t remote_queue_index;
     size_t length;
     char read_data[1024];
 
@@ -259,7 +262,8 @@
                                        0xffffffffu, queue.QueueSize()));
     LocklessQueue::ReadResult read_result =
         queue.Read(wrapped_i, &monotonic_sent_time, &realtime_sent_time,
-                   &length, &(read_data[0]));
+                   &monotonic_remote_time, &realtime_remote_time,
+                   &remote_queue_index, &length, &(read_data[0]));
 
     if (race_reads) {
       if (read_result == LocklessQueue::ReadResult::NOTHING_NEW) {
@@ -280,6 +284,9 @@
     ASSERT_GT(monotonic_sent_time, last_monotonic_sent_time);
     last_monotonic_sent_time = monotonic_sent_time;
 
+    EXPECT_EQ(monotonic_remote_time, aos::monotonic_clock::min_time);
+    EXPECT_EQ(realtime_remote_time, aos::realtime_clock::min_time);
+
     ThreadPlusCount tpc;
     ASSERT_EQ(length, sizeof(ThreadPlusCount));
     memcpy(&tpc, read_data + queue.message_data_size() - length,
diff --git a/y2016/vision/target_receiver.cc b/y2016/vision/target_receiver.cc
index b946f8b..e9a2e1d 100644
--- a/y2016/vision/target_receiver.cc
+++ b/y2016/vision/target_receiver.cc
@@ -205,7 +205,7 @@
   bool CompleteVisionStatus(::y2016::vision::VisionStatusT *status) {
     while (drivetrain_status_fetcher_.FetchNext()) {
       data_[data_index_].time =
-          drivetrain_status_fetcher_.context().monotonic_sent_time;
+          drivetrain_status_fetcher_.context().monotonic_event_time;
       data_[data_index_].left =
           drivetrain_status_fetcher_->estimated_left_position();
       data_[data_index_].right =
diff --git a/y2017/control_loops/superstructure/superstructure.cc b/y2017/control_loops/superstructure/superstructure.cc
index 29886e2..a0448fd 100644
--- a/y2017/control_loops/superstructure/superstructure.cc
+++ b/y2017/control_loops/superstructure/superstructure.cc
@@ -138,7 +138,7 @@
           status->fbb());
   flatbuffers::Offset<ShooterStatus> shooter_offset = shooter_.Iterate(
       unsafe_goal != nullptr ? &shooter_goal : nullptr,
-      position->theta_shooter(), position_context().monotonic_sent_time,
+      position->theta_shooter(), position_context().monotonic_event_time,
       output != nullptr ? &(output_struct.voltage_shooter) : nullptr,
       status->fbb());
 
diff --git a/y2017/control_loops/superstructure/vision_time_adjuster.cc b/y2017/control_loops/superstructure/vision_time_adjuster.cc
index 7db653e..6684546 100644
--- a/y2017/control_loops/superstructure/vision_time_adjuster.cc
+++ b/y2017/control_loops/superstructure/vision_time_adjuster.cc
@@ -116,7 +116,7 @@
   if (drivetrain_status_fetcher_.Fetch()) {
     const auto &position = drivetrain_status_fetcher_.get();
     DrivetrainAngle new_position{
-        .time = drivetrain_status_fetcher_.context().monotonic_sent_time,
+        .time = drivetrain_status_fetcher_.context().monotonic_event_time,
         .left = position->estimated_left_position(),
         .right = position->estimated_right_position()};
     drivetrain_data_.Push(new_position);
diff --git a/y2018/control_loops/superstructure/superstructure.cc b/y2018/control_loops/superstructure/superstructure.cc
index 9f5b9fe..f24da3b 100644
--- a/y2018/control_loops/superstructure/superstructure.cc
+++ b/y2018/control_loops/superstructure/superstructure.cc
@@ -289,7 +289,7 @@
     SendColors(0.5, 0.0, 0.0);
   } else if (!vision_status_fetcher_.get() ||
              monotonic_now >
-                 vision_status_fetcher_.context().monotonic_sent_time +
+                 vision_status_fetcher_.context().monotonic_event_time +
                      chrono::seconds(1)) {
     SendColors(0.5, 0.5, 0.0);
   } else if (rotation_state_ == RotationState::ROTATING_LEFT ||
diff --git a/y2018/wpilib_interface.cc b/y2018/wpilib_interface.cc
index 968076d..641a474 100644
--- a/y2018/wpilib_interface.cc
+++ b/y2018/wpilib_interface.cc
@@ -537,7 +537,7 @@
     // Flash the red light slowly.
     StatusLightT color;
     if (!status_light_fetcher_.get() ||
-        monotonic_now > status_light_fetcher_.context().monotonic_sent_time +
+        monotonic_now > status_light_fetcher_.context().monotonic_event_time +
                             chrono::milliseconds(100)) {
       color.red = 0.0;
       color.green = 0.0;
@@ -549,7 +549,7 @@
         color.red = 0.5;
       } else if (!vision_status_fetcher_.get() ||
                  monotonic_now >
-                     vision_status_fetcher_.context().monotonic_sent_time +
+                     vision_status_fetcher_.context().monotonic_event_time +
                          chrono::seconds(1)) {
         color.red = 0.5;
         color.green = 0.5;
diff --git a/y2019/vision/server/server.cc b/y2019/vision/server/server.cc
index 99a3727..56aa289 100644
--- a/y2019/vision/server/server.cc
+++ b/y2019/vision/server/server.cc
@@ -165,7 +165,7 @@
                          &camera_frames) {
         while (drivetrain_status_fetcher.FetchNext()) {
           DrivetrainPosition drivetrain_position{
-              drivetrain_status_fetcher.context().monotonic_sent_time,
+              drivetrain_status_fetcher.context().monotonic_event_time,
               drivetrain_status_fetcher->x(), drivetrain_status_fetcher->y(),
               drivetrain_status_fetcher->theta()};
 
diff --git a/y2019/wpilib_interface.cc b/y2019/wpilib_interface.cc
index 097f011..b6d432f 100644
--- a/y2019/wpilib_interface.cc
+++ b/y2019/wpilib_interface.cc
@@ -661,7 +661,7 @@
     // Flash the red light slowly.
     StatusLightT color;
     if (!status_light_fetcher_.get() ||
-        status_light_fetcher_.context().monotonic_sent_time +
+        status_light_fetcher_.context().monotonic_event_time +
                 chrono::milliseconds(100) <
             event_loop_->monotonic_now()) {
       color.red = 0.0;