Merge "support passing the new test-logging flags through build.py"
diff --git a/aos/build/aos_all.gyp b/aos/build/aos_all.gyp
index bd333a3..10d5a5b 100644
--- a/aos/build/aos_all.gyp
+++ b/aos/build/aos_all.gyp
@@ -34,6 +34,8 @@
         '<(AOS)/common/common.gyp:mutex_test',
         '<(AOS)/common/common.gyp:condition_test',
         '<(AOS)/common/common.gyp:once_test',
+        '<(AOS)/common/common.gyp:event_test',
+        '<(AOS)/common/common.gyp:queue_testutils_test',
         '<(AOS)/common/logging/logging.gyp:logging_impl_test',
         '<(AOS)/common/util/util.gyp:options_test',
         '<(AOS)/common/common.gyp:queue_test',
diff --git a/aos/build/externals.gyp b/aos/build/externals.gyp
index 315f0bf..2bfe241 100644
--- a/aos/build/externals.gyp
+++ b/aos/build/externals.gyp
@@ -136,11 +136,9 @@
       ],
       'dependencies': [
         'gtest_prod',
-        '<(AOS)/common/common.gyp:queue_testutils',
       ],
       'export_dependent_settings': [
         'gtest_prod',
-        '<(AOS)/common/common.gyp:queue_testutils',
       ],
       'cflags!': ['-Werror'],
       'direct_dependent_settings': {
diff --git a/aos/common/actions/action_test.cc b/aos/common/actions/action_test.cc
index eb863e3..59d8d79 100644
--- a/aos/common/actions/action_test.cc
+++ b/aos/common/actions/action_test.cc
@@ -10,6 +10,7 @@
 #include "aos/common/actions/actions.h"
 #include "aos/common/actions/actions.q.h"
 #include "aos/common/actions/test_action.q.h"
+#include "aos/common/event.h"
 
 using ::aos::time::Time;
 
@@ -424,7 +425,58 @@
   EXPECT_FALSE(action_queue_.Running());
 }
 
-}  // namespace testing.
-}  // namespace actions.
-}  // namespace common.
-}  // namespace aos.
+// Tests that cancelling an action before the message confirming it started is
+// received works.
+// Situations like this used to lock the action queue up waiting for an action
+// to report that it successfully cancelled.
+// This situation is kind of a race condition, but it happens very consistently
+// when hitting buttons while the robot is in teleop-disabled. To hit the race
+// condition consistently in the test, there are a couple of Events inserted in
+// between various things running.
+TEST_F(ActionTest, CancelBeforeStart) {
+  Event thread_ready, ready_to_start, ready_to_stop;
+  ::std::thread action_thread(
+      [this, &thread_ready, &ready_to_start, &ready_to_stop]() {
+        TestActorNOP nop_act(&actions::test_action);
+        nop_act.Initialize();
+        thread_ready.Set();
+        ready_to_start.Wait();
+        nop_act.WaitForActionRequest();
+        LOG(DEBUG, "got a request to run\n");
+        const uint32_t running_id = nop_act.RunIteration();
+        LOG(DEBUG, "waiting for %" PRIx32 " to be stopped\n", running_id);
+        ready_to_stop.Set();
+        nop_act.WaitForStop(running_id);
+      });
+
+  action_queue_.CancelAllActions();
+  EXPECT_FALSE(action_queue_.Running());
+  thread_ready.Wait();
+  LOG(DEBUG, "starting action\n");
+  action_queue_.EnqueueAction(MakeTestActionNOP());
+  action_queue_.Tick();
+  action_queue_.CancelAllActions();
+  ready_to_start.Set();
+  LOG(DEBUG, "started action\n");
+  EXPECT_TRUE(action_queue_.Running());
+  ready_to_stop.Wait();
+  EXPECT_TRUE(action_queue_.Running());
+  LOG(DEBUG, "action is ready to stop\n");
+
+  action_queue_.Tick();
+  action_queue_.CancelAllActions();
+  EXPECT_FALSE(action_queue_.Running());
+  action_queue_.Tick();
+  action_queue_.CancelAllActions();
+  ASSERT_FALSE(action_queue_.Running());
+  action_thread.join();
+
+  action_queue_.Tick();
+  action_queue_.CancelAllActions();
+  ASSERT_FALSE(action_queue_.Running());
+}
+
+}  // namespace testing
+}  // namespace actions
+}  // namespace common
+}  // namespace aos
diff --git a/aos/common/actions/actions.gyp b/aos/common/actions/actions.gyp
index 623569b..69c9c7a 100644
--- a/aos/common/actions/actions.gyp
+++ b/aos/common/actions/actions.gyp
@@ -63,7 +63,8 @@
         '<(AOS)/common/logging/logging.gyp:queue_logging',
         '<(AOS)/common/common.gyp:queues',
         '<(AOS)/common/common.gyp:time',
