Merge "Checking in blob routines."
diff --git a/aos/testing/gtest_main.cc b/aos/testing/gtest_main.cc
index ef63293..e2ba564 100644
--- a/aos/testing/gtest_main.cc
+++ b/aos/testing/gtest_main.cc
@@ -40,7 +40,8 @@
             "  -p, --print-logs\n"
             "      Print the log messages as they are being generated.\n"
             "  -o, --log-file=FILE\n"
-            "      Print all log messages to FILE instead of standard output\n"
+            "      Print all log messages to FILE instead of standard output.\n"
+	    "      This implies -p.\n"
             );
         break;
 
@@ -51,6 +52,9 @@
         break;
 
       case 'o':
+        if (::aos::testing::ForcePrintLogsDuringTests) {
+          ::aos::testing::ForcePrintLogsDuringTests();
+        }
         if (::aos::testing::SetLogFileName) {
           ::aos::testing::SetLogFileName(optarg);
         }
diff --git a/tools/cpp/arm-frc-linux-gnueabi/libs/libc.so b/tools/cpp/arm-frc-linux-gnueabi/libs/libc.so
new file mode 100644
index 0000000..714bcfe
--- /dev/null
+++ b/tools/cpp/arm-frc-linux-gnueabi/libs/libc.so
@@ -0,0 +1,5 @@
+/* GNU ld script
+   Use the shared library, but some functions are only in
+   the static library, so try that secondarily.  */
+OUTPUT_FORMAT(elf32-littlearm)
+GROUP ( libc.so.6 libc_nonshared.a  AS_NEEDED ( ld-linux.so.3 ) )
diff --git a/tools/cpp/arm-frc-linux-gnueabi/libs/libpthread.so b/tools/cpp/arm-frc-linux-gnueabi/libs/libpthread.so
new file mode 100644
index 0000000..71f034f
--- /dev/null
+++ b/tools/cpp/arm-frc-linux-gnueabi/libs/libpthread.so
@@ -0,0 +1,5 @@
+/* GNU ld script
+   Use the shared library, but some functions are only in
+   the static library, so try that secondarily.  */
+OUTPUT_FORMAT(elf32-littlearm)
+GROUP ( libpthread.so.0 libpthread_nonshared.a )
diff --git a/vm/README.md b/vm/README.md
new file mode 100644
index 0000000..03e8965
--- /dev/null
+++ b/vm/README.md
@@ -0,0 +1,64 @@
+Requirements
+--------------------------------------------------------------------------------
+1. Install Vagrant <https://www.vagrantup.com/downloads.html>
+
+1. Install VirtualBox <https://www.virtualbox.org/wiki/Downloads>
+
+1. Add `vagrant` and `VBoxManage` to your PATH.
+    - This is most likely already done by the installation binaries.
+      It's added to the system path.
+    - To test this, type these commands in a terminal:
+
+            ~$ vagrant --version
+            Vagrant 1.8.1
+            ~$ VBoxManage --version
+            5.0.14r105127
+
+    - You may need to log out and back in for the path modifications to take
+      effect.
+
+1. On my Jessie installation I had to apply the following patch before I could
+   successfully shut down and reboot the VM.
+
+        --- /opt/vagrant/embedded/gems/gems/vagrant-1.7.4/plugins/guests/debian8/cap/halt.rb    2015-07-17 13:15:13.000000000 -0700
+        +++ new_halt.rb 2015-11-18 20:11:29.003055639 -0800
+        @@ -4,7 +4,7 @@
+               class Halt
+                 def self.halt(machine)
+                   begin
+        -            machine.communicate.sudo("shutdown -h -H")
+        +            machine.communicate.sudo("systemctl poweroff")
+                   rescue IOError
+                     # Do nothing, because it probably means the machine shut down
+                     # and SSH connection was lost.
+
+Usage
+--------------------------------------------------------------------------------
+1. Check this folder out on your computer somewhere.
+
+        svn co https://robotics.mvla.net/svn/frc971/2016/trunk/src/vagrant_dev_vm
+
+1. Go into the directory and build the VM.
+
+        vagrant up
+
+1. Some errors during the `vagrant up` process can be addressed by
+   re-provisioning the vagrant box. This is useful if, for example, an
+   `apt-get` invocation timed out and caused the provisioning process to abort.
+
+        vagrant provision
+
+1. Once build, reboot the VM so it starts the GUI properly.
+
+        vagrant reload
+
+1. You can then log in and open a terminal. The username and password are both
+   `user`.
+
+1. Download the code and build it.
+
+        git clone https://USERNAME@robotics.mvla.net/gerrit/971-Robot-Code
+        cd 971-Robot-Code
+        bazel build //y2016/... -- $(cat NO_BUILD_AMD64)
+
+   where USERNAME is the same username you use to log into SVN.
diff --git a/vm/Vagrantfile b/vm/Vagrantfile
new file mode 100644
index 0000000..c802a5a
--- /dev/null
+++ b/vm/Vagrantfile
@@ -0,0 +1,57 @@
+# Vagrantfile API/syntax version. Don't touch unless you know what you're
+# doing!
+VAGRANTFILE_API_VERSION = "2"
+
+# Install the necessary plugins.
+required_plugins = %w( vagrant-persistent-storage )
+required_plugins.each do |plugin|
+  unless Vagrant.has_plugin? plugin || ARGV[0] == 'plugin' then
+    exec "vagrant plugin install #{plugin};vagrant #{ARGV.join(" ")}"
+  end
+end
+
+Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
+  # All Vagrant configuration is done here. The most common configuration
+  # options are documented and commented below. For a complete reference,
+  # please see the online documentation at vagrantup.com.
+
+  # Every Vagrant virtual environment requires a box to build off of.
+  config.vm.box = "debian/jessie64"
+
+  config.vm.provider "virtualbox" do |vb|
+    # Don't boot with headless mode
+    vb.gui = true
+    vb.name = "FRC971-Development-2016"
+
+    # There are two shortcuts for modifying number of CPUs and amount of
+    # memory. Modify them to your liking.
+    vb.cpus = 2
+    vb.memory = 1024 * 2
+  end
+
+  # Use rsync to sync the /vagrant folder.
+  # NOTE: If you change these settings they will not take effect until you
+  # reboot the VM -- i.e. run a "vagrant reload".
+  config.vm.synced_folder ".", "/vagrant", type: "rsync",
+      rsync__exclude: [".git/", ".svn/", "workspace.vdi"], rsync__verbose: true
+
+  # Set up apt and install packages necessary for building the code.
+  config.vm.provision :shell, inline: "/vagrant/setup_apt.sh"
+  config.vm.provision :shell, inline: "/vagrant/setup_extra_storage.sh"
+  config.vm.provision :shell, inline: "/vagrant/setup_code_building.sh"
+  config.vm.provision :shell, inline: "/vagrant/setup_scouting.sh"
+  config.vm.provision :shell, inline: "/vagrant/setup_desktop.sh"
+  config.vm.provision :shell, inline: "/vagrant/setup_misc_packages.sh"
+  config.vm.provision :shell, inline: "/vagrant/setup_vbox_guest_additions.sh"
+
+  # Add a second disk so we have plenty of space to compile the code.
+  config.persistent_storage.enabled = true
+  config.persistent_storage.location = "workspace.vdi"
+  config.persistent_storage.size = 40000 # MiB
+  config.persistent_storage.use_lvm = false
+  config.persistent_storage.filesystem = 'ext4'
+  config.persistent_storage.mountpoint = '/home/user'
+
+  # Forward the scouting app's port.
+  config.vm.network :forwarded_port, guest: 5000, host: 5000, auto_correct: true
+end
diff --git a/vm/setup_apt.sh b/vm/setup_apt.sh
new file mode 100755
index 0000000..d31a372
--- /dev/null
+++ b/vm/setup_apt.sh
@@ -0,0 +1,15 @@
+#!/usr/bin/env bash
+
+set -e
+set -u
+
+export DEBIAN_FRONTEND=noninteractive
+
+# Set up contrib and non-free so we can install some more interesting programs.
+cat > /etc/apt/sources.list.d/contrib.list <<EOT
+deb  http://ftp.us.debian.org/debian/ jessie contrib non-free
+deb-src  http://ftp.us.debian.org/debian/ jessie contrib non-free
+EOT
+
+# Get a list of the latest packages.
+apt-get update
diff --git a/vm/setup_code_building.sh b/vm/setup_code_building.sh
new file mode 100755
index 0000000..a27b60e
--- /dev/null
+++ b/vm/setup_code_building.sh
@@ -0,0 +1,56 @@
+#!/usr/bin/env bash
+
+set -e
+set -u
+
+export DEBIAN_FRONTEND=noninteractive
+
+readonly PKGS=(
+  bazel
+  clang-3.6
+  clang-format-3.5
+  gfortran
+  git
+  libblas-dev
+  liblapack-dev
+  libpython3-dev
+  libpython-dev
+  python3
+  python3-matplotlib
+  python3-numpy
+  python3-scipy
+  python-matplotlib
+  python-scipy
+  resolvconf
+  ruby
+)
+
+# Set up the backports repo.
+cat > /etc/apt/sources.list.d/backports.list <<EOT
+deb http://http.debian.net/debian jessie-backports main
+EOT
+
+# Set up the LLVM repo.
+cat > /etc/apt/sources.list.d/llvm-3.6.list <<EOT
+deb  http://llvm.org/apt/jessie/ llvm-toolchain-jessie-3.6 main
+deb-src  http://llvm.org/apt/jessie/ llvm-toolchain-jessie-3.6 main
+EOT
+
+# Set up the 971-managed bazel repo.
+cat > /etc/apt/sources.list.d/bazel-971.list <<EOT
+deb http://robotics.mvla.net/files/frc971/packages jessie main
+EOT
+
+# Enable user namespace for sandboxing.
+cat > /etc/sysctl.d/99-enable-user-namespaces.conf <<EOT
+kernel.unprivileged_userns_clone = 1
+EOT
+
+# Accept the LLVM GPG key so we can install their packages.
+wget -O - http://llvm.org/apt/llvm-snapshot.gpg.key | apt-key add -
+
+# Install all the packages that we need/want.
+apt-get update
+for pkg in "${PKGS[@]}"; do
+  apt-get install -y -f --force-yes "$pkg"
+done
diff --git a/vm/setup_desktop.sh b/vm/setup_desktop.sh
new file mode 100755
index 0000000..d713856
--- /dev/null
+++ b/vm/setup_desktop.sh
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+
+set -e
+set -u
+
+export DEBIAN_FRONTEND=noninteractive
+
+readonly PKGS=(
+  iceweasel
+  lightdm
+  mousepad
+  xfce4
+  xfce4-terminal
+)
+
+# Install all the packages that we need/want.
+for pkg in "${PKGS[@]}"; do
+  apt-get install -y -f "$pkg"
+done
diff --git a/vm/setup_extra_storage.sh b/vm/setup_extra_storage.sh
new file mode 100755
index 0000000..5fcf4cb
--- /dev/null
+++ b/vm/setup_extra_storage.sh
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+
+set -e
+set -u
+
+readonly EXTRA_USER=user
+readonly EXTRA_STORAGE=/home/"${EXTRA_USER}"
+
+if ! grep -q "$EXTRA_STORAGE" /etc/passwd; then
+  PASSWORD="$(echo "$EXTRA_USER" | mkpasswd -s)"
+  useradd \
+      --home="$EXTRA_STORAGE" -M \
+      --password="$PASSWORD" \
+      --shell=/bin/bash \
+      "$EXTRA_USER"
+  chown "$EXTRA_USER:$EXTRA_USER" "$EXTRA_STORAGE"
+fi
+
+usermod -a -G sudo "$EXTRA_USER"
diff --git a/vm/setup_misc_packages.sh b/vm/setup_misc_packages.sh
new file mode 100755
index 0000000..691b2be
--- /dev/null
+++ b/vm/setup_misc_packages.sh
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+
+set -e
+set -u
+
+export DEBIAN_FRONTEND=noninteractive
+
+readonly PKGS=(
+  colordiff
+  tmux
+  vim
+)
+
+# Install all the packages that we want.
+for pkg in "${PKGS[@]}"; do
+  apt-get install -y -f "$pkg"
+done
diff --git a/vm/setup_scouting.sh b/vm/setup_scouting.sh
new file mode 100755
index 0000000..3360ff5
--- /dev/null
+++ b/vm/setup_scouting.sh
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+
+set -e
+set -u
+
+export DEBIAN_FRONTEND=noninteractive
+
+readonly PKGS=(
+  python3
+  python3-flask
+)
+
+# Install all the packages that we need/want.
+apt-get update
+for pkg in "${PKGS[@]}"; do
+  apt-get install -y -f --force-yes "$pkg"
+done
diff --git a/vm/setup_vbox_guest_additions.sh b/vm/setup_vbox_guest_additions.sh
new file mode 100755
index 0000000..c9f4b09
--- /dev/null
+++ b/vm/setup_vbox_guest_additions.sh
@@ -0,0 +1,15 @@
+#!/usr/bin/env bash
+
+set -e
+set -u
+
+export DEBIAN_FRONTEND=noninteractive
+
+# Install the kernel sources before the guest additions to guarantee that
+# we can compile the kernel module.
+apt-get install -q -y linux-headers-amd64
+
+# Now we can install the guest additions.
+apt-get install -q -y \
+    virtualbox-guest-dkms \
+    virtualbox-guest-x11
diff --git a/y2017/BUILD b/y2017/BUILD
new file mode 100644
index 0000000..aa423a8
--- /dev/null
+++ b/y2017/BUILD
@@ -0,0 +1,17 @@
+cc_library(
+  name = 'constants',
+  visibility = ['//visibility:public'],
+  srcs = [
+    'constants.cc',
+  ],
+  hdrs = [
+    'constants.h',
+  ],
+  deps = [
+    '//aos/common/logging',
+    '//aos/common:once',
+    '//aos/common/network:team_number',
+    '//aos/common:mutex',
+    '//frc971:constants',
+  ],
+)
diff --git a/y2017/actors/BUILD b/y2017/actors/BUILD
new file mode 100644
index 0000000..6766422
--- /dev/null
+++ b/y2017/actors/BUILD
@@ -0,0 +1,50 @@
+package(default_visibility = ['//visibility:public'])
+
+load('/aos/build/queues', 'queue_library')
+
+filegroup(
+  name = 'binaries',
+  srcs = [
+    ':autonomous_action',
+  ],
+)
+
+queue_library(
+  name = 'autonomous_action_queue',
+  srcs = [
+    'autonomous_action.q',
+  ],
+  deps = [
+    '//aos/common/actions:action_queue',
+  ],
+)
+
+cc_library(
+  name = 'autonomous_action_lib',
+  srcs = [
+    'autonomous_actor.cc',
+  ],
+  hdrs = [
+    'autonomous_actor.h',
+  ],
+  deps = [
+    ':autonomous_action_queue',
+    '//aos/common/util:phased_loop',
+    '//aos/common/logging',
+    '//aos/common/actions:action_lib',
+    '//frc971/control_loops/drivetrain:drivetrain_queue',
+    '//frc971/control_loops/drivetrain:drivetrain_config',
+  ],
+)
+
+cc_binary(
+  name = 'autonomous_action',
+  srcs = [
+    'autonomous_actor_main.cc',
+  ],
+  deps = [
+    ':autonomous_action_lib',
+    ':autonomous_action_queue',
+    '//aos/linux_code:init',
+  ],
+)
\ No newline at end of file
diff --git a/y2017/actors/autonomous_action.q b/y2017/actors/autonomous_action.q
new file mode 100644
index 0000000..52873ca
--- /dev/null
+++ b/y2017/actors/autonomous_action.q
@@ -0,0 +1,29 @@
+package y2017.actors;
+
+import "aos/common/actions/actions.q";
+
+message AutonomousMode {
+  // Mode read from the mode setting sensors.
+  int32_t mode;
+};
+
+queue AutonomousMode auto_mode;
+
+struct AutonomousActionParams {
+  // The mode from the sensors when auto starts.
+  int32_t mode;
+};
+
+queue_group AutonomousActionQueueGroup {
+  implements aos.common.actions.ActionQueueGroup;
+
+  message Goal {
+    uint32_t run;
+    AutonomousActionParams params;
+  };
+
+  queue Goal goal;
+  queue aos.common.actions.Status status;
+};
+
+queue_group AutonomousActionQueueGroup autonomous_action;
diff --git a/y2017/actors/autonomous_actor.cc b/y2017/actors/autonomous_actor.cc
new file mode 100644
index 0000000..ad4b87c
--- /dev/null
+++ b/y2017/actors/autonomous_actor.cc
@@ -0,0 +1,82 @@
+#include "y2017/actors/autonomous_actor.h"
+
+#include <inttypes.h>
+
+#include <chrono>
+#include <cmath>
+
+#include "aos/common/util/phased_loop.h"
+#include "aos/common/logging/logging.h"
+
+#include "frc971/control_loops/drivetrain/drivetrain.q.h"
+#include "y2017/actors/autonomous_action.q.h"
+
+namespace y2017 {
+namespace actors {
+using ::frc971::control_loops::drivetrain_queue;
+using ::aos::monotonic_clock;
+namespace chrono = ::std::chrono;
+namespace this_thread = ::std::this_thread;
+
+namespace {
+double DoubleSeconds(monotonic_clock::duration duration) {
+  return ::std::chrono::duration_cast<::std::chrono::duration<double>>(duration)
+      .count();
+}
+}  // namespace
+
+AutonomousActor::AutonomousActor(actors::AutonomousActionQueueGroup *s)
+    : aos::common::actions::ActorBase<actors::AutonomousActionQueueGroup>(s) {}
+
+void AutonomousActor::WaitUntilDoneOrCanceled(
+    ::std::unique_ptr<aos::common::actions::Action> action) {
+  if (!action) {
+    LOG(ERROR, "No action, not waiting\n");
+    return;
+  }
+
+  ::aos::time::PhasedLoop phased_loop(::std::chrono::milliseconds(5),
+                                      ::std::chrono::milliseconds(5) / 2);
+  while (true) {
+    // Poll the running bit and see if we should cancel.
+    phased_loop.SleepUntilNext();
+    if (!action->Running() || ShouldCancel()) {
+      return;
+    }
+  }
+}
+
+bool AutonomousActor::RunAction(const actors::AutonomousActionParams &params) {
+  monotonic_clock::time_point start_time = monotonic_clock::now();
+  LOG(INFO, "Starting autonomous action with mode %" PRId32 "\n", params.mode);
+
+  switch (params.mode) {
+    case 0:
+      break;
+
+    default:
+      LOG(ERROR, "Invalid auto mode %d\n", params.mode);
+      return true;
+  }
+
+  LOG(INFO, "Done %f\n", DoubleSeconds(monotonic_clock::now() - start_time));
+
+  ::aos::time::PhasedLoop phased_loop(::std::chrono::milliseconds(5),
+                                      ::std::chrono::milliseconds(5) / 2);
+
+  while (!ShouldCancel()) {
+    phased_loop.SleepUntilNext();
+  }
+  LOG(DEBUG, "Done running\n");
+
+  return true;
+}
+
+::std::unique_ptr<AutonomousAction> MakeAutonomousAction(
+    const ::y2017::actors::AutonomousActionParams &params) {
+  return ::std::unique_ptr<AutonomousAction>(
+      new AutonomousAction(&::y2017::actors::autonomous_action, params));
+}
+
+}  // namespace actors
+}  // namespace y2017
diff --git a/y2017/actors/autonomous_actor.h b/y2017/actors/autonomous_actor.h
new file mode 100644
index 0000000..ef63b8e
--- /dev/null
+++ b/y2017/actors/autonomous_actor.h
@@ -0,0 +1,38 @@
+#ifndef Y2017_ACTORS_AUTONOMOUS_ACTOR_H_
+#define Y2017_ACTORS_AUTONOMOUS_ACTOR_H_
+
+#include <chrono>
+#include <memory>
+
+#include "aos/common/actions/actions.h"
+#include "aos/common/actions/actor.h"
+#include "frc971/control_loops/drivetrain/drivetrain.q.h"
+#include "frc971/control_loops/drivetrain/drivetrain_config.h"
+#include "y2017/actors/autonomous_action.q.h"
+
+namespace y2017 {
+namespace actors {
+using ::frc971::ProfileParameters;
+
+class AutonomousActor
+    : public ::aos::common::actions::ActorBase<AutonomousActionQueueGroup> {
+ public:
+  explicit AutonomousActor(AutonomousActionQueueGroup *s);
+
+  bool RunAction(const actors::AutonomousActionParams &params) override;
+ private:
+  void WaitUntilDoneOrCanceled(::std::unique_ptr<aos::common::actions::Action>
+      action);
+};
+
+typedef ::aos::common::actions::TypedAction<AutonomousActionQueueGroup>
+    AutonomousAction;
+
+// Makes a new AutonomousActor action.
+::std::unique_ptr<AutonomousAction> MakeAutonomousAction(
+    const ::y2017::actors::AutonomousActionParams &params);
+
+}  // namespace actors
+}  // namespace y2017
+
+#endif  // Y2017_ACTORS_AUTONOMOUS_ACTOR_H_
diff --git a/y2017/actors/autonomous_actor_main.cc b/y2017/actors/autonomous_actor_main.cc
new file mode 100644
index 0000000..21c7e7a
--- /dev/null
+++ b/y2017/actors/autonomous_actor_main.cc
@@ -0,0 +1,16 @@
+#include <stdio.h>
+
+#include "aos/linux_code/init.h"
+#include "y2017/actors/autonomous_action.q.h"
+#include "y2017/actors/autonomous_actor.h"
+
+int main(int /*argc*/, char * /*argv*/ []) {
+  ::aos::Init(-1);
+
+  ::y2017::actors::AutonomousActor autonomous(
+      &::y2017::actors::autonomous_action);
+  autonomous.Run();
+
+  ::aos::Cleanup();
+  return 0;
+}
diff --git a/y2017/constants.cc b/y2017/constants.cc
new file mode 100644
index 0000000..33deee3
--- /dev/null
+++ b/y2017/constants.cc
@@ -0,0 +1,92 @@
+#include "y2017/constants.h"
+
+#include <math.h>
+#include <stdint.h>
+#include <inttypes.h>
+
+#include <map>
+
+#if __has_feature(address_sanitizer)
+#include "sanitizer/lsan_interface.h"
+#endif
+
+#include "aos/common/logging/logging.h"
+#include "aos/common/once.h"
+#include "aos/common/network/team_number.h"
+#include "aos/common/mutex.h"
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+namespace y2017 {
+namespace constants {
+
+// ///// Mutual constants between robots. /////
+const int Values::kZeroingSampleSize;
+
+constexpr double Values::kDrivetrainEncoderRatio;
+
+namespace {
+const uint16_t kCompTeamNumber = 971;
+const uint16_t kPracticeTeamNumber = 9971;
+
+// ///// Dynamic constants. /////
+
+const Values *DoGetValuesForTeam(uint16_t team) {
+  switch (team) {
+    case 1:  // for tests
+      return new Values{
+          5.0,  // drivetrain max speed
+      };
+      break;
+
+    case kCompTeamNumber:
+      return new Values{
+          5.0,  // drivetrain max speed
+      };
+      break;
+
+    case kPracticeTeamNumber:
+      return new Values{
+          5.0,  // drivetrain max speed
+      };
+      break;
+
+    default:
+      LOG(FATAL, "unknown team #%" PRIu16 "\n", team);
+  }
+}
+
+const Values *DoGetValues() {
+  uint16_t team = ::aos::network::GetTeamNumber();
+  LOG(INFO, "creating a Constants for team %" PRIu16 "\n", team);
+  return DoGetValuesForTeam(team);
+}
+
+}  // namespace
+
+const Values &GetValues() {
+  static ::aos::Once<const Values> once(DoGetValues);
+  return *once.Get();
+}
+
+const Values &GetValuesForTeam(uint16_t team_number) {
+  static ::aos::Mutex mutex;
+  ::aos::MutexLocker locker(&mutex);
+
+  // IMPORTANT: This declaration has to stay after the mutex is locked to avoid
+  // race conditions.
+  static ::std::map<uint16_t, const Values *> values;
+
+  if (values.count(team_number) == 0) {
+    values[team_number] = DoGetValuesForTeam(team_number);
+#if __has_feature(address_sanitizer)
+    __lsan_ignore_object(values[team_number]);
+#endif
+  }
+  return *values[team_number];
+}
+
+}  // namespace constants
+}  // namespace y2017
diff --git a/y2017/constants.h b/y2017/constants.h
new file mode 100644
index 0000000..be29a2a
--- /dev/null
+++ b/y2017/constants.h
@@ -0,0 +1,41 @@
+#ifndef Y2017_CONSTANTS_H_
+#define Y2017_CONSTANTS_H_
+
+#include <stdint.h>
+
+namespace y2017 {
+namespace constants {
+
+// Has all of the numbers that change for both robots and makes it easy to
+// retrieve the values for the current one.
+
+// Everything is in SI units (volts, radians, meters, seconds, etc).
+// Some of these values are related to the conversion between raw values
+// (encoder counts, voltage, etc) to scaled units (radians, meters, etc).
+
+// This structure contains current values for all of the things that change.
+struct Values {
+  // ///// Mutual constants between robots. /////
+  // TODO(constants): Update/check these with what we're using this year.
+  static const int kZeroingSampleSize = 200;
+
+  // The ratio from the encoder shaft to the drivetrain wheels.
+  static constexpr double kDrivetrainEncoderRatio =
+      (18.0 / 36.0) /*output reduction*/ * (44.0 / 30.0) /*encoder gears*/;
+
+  // ///// Dynamic constants. /////
+  double drivetrain_max_speed;
+};
+
+// Creates (once) a Values instance for ::aos::network::GetTeamNumber() and
+// returns a reference to it.
+const Values &GetValues();
+
+// Creates Values instances for each team number it is called with and returns
+// them.
+const Values &GetValuesForTeam(uint16_t team_number);
+
+}  // namespace constants
+}  // namespace y2017
+
+#endif  // Y2017_CONSTANTS_H_