converted more stuff to LogInterval
diff --git a/aos/common/common.gyp b/aos/common/common.gyp
index 7259c32..906b5b2 100644
--- a/aos/common/common.gyp
+++ b/aos/common/common.gyp
@@ -183,6 +183,7 @@
         'time',
         'control_loop_queues',
         '<(AOS)/common/logging/logging.gyp:queue_logging',
+        '<(AOS)/common/util/util.gyp:log_interval',
       ],
       'export_dependent_settings': [
         '<(AOS)/common/messages/messages.gyp:aos_queues',
@@ -191,6 +192,7 @@
         'time',
         'control_loop_queues',
         '<(AOS)/common/logging/logging.gyp:queue_logging',
+        '<(AOS)/common/util/util.gyp:log_interval',
       ],
     },
     {
diff --git a/aos/common/control_loop/ControlLoop-tmpl.h b/aos/common/control_loop/ControlLoop-tmpl.h
index 3173d2c..b5714d5 100644
--- a/aos/common/control_loop/ControlLoop-tmpl.h
+++ b/aos/common/control_loop/ControlLoop-tmpl.h
@@ -11,6 +11,10 @@
 // TODO(aschuh): Tests.
 
 template <class T, bool has_position, bool fail_no_position>
+constexpr ::aos::time::Time
+    ControlLoop<T, has_position, fail_no_position>::kStaleLogInterval;
+
+template <class T, bool has_position, bool fail_no_position>
 void ControlLoop<T, has_position, fail_no_position>::ZeroOutputs() {
   aos::ScopedMessagePtr<OutputType> output =
       control_loop_->output.MakeMessage();
@@ -29,7 +33,7 @@
   // goals.
   const GoalType *goal = control_loop_->goal.get();
   if (goal == NULL) {
-    LOG(ERROR, "No prior control loop goal.\n");
+    LOG_INTERVAL(no_prior_goal_);
     ZeroOutputs();
     return;
   }
@@ -48,15 +52,15 @@
       if (control_loop_->position.get()) {
         int msec_age = control_loop_->position.Age().ToMSec();
         if (!control_loop_->position.IsNewerThanMS(kPositionTimeoutMs)) {
-          LOG(ERROR, "Stale position. %d ms > %d ms.  Outputs disabled.\n",
-              msec_age, kPositionTimeoutMs);
+          LOG_INTERVAL(very_stale_position_);
           ZeroOutputs();
           return;
         } else {
-          LOG(ERROR, "Stale position. %d ms\n", msec_age);
+          LOG(ERROR, "Stale position. %d ms (< %d ms)\n", msec_age,
+              kPositionTimeoutMs);
         }
       } else {
-        LOG(ERROR, "Never had a position.\n");
+        LOG_INTERVAL(no_prior_position_);
         if (fail_no_position) {
           ZeroOutputs();
           return;
@@ -77,10 +81,9 @@
     outputs_enabled = true;
   } else {
     if (aos::robot_state.get()) {
-      int msec_age = aos::robot_state.Age().ToMSec();
-      LOG(ERROR, "Driver Station packet is too old (%d ms).\n", msec_age);
+      LOG_INTERVAL(driver_station_old_);
     } else {
-      LOG(ERROR, "No Driver Station packet.\n");
+      LOG_INTERVAL(no_driver_station_);
     }
   }
 
diff --git a/aos/common/control_loop/ControlLoop.h b/aos/common/control_loop/ControlLoop.h
index 6af7235..e48d259 100644
--- a/aos/common/control_loop/ControlLoop.h
+++ b/aos/common/control_loop/ControlLoop.h
@@ -7,6 +7,7 @@
 #include "aos/common/type_traits.h"
 #include "aos/common/queue.h"
 #include "aos/common/time.h"
+#include "aos/common/util/log_interval.h"
 
 namespace aos {
 namespace control_loops {
@@ -129,6 +130,25 @@
  private:
   // Pointer to the queue group
   T *control_loop_;
+
+  typedef ::aos::util::SimpleLogInterval SimpleLogInterval;
+  static constexpr ::aos::time::Time kStaleLogInterval =
+      ::aos::time::Time::InSeconds(0.1);
+  SimpleLogInterval very_stale_position_ =
+      SimpleLogInterval(kStaleLogInterval, ERROR,
+                        "outputs disabled because position is very stale");
+  SimpleLogInterval no_prior_goal_ =
+      SimpleLogInterval(kStaleLogInterval, ERROR,
+                        "no prior goal");
+  SimpleLogInterval no_prior_position_ =
+      SimpleLogInterval(kStaleLogInterval, ERROR,
+                        "no prior position");
+  SimpleLogInterval no_driver_station_ =
+      SimpleLogInterval(kStaleLogInterval, ERROR,
+                        "no driver station packet");
+  SimpleLogInterval driver_station_old_ =
+      SimpleLogInterval(kStaleLogInterval, ERROR,
+                        "driver station packet is too old");
 };
 
 }  // namespace control_loops
diff --git a/aos/common/logging/queue_logging.h b/aos/common/logging/queue_logging.h
index df343df..191d4c7 100644
--- a/aos/common/logging/queue_logging.h
+++ b/aos/common/logging/queue_logging.h
@@ -11,7 +11,6 @@
 namespace aos {
 namespace logging {
 
-#if 1
 #define LOG_STRUCT(level, message, structure)                          \
   do {                                                                 \
     static const ::std::string kAosLoggingMessage(                     \
@@ -24,9 +23,6 @@
       abort();                                                         \
     }                                                                  \
   } while (false)
-#else
-#define LOG_STRUCT(level, message, structure)
-#endif
 
 template <class T>
 void DoLogStruct(log_level level, const ::std::string &message,
diff --git a/aos/common/util/log_interval.h b/aos/common/util/log_interval.h
index 1e5c57b..9acbe40 100644
--- a/aos/common/util/log_interval.h
+++ b/aos/common/util/log_interval.h
@@ -46,6 +46,8 @@
     return r;
   }
 
+  const ::aos::time::Time &interval() const { return interval_; }
+
  private:
   int count_;
   const ::aos::time::Time interval_;
@@ -65,8 +67,9 @@
   void Hit(const char *context) {
     interval_.WantToLog();
     if (interval_.ShouldLog()) {
-      log_do(level_, "%s: %.*s %d times\n", context, message_.size(),
-             message_.data(), interval_.Count());
+      log_do(level_, "%s: %.*s %d times over %f sec\n", context,
+             message_.size(), message_.data(), interval_.Count(),
+             interval_.interval().ToSeconds());
     }
   }
 
diff --git a/bbb_cape/src/bbb/packet_finder.h b/bbb_cape/src/bbb/packet_finder.h
index 85281fb..962291f 100644
--- a/bbb_cape/src/bbb/packet_finder.h
+++ b/bbb_cape/src/bbb/packet_finder.h
@@ -67,7 +67,7 @@
 
   typedef ::aos::util::SimpleLogInterval SimpleLogInterval;
   static constexpr ::aos::time::Time kDebugLogInterval =
-      ::aos::time::Time::InSeconds(0.25);
+      ::aos::time::Time::InSeconds(0.3);
   SimpleLogInterval invalid_packet_ =
       SimpleLogInterval(kDebugLogInterval, INFO, "invalid packet");
   SimpleLogInterval bad_checksum_ =
diff --git a/bbb_cape/src/bbb/uart_reader.cc b/bbb_cape/src/bbb/uart_reader.cc
index 9ed0c39..c45c8ea 100644
--- a/bbb_cape/src/bbb/uart_reader.cc
+++ b/bbb_cape/src/bbb/uart_reader.cc
@@ -74,8 +74,7 @@
     : fd_(open_device()) {
   
   if (fd_ < 0) {
-    LOG(FATAL, "open(%s, O_RDWR | O_NOCTTY | O_NOBLOCK) failed with %d: %s."
-               " Did you read my note in bbb/uart_reader.cc?\n",
+    LOG(FATAL, "open(%s, O_RDWR | O_NOCTTY | O_NOBLOCK) failed with %d: %s\n",
         device, errno, strerror(errno));
   }
 
diff --git a/frc971/control_loops/drivetrain/drivetrain.cc b/frc971/control_loops/drivetrain/drivetrain.cc
index 982c6e9..a074c22 100644
--- a/frc971/control_loops/drivetrain/drivetrain.cc
+++ b/frc971/control_loops/drivetrain/drivetrain.cc
@@ -581,7 +581,7 @@
 
   bool bad_pos = false;
   if (position == nullptr) {
-    LOG(WARNING, "no position\n");
+    LOG_INTERVAL(no_position_);
     bad_pos = true;
   }
 
diff --git a/frc971/control_loops/drivetrain/drivetrain.gyp b/frc971/control_loops/drivetrain/drivetrain.gyp
index 7821c39..d7bce50 100644
--- a/frc971/control_loops/drivetrain/drivetrain.gyp
+++ b/frc971/control_loops/drivetrain/drivetrain.gyp
@@ -47,6 +47,7 @@
         '<(DEPTH)/aos/build/externals.gyp:libcdd',
         '<(DEPTH)/frc971/control_loops/control_loops.gyp:state_feedback_loop',
         '<(DEPTH)/frc971/queues/queues.gyp:queues',
+        '<(AOS)/common/util/util.gyp:log_interval',
       ],
       'export_dependent_settings': [
         '<(DEPTH)/aos/build/externals.gyp:libcdd',
diff --git a/frc971/control_loops/drivetrain/drivetrain.h b/frc971/control_loops/drivetrain/drivetrain.h
index 249de70..9ee7a27 100644
--- a/frc971/control_loops/drivetrain/drivetrain.h
+++ b/frc971/control_loops/drivetrain/drivetrain.h
@@ -7,6 +7,7 @@
 #include "aos/common/control_loop/ControlLoop.h"
 #include "aos/controls/polytope.h"
 #include "frc971/control_loops/drivetrain/drivetrain.q.h"
+#include "aos/common/util/log_interval.h"
 
 namespace frc971 {
 namespace control_loops {
@@ -35,6 +36,10 @@
       const control_loops::Drivetrain::Position *position,
       control_loops::Drivetrain::Output *output,
       control_loops::Drivetrain::Status *status);
+
+  typedef ::aos::util::SimpleLogInterval SimpleLogInterval;
+  SimpleLogInterval no_position_ = SimpleLogInterval(
+      ::aos::time::Time::InSeconds(0.25), WARNING, "no position");
 };
 
 }  // namespace control_loops
diff --git a/frc971/output/motor_writer.cc b/frc971/output/motor_writer.cc
index c6dbf63..57040c1 100644
--- a/frc971/output/motor_writer.cc
+++ b/frc971/output/motor_writer.cc
@@ -5,10 +5,15 @@
 #include "aos/prime/output/motor_output.h"
 #include "aos/common/logging/logging.h"
 #include "aos/linux_code/init.h"
+#include "aos/common/util/log_interval.h"
+#include "aos/common/time.h"
+#include "aos/common/logging/queue_logging.h"
 
 #include "frc971/queues/piston.q.h"
 #include "frc971/control_loops/drivetrain/drivetrain.q.h"
 
+using ::aos::util::SimpleLogInterval;
+
 using ::frc971::control_loops::drivetrain;
 using ::frc971::control_loops::shifters;
 
@@ -18,7 +23,8 @@
 class MotorWriter : public ::aos::MotorOutput {
   // Maximum age of an output packet before the motors get zeroed instead.
   static const int kOutputMaxAgeMS = 20;
-  static const int kEnableDrivetrain = true;
+  static constexpr ::aos::time::Time kOldLogInterval =
+      ::aos::time::Time::InSeconds(0.5);
 
   virtual void RunIteration() {
     values_.digital_module = 0;
@@ -26,29 +32,37 @@
     values_.compressor_channel = 1;
     values_.solenoid_module = 0;
 
-    drivetrain.output.FetchLatest();
-    if (drivetrain.output.IsNewerThanMS(kOutputMaxAgeMS) && kEnableDrivetrain) {
-      SetPWMOutput(2, drivetrain.output->right_voltage / 12.0, kTalonBounds);
-      SetPWMOutput(3, drivetrain.output->right_voltage / 12.0, kTalonBounds);
-      SetPWMOutput(5, -drivetrain.output->left_voltage / 12.0, kTalonBounds);
-      SetPWMOutput(6, -drivetrain.output->left_voltage / 12.0, kTalonBounds);
-    } else {
-      DisablePWMOutput(2);
-      DisablePWMOutput(3);
-      DisablePWMOutput(5);
-      DisablePWMOutput(6);
-      if (kEnableDrivetrain) {
-        LOG(WARNING, "drivetrain not new enough\n");
+    if (true) {
+      drivetrain.output.FetchLatest();
+      if (drivetrain.output.get()) {
+        LOG_STRUCT(DEBUG, "will output", *drivetrain.output.get());
+      }
+      if (drivetrain.output.IsNewerThanMS(kOutputMaxAgeMS)) {
+        SetPWMOutput(2, drivetrain.output->right_voltage / 12.0, kTalonBounds);
+        SetPWMOutput(3, drivetrain.output->right_voltage / 12.0, kTalonBounds);
+        SetPWMOutput(5, -drivetrain.output->left_voltage / 12.0, kTalonBounds);
+        SetPWMOutput(6, -drivetrain.output->left_voltage / 12.0, kTalonBounds);
+      } else {
+        DisablePWMOutput(2);
+        DisablePWMOutput(3);
+        DisablePWMOutput(5);
+        DisablePWMOutput(6);
+        LOG_INTERVAL(drivetrain_old_);
+      }
+      shifters.FetchLatest();
+      if (shifters.get()) {
+        SetSolenoid(1, shifters->set);
+        SetSolenoid(2, !shifters->set);
       }
     }
-    shifters.FetchLatest();
-    if (shifters.get()) {
-      SetSolenoid(1, shifters->set);
-      SetSolenoid(2, !shifters->set);
-    }
   }
+
+  SimpleLogInterval drivetrain_old_ =
+      SimpleLogInterval(kOldLogInterval, WARNING, "drivetrain too old");
 };
 
+constexpr ::aos::time::Time MotorWriter::kOldLogInterval;
+
 }  // namespace output
 }  // namespace frc971
 
diff --git a/frc971/output/output.gyp b/frc971/output/output.gyp
index 1d81c69..da8f545 100644
--- a/frc971/output/output.gyp
+++ b/frc971/output/output.gyp
@@ -35,6 +35,8 @@
         '<(DEPTH)/frc971/control_loops/drivetrain/drivetrain.gyp:drivetrain_loop',
         '<(AOS)/common/common.gyp:controls',
         '<(DEPTH)/frc971/queues/queues.gyp:queues',
+        '<(AOS)/common/util/util.gyp:log_interval',
+        '<(AOS)/common/common.gyp:time',
       ],
     },
   ],