-        'action_queue'
+        'action_queue',
+        '<(AOS)/common/common.gyp:event',
       ],
     },
   ],
diff --git a/aos/common/common.gyp b/aos/common/common.gyp
index a0750fe..461200e 100644
--- a/aos/common/common.gyp
+++ b/aos/common/common.gyp
@@ -21,6 +21,7 @@
         '<(AOS)/build/aos.gyp:logging',
         'once',
         '<(AOS)/linux_code/ipc_lib/ipc_lib.gyp:shared_mem',
+        'mutex',
       ],
       'export_dependent_settings': [
         '<(AOS)/linux_code/ipc_lib/ipc_lib.gyp:shared_mem',
@@ -244,11 +245,35 @@
       'dependencies': [
         '<(AOS)/linux_code/ipc_lib/ipc_lib.gyp:aos_sync',
         '<(AOS)/build/aos.gyp:logging_interface',
-        'die',
       ],
       'export_dependent_settings': [
         '<(AOS)/linux_code/ipc_lib/ipc_lib.gyp:aos_sync',
-        'die',
+      ],
+    },
+    {
+      'target_name': 'event',
+      'type': 'static_library',
+      'sources': [
+        '<(AOS)/linux_code/ipc_lib/event.cc',
+      ],
+      'dependencies': [
+        '<(AOS)/linux_code/ipc_lib/ipc_lib.gyp:aos_sync',
+        '<(AOS)/build/aos.gyp:logging_interface',
+      ],
+      'export_dependent_settings': [
+        '<(AOS)/linux_code/ipc_lib/ipc_lib.gyp:aos_sync',
+      ],
+    },
+    {
+      'target_name': 'queue_testutils_test',
+      'type': 'executable',
+      'sources': [
+        'queue_testutils_test.cc',
+      ],
+      'dependencies': [
+        '<(EXTERNALS):gtest',
+        'queue_testutils',
+        '<(AOS)/build/aos.gyp:logging',
       ],
     },
     {
@@ -269,6 +294,19 @@
       ],
     },
     {
+      'target_name': 'event_test',
+      'type': 'executable',
+      'sources': [
+        'event_test.cc',
+      ],
+      'dependencies': [
+        '<(EXTERNALS):gtest',
+        'event',
+        'queue_testutils',
+        'time',
+      ],
+    },
+    {
       'target_name': 'condition_test',
       'type': 'executable',
       'sources': [
diff --git a/aos/common/event.h b/aos/common/event.h
new file mode 100644
index 0000000..9839d41
--- /dev/null
+++ b/aos/common/event.h
@@ -0,0 +1,53 @@
+#ifndef AOS_COMMON_EVENT_H_
+#define AOS_COMMON_EVENT_H_
+
+#include "aos/linux_code/ipc_lib/aos_sync.h"
+
+namespace aos {
+
+// An abstraction of an event which is easy to implement for Linux and in other
+// environments.
+// On Linux at least, this is definitely safe for passing through C code with
+// memcpy etc.
+//
+// An event is either "set" or "unset". Any thread can transition it between
+// these two states and other threads can wait for an unset->set transition.
+// This is not a particularly powerful synchronization primitive, but it can be
+// much easier to use than more complicated ones in some situations. The name is
+// taken from Python's implementation of the same thing.
+//
+// An event is equivalent to a semaphore which is either set to 0 or infinity.
+// It is also equivalent to a condition variable with the monitored condition
+// being "is it set or not".
+//
+// IMPORTANT: You can NOT use this to successfully replace a standard condition
+// variable in most cases. When the condition being monitored changes separately
+// from the actual state of the condition variable/event, there WILL be race
+// conditions if you try to use this class.
+//
+// It is undefined behavior to destroy an Event while there are current
+// Wait()ers.
+class Event {
+ public:
+  // Creates an unset event.
+  Event();
+  // There must be no waiters when an Event is destroyed.
+  ~Event() = default;
+
+  // Waits for the event to be set. Returns immediately if it is already set.
+  void Wait();
+
+  // Wakes up all Wait()ers and sets the event (atomically).
+  void Set();
+  // Unsets the event so future Wait() callers will block instead of returning
+  // immediately.
+  // Returns true if the event was previously set.
+  bool Clear();
+
+ private:
+  aos_futex impl_;
+};
+
+}  // namespace aos
+
+#endif  // AOS_COMMON_EVENT_H_
diff --git a/aos/common/event_test.cc b/aos/common/event_test.cc
new file mode 100644
index 0000000..d8671eb
--- /dev/null
+++ b/aos/common/event_test.cc
@@ -0,0 +1,71 @@
+#include "aos/common/event.h"
+
+#include <thread>
+
+#include "gtest/gtest.h"
+
+#include "aos/common/queue_testutils.h"
+#include "aos/common/time.h"
+
+namespace aos {
+namespace testing {
+
+class EventTest : public ::testing::Test {
+ public:
+  Event test_event;
+
+ protected:
+  void SetUp() override {
+    ::aos::common::testing::EnableTestLogging();
+  }
+};
+
+// Makes sure that basic operations with no blocking or anything work.
+TEST_F(EventTest, Basic) {
+  EXPECT_FALSE(test_event.Clear());
+  EXPECT_FALSE(test_event.Clear());
+
+  test_event.Set();
+  test_event.Wait();
+  EXPECT_TRUE(test_event.Clear());
+  EXPECT_FALSE(test_event.Clear());
+}
+
+// Tests that tsan understands that events establish a happens-before
+// relationship.
+TEST_F(EventTest, ThreadSanitizer) {
+  for (int i = 0; i < 3000; ++i) {
+    int variable = 0;
+    test_event.Clear();
+    ::std::thread thread([this, &variable]() {
+      test_event.Wait();
+      --variable;
+    });
+    ++variable;
+    test_event.Set();
+    thread.join();
+    EXPECT_EQ(0, variable);
+  }
+}
+
+// Tests that an event blocks correctly.
+TEST_F(EventTest, Blocks) {
+  time::Time start_time, finish_time;
+  // Without this, it sometimes manages to fail under tsan.
+  Event started;
+  ::std::thread thread([this, &start_time, &finish_time, &started]() {
+    start_time = time::Time::Now();
+    started.Set();
+    test_event.Wait();
+    finish_time = time::Time::Now();
+  });
+  static const time::Time kWaitTime = time::Time::InSeconds(0.05);
+  started.Wait();
+  time::SleepFor(kWaitTime);
+  test_event.Set();
+  thread.join();
+  EXPECT_GE(finish_time - start_time, kWaitTime);
+}
+
+}  // namespace testing
+}  // namespace aos
diff --git a/aos/common/queue_testutils.cc b/aos/common/queue_testutils.cc
index d71d023..a40b5e9 100644
--- a/aos/common/queue_testutils.cc
+++ b/aos/common/queue_testutils.cc
@@ -10,6 +10,7 @@
 #include "aos/common/queue.h"
 #include "aos/common/logging/logging_impl.h"
 #include "aos/common/once.h"
+#include "aos/common/mutex.h"
 
 using ::aos::logging::LogMessage;
 
@@ -31,11 +32,13 @@
 
   // Clears out all of the messages already recorded.
   void ClearMessages() {
+    ::aos::MutexLocker locker(&messages_mutex_);
     messages_.clear();
   }
 
   // Prints out all of the messages (like when a test fails).
   void PrintAllMessages() {
+    ::aos::MutexLocker locker(&messages_mutex_);
     for (auto it = messages_.begin(); it != messages_.end(); ++it) {
       logging::internal::PrintMessage(stdout, *it);
     }
@@ -66,6 +69,7 @@
   }
 
   virtual void HandleMessage(const LogMessage &message) override {
+    ::aos::MutexLocker locker(&messages_mutex_);
     if (message.level == FATAL || print_as_messages_come_in_) {
       logging::internal::PrintMessage(output_file_, message);
     }
@@ -76,6 +80,7 @@
   ::std::vector<LogMessage> messages_;
   bool print_as_messages_come_in_ = false;
   FILE *output_file_ = stdout;
+  ::aos::Mutex messages_mutex_;
 };
 
 class MyTestEventListener : public ::testing::EmptyTestEventListener {
diff --git a/aos/common/queue_testutils_test.cc b/aos/common/queue_testutils_test.cc
new file mode 100644
index 0000000..2defe74
--- /dev/null
+++ b/aos/common/queue_testutils_test.cc
@@ -0,0 +1,31 @@
+#include "aos/common/queue_testutils.h"
+
+#include <thread>
+
+#include "gtest/gtest.h"
+
+#include "aos/common/logging/logging.h"
+
+namespace aos {
+namespace common {
+namespace testing {
+
+// Tests logging from multiple threads.
+// tsan used to complain about this.
+TEST(QueueTestutilsTest, MultithreadedLog) {
+  EnableTestLogging();
+
+  ::std::thread thread([]() {
+    for (int i = 0; i < 1000; ++i) {
+      LOG(INFO, "test from thread\n");
+    }
+  });
+  for (int i = 0; i < 1000; ++i) {
+    LOG(INFO, "not from thread\n");
+  }
+  thread.join();
+}
+
+}  // namespace testing
+}  // namespace common
+}  // namespace aos
diff --git a/aos/externals/gtest/gtest_main.cc b/aos/externals/gtest/gtest_main.cc
index 024c502..0f6b60e 100644
--- a/aos/externals/gtest/gtest_main.cc
+++ b/aos/externals/gtest/gtest_main.cc
@@ -2,7 +2,18 @@
 #include <getopt.h>
 
 #include "gtest/gtest.h"
-#include "aos/common/queue_testutils.h"
+
+namespace aos {
+namespace common {
+namespace testing {
+
+// Actually declared/defined in aos/common/queue_testutils.
+void SetLogFileName(const char* filename) __attribute__((weak));
+void ForcePrintLogsDuringTests() __attribute__((weak));
+
+}  // namespace testing
+}  // namespace common
+}  // namespace aos
 
 GTEST_API_ int main(int argc, char **argv) {
   static const struct option long_options[] = {
@@ -36,11 +47,15 @@
         break;
 
       case 'p':
-        aos::common::testing::ForcePrintLogsDuringTests();
+        if (::aos::common::testing::ForcePrintLogsDuringTests) {
+          ::aos::common::testing::ForcePrintLogsDuringTests();
+        }
         break;
 
       case 'o':
-        aos::common::testing::SetLogFileName(optarg);
+        if (::aos::common::testing::SetLogFileName) {
+          ::aos::common::testing::SetLogFileName(optarg);
+        }
         break;
 
       case '?':
diff --git a/aos/linux_code/ipc_lib/aos_sync.cc b/aos/linux_code/ipc_lib/aos_sync.cc
index ffd1061..311d88d 100644
--- a/aos/linux_code/ipc_lib/aos_sync.cc
+++ b/aos/linux_code/ipc_lib/aos_sync.cc
@@ -411,6 +411,10 @@
       return -1;
     }
   }
+#ifdef AOS_SANITIZER_thread
+  // Help tsan figure out that we're synchronizing on this.
+  __sync_add_and_fetch(m, 0);
+#endif
   return 0;
 }
 
diff --git a/aos/linux_code/ipc_lib/event.cc b/aos/linux_code/ipc_lib/event.cc
new file mode 100644
index 0000000..e95b45e
--- /dev/null
+++ b/aos/linux_code/ipc_lib/event.cc
@@ -0,0 +1,35 @@
+#include "aos/common/event.h"
+
+#include "aos/common/type_traits.h"
+#include "aos/common/logging/logging.h"
+
+namespace aos {
+
+Event::Event() : impl_(0) {
+  static_assert(shm_ok<Event>::value,
+                "Event is not safe for use in shared memory.");
+}
+
+void Event::Wait() {
+  int ret;
+  do {
+    ret = futex_wait(&impl_);
+  } while (ret == 1);
+  if (ret == 0) return;
+  CHECK_EQ(-1, ret);
+  PLOG(FATAL, "futex_wait(%p) failed", &impl_);
+}
+
+// We're not going to expose the number woken because that's not easily portable
+// to condition variable-based implementations.
+void Event::Set() {
+  if (futex_set(&impl_) == -1) {
+    PLOG(FATAL, "futex_set(%p) failed", &impl_);
+  }
+}
+
+bool Event::Clear() {
+  return !futex_unset(&impl_);
+}
+
+}  // namespace aos
diff --git a/aos/linux_code/logging/linux_logging.cc b/aos/linux_code/logging/linux_logging.cc
index c27e94b..b13a740 100644
--- a/aos/linux_code/logging/linux_logging.cc
+++ b/aos/linux_code/logging/linux_logging.cc
@@ -108,7 +108,7 @@
   if (__builtin_expect(dropped_messages > 0, false)) {
     ::aos::time::Time message_time =
         ::aos::time::Time(msg->seconds, msg->nseconds);
-    if (backoff_start - message_time < kDropBackoff) {
+    if (message_time - backoff_start < kDropBackoff) {
       ++dropped_messages;
       queue->FreeMessage(msg);
       return;
diff --git a/frc971/actors/stack_actor.cc b/frc971/actors/stack_actor.cc
index 3b80184..49fbf98 100644
--- a/frc971/actors/stack_actor.cc
+++ b/frc971/actors/stack_actor.cc
@@ -75,20 +75,25 @@
   }
 
   if (params.only_place) {
+    // open grabber for place only
     DoFridgeProfile(params.bottom + values.tote_height, 0.0, kFastElevatorMove,
                     kFastArmMove, false);
     // Finish early if we aren't supposed to grab.
     return true;
   }
 
-  DoFridgeProfile(params.bottom + values.tote_height, params.arm_clearance,
+  // TODO(ben): I'm not sure why this liitle jog is here. we are removing it for
+  // the fangs, but I want to keep the code here so austin can explain.
+  /*DoFridgeProfile(params.bottom + values.tote_height, params.arm_clearance,
                   kFastElevatorMove, kFastArmMove, false);
 
   if (ShouldCancel()) return true;
   DoFridgeProfile(params.bottom, params.arm_clearance, kFastElevatorMove,
-                  kFastArmMove, false);
+                  kFastArmMove, false);*/
   if (ShouldCancel()) return true;
-  DoFridgeProfile(params.bottom, 0.0, kFastElevatorMove, kFastArmMove, false);
+  // grab can (if in fang mode the grabber stays closed)
+  DoFridgeProfile(params.bottom, 0.0, kFastElevatorMove, kFastArmMove, false,
+                  true, true);
   if (ShouldCancel()) return true;
   aos::time::SleepFor(aos::time::Time::InMS(200));
 
diff --git a/frc971/actors/stack_and_hold_actor.cc b/frc971/actors/stack_and_hold_actor.cc
index 2acb415..ab9b077 100644
--- a/frc971/actors/stack_and_hold_actor.cc
+++ b/frc971/actors/stack_and_hold_actor.cc
@@ -23,6 +23,9 @@
     : FridgeActorBase<StackAndHoldActionQueueGroup>(queues) {}
 
 bool StackAndHoldActor::RunAction(const StackAndHoldParams &params) {
+  // TODO(ben)): this action is no longer used (source Cameron) and my be broken
+  // by the stack action having the grabbers closed at the end for the fangs. So
+  // here I am disabling it until further information is provided.
   if (params.place_not_stack) {
     // Move the arm out of the way.
     {
diff --git a/frc971/constants.cc b/frc971/constants.cc
index ec98743..785c0d7 100644
--- a/frc971/constants.cc
+++ b/frc971/constants.cc
@@ -248,13 +248,13 @@
            {-M_PI / 2 - 0.05, M_PI / 2 + 0.05, -M_PI / 2, M_PI / 2},
 
            // Elevator zeroing constants: left, right.
-           {kZeroingSampleSize, kElevatorEncoderIndexDifference, 0.088984, 0.3},
-           {kZeroingSampleSize, kElevatorEncoderIndexDifference, 0.104557, 0.3},
+           {kZeroingSampleSize, kElevatorEncoderIndexDifference, 0.110677, 0.3},  // Was 0.088984 (3 mm too high)
+           {kZeroingSampleSize, kElevatorEncoderIndexDifference, 0.109974, 0.3},  // Was 0.104557 (4 mm too low)
            // Arm zeroing constants: left, right.
            {kZeroingSampleSize, kArmEncoderIndexDifference, -0.324437, 0.3},
            {kZeroingSampleSize, kArmEncoderIndexDifference, -0.064683, 0.3},
-           0.722230 - -0.000594 - -0.026183,
-           -0.081354 - -0.000374 - -0.024793,
+           0.722230 - -0.000594 - -0.026183 - 0.003442,   // Left Elevator Poteniometer adjustment
+           -0.081354 - -0.000374 - -0.024793 - -0.006916, // Right Elevator Poteniometer adjustment
            -3.509611 - 0.007415 - -0.019081,
            3.506927 - 0.170017 - -0.147970,
 
diff --git a/frc971/control_loops/claw/claw.cc b/frc971/control_loops/claw/claw.cc
index 74c9060..068836c 100644
--- a/frc971/control_loops/claw/claw.cc
+++ b/frc971/control_loops/claw/claw.cc
@@ -231,7 +231,6 @@
         claw_position() <= values.claw.wrist.lower_hard_limit) {
       LOG(ERROR, "Claw at %f out of bounds [%f, %f].\n", claw_position(),
           values.claw.wrist.lower_limit, values.claw.wrist.upper_limit);
-      state_ = ESTOP;
     }
   }
 
diff --git a/frc971/control_loops/claw/claw.q b/frc971/control_loops/claw/claw.q
index 7696d3b..8da1c20 100644
--- a/frc971/control_loops/claw/claw.q
+++ b/frc971/control_loops/claw/claw.q
@@ -14,11 +14,11 @@
     // Angle of wrist joint.
     double angle;
     // Angular velocity of wrist.
-    double angular_velocity;
+    float angular_velocity;
     // Maximum profile velocity, or 0 for the default.
-    double max_velocity;
+    float max_velocity;
     // Maximum profile acceleration, or 0 for the default.
-    double max_acceleration;
+    float max_acceleration;
     // Voltage of intake rollers. Positive means sucking in, negative means
     // spitting out.
     double intake;
@@ -54,11 +54,11 @@
     // Estimated angle of wrist joint.
     double angle;
     // Estimated angular velocity of wrist.
-    double angular_velocity;
+    float angular_velocity;
 
     // Goal angle of wrist joint.
     double goal_angle;
-    double goal_velocity;
+    float goal_velocity;
     // Voltage of intake rollers. Positive means sucking in, negative means
     // spitting out.
     double intake;
diff --git a/frc971/control_loops/fridge/fridge.cc b/frc971/control_loops/fridge/fridge.cc
index e414f93..14514a9 100644
--- a/frc971/control_loops/fridge/fridge.cc
+++ b/frc971/control_loops/fridge/fridge.cc
@@ -335,12 +335,11 @@
             elevator_goal_ += kElevatorSafeHeightVelocity *
                               ::aos::controls::kLoopFrequency.ToSeconds();
             elevator_goal_velocity_ = kElevatorSafeHeightVelocity;
+            state_ = ZEROING_ELEVATOR;
           } else {
             // We want it stopped at whatever height it's currently set to.
             elevator_goal_velocity_ = 0;
           }
-
-          state_ = ZEROING_ELEVATOR;
           break;
         }
 
@@ -365,7 +364,8 @@
     case ZEROING_ARM:
       LOG(DEBUG, "Zeroing the arm\n");
 
-      if (elevator() < values.fridge.arm_zeroing_height) {
+      if (elevator() < values.fridge.arm_zeroing_height - 0.10 ||
+          elevator_goal_ < values.fridge.arm_zeroing_height) {
         LOG(INFO,
             "Going back to ZEROING_ELEVATOR until it gets high enough to "
             "safely zero the arm\n");
diff --git a/frc971/control_loops/fridge/fridge.q b/frc971/control_loops/fridge/fridge.q
index 637f499..257374d 100644
--- a/frc971/control_loops/fridge/fridge.q
+++ b/frc971/control_loops/fridge/fridge.q
@@ -42,19 +42,19 @@
     double height;
 
     // Angular velocity of the arm.
-    double angular_velocity;
+    float angular_velocity;
     // Linear velocity of the elevator.
-    double velocity;
+    float velocity;
 
     // Maximum arm profile angular velocity or 0 for the default.
-    double max_angular_velocity;
+    float max_angular_velocity;
     // Maximum elevator profile velocity or 0 for the default.
-    double max_velocity;
+    float max_velocity;
 
     // Maximum arm profile acceleration or 0 for the default.
-    double max_angular_acceleration;
+    float max_angular_acceleration;
     // Maximum elevator profile acceleration or 0 for the default.
-    double max_acceleration;
+    float max_acceleration;
 
     // X position of the fridge.
     double x;
@@ -62,19 +62,19 @@
     double y;
 
     // Velocity of the x position of the fridge.
-    double x_velocity;
+    float x_velocity;
     // Velocity of the y position of the fridge.
-    double y_velocity;
+    float y_velocity;
 
     // Maximum x profile velocity or 0 for the default.
-    double max_x_velocity;
+    float max_x_velocity;
     // Maximum y profile velocity or 0 for the default.
-    double max_y_velocity;
+    float max_y_velocity;
 
     // Maximum x profile acceleration or 0 for the default.
-    double max_x_acceleration;
+    float max_x_acceleration;
     // Maximum y profile acceleration or 0 for the default.
-    double max_y_acceleration;
+    float max_y_acceleration;
 
     // TODO(austin): Do I need acceleration here too?
 
@@ -93,34 +93,34 @@
     // Estimated angle of the arm.
     double angle;
     // Estimated angular velocity of the arm.
-    double angular_velocity;
+    float angular_velocity;
     // Estimated height of the elevator.
     double height;
     // Estimated velocity of the elvator.
-    double velocity;
+    float velocity;
     // state of the grabber pistons
     GrabberPistons grabbers;
 
     // Goal angle and velocity of the arm.
     double goal_angle;
-    double goal_angular_velocity;
+    float goal_angular_velocity;
     // Goal height and velocity of the elevator.
     double goal_height;
-    double goal_velocity;
+    float goal_velocity;
 
     // Estimated X/Y position of the fridge.
     // These are translated directly from the height/angle statuses.
     double x;
     double y;
-    double x_velocity;
-    double y_velocity;
+    float x_velocity;
+    float y_velocity;
 
     // X/Y goals of the fridge.
     // These are translated directly from the height/angle goals.
     double goal_x;
     double goal_y;
-    double goal_x_velocity;
-    double goal_y_velocity;
+    float goal_x_velocity;
+    float goal_y_velocity;
 
     // If true, we have aborted.
     bool estopped;