Merge "Factored out 1 DOF profiled subsystem from 2016"
diff --git a/NO_BUILD_AMD64 b/NO_BUILD_AMD64
index 3edc6fe..f4faeab 100644
--- a/NO_BUILD_AMD64
+++ b/NO_BUILD_AMD64
@@ -1,6 +1,8 @@
 -//third_party:wpilib
 -//third_party/allwpilib_2016/...
 -//third_party/ntcore_2016/...
+-//third_party/allwpilib_2017/...
+-//third_party/ntcore_2017/...
 -//frc971/wpilib/...
 -//y2012/wpilib/...
 -//y2012:download
diff --git a/WORKSPACE b/WORKSPACE
index 0d4eb2f..01187a5 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -62,16 +62,29 @@
 )
 
 new_http_archive(
-  name = 'allwpilib_ni_libraries_repo',
-  build_file = 'debian/ni-libraries.BUILD',
+  name = 'allwpilib_ni_libraries_repo_2016',
+  build_file = 'debian/ni-libraries-2016.BUILD',
   sha256 = '821687afbee2d7531fb3e47d8d58ac10005695e59685be3ac3aa00b3179faf52',
   url = 'http://frc971.org/Build-Dependencies/allwpilib_ni-libraries_20749ed.tar.gz',
   strip_prefix = 'ni-libraries',
 )
 
 bind(
-  name = 'ni-libraries',
-  actual = '@allwpilib_ni_libraries_repo//:ni-libraries',
+  name = 'ni-libraries-2016',
+  actual = '@allwpilib_ni_libraries_repo_2016//:ni-libraries',
+)
+
+new_http_archive(
+  name = 'allwpilib_ni_libraries_repo_2017',
+  build_file = 'debian/ni-libraries-2017.BUILD',
+  sha256 = '67c1ad365fb712cc0acb0bf43465b831030523dc6f88daa02626994f644d91eb',
+  url = 'http://frc971.org/Build-Dependencies/allwpilib_ni-libraries_e375b4a.tar.gz',
+  strip_prefix = 'ni-libraries',
+)
+
+bind(
+  name = 'ni-libraries-2017',
+  actual = '@allwpilib_ni_libraries_repo_2017//:ni-libraries',
 )
 
 # Downloaded from:
diff --git a/aos/vision/blob/BUILD b/aos/vision/blob/BUILD
index 8cb4373..f823cc0 100644
--- a/aos/vision/blob/BUILD
+++ b/aos/vision/blob/BUILD
@@ -1,3 +1,4 @@
+load('/tools/build_rules/gtk_dependent', 'gtk_dependent_cc_binary', 'gtk_dependent_cc_library')
 package(default_visibility = ['//visibility:public'])
 
 cc_library(
@@ -93,7 +94,7 @@
   ],
 )
 
-cc_library(
+gtk_dependent_cc_library(
   name = 'stream_view',
   hdrs = ['stream_view.h'],
   deps = [
diff --git a/aos/vision/debug/BUILD b/aos/vision/debug/BUILD
index 91111b2..3b4fa7c 100644
--- a/aos/vision/debug/BUILD
+++ b/aos/vision/debug/BUILD
@@ -1,4 +1,4 @@
-#!/usr/bin/python3
+load('/tools/build_rules/gtk_dependent', 'gtk_dependent_cc_binary', 'gtk_dependent_cc_library')
 package(default_visibility = ["//visibility:public"])
 
 cc_library(
@@ -11,7 +11,7 @@
         ],
 )
 
-cc_library(name = "debug_viewer",
+gtk_dependent_cc_library(name = "debug_viewer",
     srcs = ["debug_viewer.cc"],
     hdrs = ["debug_viewer.h"],
     deps = [
diff --git a/aos/vision/events/udp_test.cc b/aos/vision/events/udp_test.cc
index 7e77b68..33fb4d6 100644
--- a/aos/vision/events/udp_test.cc
+++ b/aos/vision/events/udp_test.cc
@@ -11,8 +11,8 @@
 
   int txdata[] = {1, 2, 3, 4};
   int rxdata[4];
-  tx.Send(static_cast<const void *>(&txdata), sizeof(txdata));
-  rx.Recv(static_cast<void *>(&rxdata), sizeof(rxdata));
+  tx.Send(reinterpret_cast<const char *>(&txdata), sizeof(txdata));
+  rx.Recv(reinterpret_cast<char *>(&rxdata), sizeof(rxdata));
   EXPECT_EQ(txdata[0], rxdata[0]);
   EXPECT_EQ(txdata[1], rxdata[1]);
   EXPECT_EQ(txdata[2], rxdata[2]);
diff --git a/debian/ni-libraries.BUILD b/debian/ni-libraries-2016.BUILD
similarity index 100%
rename from debian/ni-libraries.BUILD
rename to debian/ni-libraries-2016.BUILD
diff --git a/debian/ni-libraries-2017.BUILD b/debian/ni-libraries-2017.BUILD
new file mode 100644
index 0000000..9d79098
--- /dev/null
+++ b/debian/ni-libraries-2017.BUILD
@@ -0,0 +1,20 @@
+cc_library(
+  name = 'ni-libraries',
+  visibility = ['//visibility:public'],
+  srcs = [
+    'lib/libFRC_NetworkCommunication.so.17.0.0',
+    'lib/libRoboRIO_FRC_ChipObject.so.17.0.0',
+    'lib/libNiFpgaLv.so.16.0.0',
+    'lib/libNiFpga.so.16.0.0',
+    'lib/libNiRioSrv.so.16.0.0',
+    'lib/libspi.so.1.0.0',
+    'lib/libi2c.so.2.0.0',
+    'lib/libniriosession.so.16.0.0',
+    'lib/libniriodevenum.so.16.0.0',
+  ],
+  includes = [
+    'include',
+  ],
+  hdrs = glob(['include/**']),
+  linkstatic = True,
+)
diff --git a/debian/usr.BUILD b/debian/usr.BUILD
index 403d069..e34e933 100644
--- a/debian/usr.BUILD
+++ b/debian/usr.BUILD
@@ -1,3 +1,5 @@
+load('/tools/build_rules/gtk_dependent', 'gtk_dependent_cc_binary', 'gtk_dependent_cc_library')
+
 package(default_visibility = ['@//debian:__pkg__'])
 
 cc_library(
@@ -87,7 +89,7 @@
   visibility = ['//visibility:public'],
 )
 
-cc_library(
+gtk_dependent_cc_library(
   name = 'gtk+-3.0',
   hdrs = glob([
     'include/gtk-3.0/**/*.h',
diff --git a/doc/allwpilib_ni-libraries_tarball.sh b/doc/allwpilib_ni-libraries_tarball.sh
index 61fccd4..07058a0 100755
--- a/doc/allwpilib_ni-libraries_tarball.sh
+++ b/doc/allwpilib_ni-libraries_tarball.sh
@@ -3,8 +3,7 @@
 # A script to generate a allwpilib_ni-libraries_bla.tar.gz file from a given
 # revision of allwpilib.
 
-# Example: ./doc/allwpilib_ni-libraries_tarball.sh \
-#   https://usfirst.collab.net/gerrit/allwpilib master
+# Example: `./doc/allwpilib_ni-libraries_tarball.sh https://github.com/wpilibsuite/allwpilib master`
 
 set -e
 set -u
diff --git a/doc/allwpilib_subtree.sh b/doc/allwpilib_subtree.sh
index aa83d0c..7f79dc0 100755
--- a/doc/allwpilib_subtree.sh
+++ b/doc/allwpilib_subtree.sh
@@ -7,8 +7,7 @@
 # The implementation running `git filter-branch` over allwpilib's entire history
 # isn't the fastest thing ever, but it's not all that bad.
 
-# Example: ./doc/allwpilib_subtree.sh add third_party/allwpilib_2016 \
-#   https://usfirst.collab.net/gerrit/allwpilib master
+# Example: `./doc/allwpilib_subtree.sh add third_party/allwpilib_2017 https://github.com/wpilibsuite/allwpilib master`
 
 set -e
 set -u
@@ -26,8 +25,18 @@
 
 git fetch "${REMOTE}" "${REF}"
 
-readonly REMOVE_DIRECTORIES="ni-libraries wpilibj wpilibjIntegrationTests gradle"
-readonly TREE_FILTER="$(for d in ${REMOVE_DIRECTORIES}; do
+readonly REMOVE_DIRECTORIES=(
+ni-libraries
+wpilibj
+wpilibjIntegrationTests
+gradle
+simulation
+myRobot
+myRobotCpp
+gen
+test-scripts
+)
+readonly TREE_FILTER="$(for d in "${REMOVE_DIRECTORIES[@]}"}; do
   echo "if [ -d $d ]; then git rm -rf $d; fi && "
 done)"
 git filter-branch --tree-filter "${TREE_FILTER}true" FETCH_HEAD
diff --git a/frc971/constants.h b/frc971/constants.h
index ad3ce71..0da9c96 100644
--- a/frc971/constants.h
+++ b/frc971/constants.h
@@ -16,6 +16,19 @@
   double allowable_encoder_error;
 };
 
+struct EncoderPlusIndexZeroingConstants {
+  // The amount of index pulses in the limb's range of motion.
+  int num_index_pulses;
+};
+
+struct PotAndAbsoluteEncoderZeroingConstants {
+  // The distance that the absolute encoder needs to complete a full rotation.
+  double abs_duration;
+  // Sample mechanism angle and absolute encoder value.
+  double sample_abs_value;
+  double sample_degrees;
+};
+
 // Defines a range of motion for a subsystem.
 // These are all absolute positions in scaled units.
 struct Range {
diff --git a/frc971/control_loops/control_loops.q b/frc971/control_loops/control_loops.q
index 05a660b..f13d82b 100644
--- a/frc971/control_loops/control_loops.q
+++ b/frc971/control_loops/control_loops.q
@@ -1,5 +1,20 @@
 package frc971;
 
+// Represents all of the data for a single indexed encoder. In other words,
+// just a relative encoder with an index pulse.
+// The units on all of the positions are the same.
+// All encoder values are relative to where the encoder was at some arbitrary
+// point in time. All potentiometer values are relative to some arbitrary 0
+// position which varies with each robot.
+struct IndexPosition {
+  // Current position read from the encoder.
+  double encoder;
+  // Position from the encoder latched at the last index pulse.
+  double latched_encoder;
+  // How many index pulses we've seen since startup. Starts at 0.
+  uint32_t index_pulses;
+};
+
 // Represents all of the data for a single potentiometer and indexed encoder
 // pair.
 // The units on all of the positions are the same.
diff --git a/frc971/control_loops/position_sensor_sim.cc b/frc971/control_loops/position_sensor_sim.cc
index 6075155..1d06974 100644
--- a/frc971/control_loops/position_sensor_sim.cc
+++ b/frc971/control_loops/position_sensor_sim.cc
@@ -27,6 +27,33 @@
  *                   A
  *                   |
  *              index pulse
+ *
+ *
+ *
+ * Absolute encoder explanation:
+ *
+ * If we were to graph the output of an absolute encoder that resets every 0.1
+ * meters for example, it would looks something like the following. The y-axis
+ * represents the output of the absolute encoder. The x-axis represents the
+ * actual position of the robot's mechanism.
+ *
+ *          1 encoder segment
+ *              +------+
+ *
+ *      |
+ *  0.1 +      /|     /|     /|     /|     /|     /|     /|     /|
+ *      |     / |    / |    / |    / |    / |    / |    / |    / |
+ *      |    /  |   /  |   /  |   /  |   /  |   /  |   /  |   /  |
+ *      |   /   |  /   |  /   |  /   |  /   |  /   |  /   |  /   |
+ *      |  /    | /    | /    | /    | /    | /    | /    | /    |
+ *      | /     |/     |/     |/     |/     |/     |/     |/     |
+ *  0.0 ++------+------+------+------+------+------+------+------+----
+ *      0.05   0.15   0.25   0.35   0.45   0.55   0.65   0.75   0.85
+ *
+ * An absolute encoder can be used to determine exactly where the mechanism in
+ * question is within a certain segment. As long as you know a single combo of
+ * absolute encoder reading and mechanism location you can extrapolate the
+ * remainder of the graph.
  */
 
 PositionSensorSimulator::PositionSensorSimulator(double index_diff,
@@ -35,12 +62,14 @@
   Initialize(0.0, 0.0);
 }
 
-void PositionSensorSimulator::Initialize(double start_position,
-                                         double pot_noise_stddev,
-                                         double known_index_pos /* = 0*/) {
+void PositionSensorSimulator::Initialize(
+    double start_position, double pot_noise_stddev,
+    double known_index_pos /* = 0*/,
+    double known_absolute_encoder_pos /* = 0*/) {
   // We're going to make the index pulse we know "segment zero".
   cur_index_segment_ = floor((start_position - known_index_pos) / index_diff_);
   known_index_pos_ = known_index_pos;
+  known_absolute_encoder_ = known_absolute_encoder_pos;
   cur_index_ = 0;
   index_count_ = 0;
   cur_pos_ = start_position;
@@ -79,6 +108,22 @@
   cur_pos_ = new_pos;
 }
 
+void PositionSensorSimulator::GetSensorValues(IndexPosition *values) {
+  values->encoder = cur_pos_ - start_position_;
+
+  if (index_count_ == 0) {
+    values->latched_encoder = 0.0;
+  } else {
+    // Determine the position of the index pulse relative to absolute zero.
+    double index_pulse_position = cur_index_ * index_diff_ + known_index_pos_;
+
+    // Populate the latched encoder samples.
+    values->latched_encoder = index_pulse_position - start_position_;
+  }
+
+  values->index_pulses = index_count_;
+}
+
 void PositionSensorSimulator::GetSensorValues(PotAndIndexPosition *values) {
   values->pot = pot_noise_.AddNoiseToSample(cur_pos_);
   values->encoder = cur_pos_ - start_position_;
@@ -98,5 +143,15 @@
   values->index_pulses = index_count_;
 }
 
+void PositionSensorSimulator::GetSensorValues(PotAndAbsolutePosition *values) {
+  values->pot = pot_noise_.AddNoiseToSample(cur_pos_);
+  values->relative_encoder = cur_pos_ - start_position_;
+  // TODO(phil): Create some lag here since this is a PWM signal it won't be
+  // instantaneous like the other signals. Better yet, its lag varies
+  // randomly with the distribution varying depending on the reading.
+  values->absolute_encoder =
+      fmod(cur_pos_ - known_index_pos_ + known_absolute_encoder_, index_diff_);
+}
+
 }  // namespace control_loops
 }  // namespace frc971
diff --git a/frc971/control_loops/position_sensor_sim.h b/frc971/control_loops/position_sensor_sim.h
index 3ce3056..3de96c1 100644
--- a/frc971/control_loops/position_sensor_sim.h
+++ b/frc971/control_loops/position_sensor_sim.h
@@ -17,6 +17,9 @@
   // index_diff: The interval between index pulses. This is measured in SI
   //             units. For example, if an index pulse hits every 5cm on the
   //             elevator, set this to 0.05.
+  //             NOTE: When retrieving the sensor values for a
+  //             PotAndAbsolutePosition message this field represents the
+  //             interval between when the absolute encoder reads 0.
   // noise_seed: The seed to feed into the random number generator for the
   //             potentiometer values.
   // TODO(danielp): Allow for starting with a non-zero encoder value.
@@ -34,7 +37,8 @@
   // known_index_pos: The absolute position of an index pulse.
   void Initialize(double start_position,
                   double pot_noise_stddev,
-                  double known_index_pos = 0.0);
+                  double known_index_pos = 0.0,
+                  double known_absolute_encoder_pos = 0.0);
 
   // Simulate the structure moving to a new position. The new value is measured
   // relative to absolute zero. This will update the simulated sensors with new
@@ -46,8 +50,20 @@
   // values: The target structure will be populated with simulated sensor
   //         readings. The readings will be in SI units. For example the units
   //         can be given in radians, meters, etc.
+  void GetSensorValues(IndexPosition* values);
+
+  // Get the current values of the simulated sensors.
+  // values: The target structure will be populated with simulated sensor
+  //         readings. The readings will be in SI units. For example the units
+  //         can be given in radians, meters, etc.
   void GetSensorValues(PotAndIndexPosition* values);
 
+  // Get the current values of the simulated sensors.
+  // values: The target structure will be populated with simulated sensor
+  //         readings. The readings will be in SI units. For example the units
+  //         can be given in radians, meters, etc.
+  void GetSensorValues(PotAndAbsolutePosition* values);
+
  private:
   // The absolute segment between two index pulses the simulation is on. For
   // example, when the current position is betwen index pulse zero and one,
@@ -65,7 +81,13 @@
   // Distance between index pulses on the mechanism.
   double index_diff_;
   // Absolute position of a known index pulse.
+  // OR
+  // Absolute position of the absolute encoder's reading stored in
+  // known_absolute_encoder_.
   double known_index_pos_;
+  // The readout of the absolute encoder when the robot's mechanism is at
+  // known_index_pos_.
+  double known_absolute_encoder_;
   // Current position of the mechanism relative to absolute zero.
   double cur_pos_;
   // Starting position of the mechanism relative to absolute zero. See the
diff --git a/frc971/control_loops/position_sensor_sim_test.cc b/frc971/control_loops/position_sensor_sim_test.cc
index 1ca5033..0c7fa0e 100644
--- a/frc971/control_loops/position_sensor_sim_test.cc
+++ b/frc971/control_loops/position_sensor_sim_test.cc
@@ -23,37 +23,46 @@
   // this test is to verify that no false index pulses are generated while the
   // mechanism stays between two index pulses.
   const double index_diff = 0.5;
-  PotAndIndexPosition position;
+  IndexPosition index_position;
+  PotAndIndexPosition pot_and_index_position;
   PositionSensorSimulator sim(index_diff);
   sim.Initialize(3.6 * index_diff, 0);
 
   // Make sure that we don't accidentally hit an index pulse.
   for (int i = 0; i < 30; i++) {
     sim.MoveTo(3.6 * index_diff);
-    sim.GetSensorValues(&position);
-    ASSERT_DOUBLE_EQ(3.6 * index_diff, position.pot);
-    ASSERT_EQ(0u, position.index_pulses);
+    sim.GetSensorValues(&index_position);
+    sim.GetSensorValues(&pot_and_index_position);
+    ASSERT_DOUBLE_EQ(3.6 * index_diff, pot_and_index_position.pot);
+    ASSERT_EQ(0u, pot_and_index_position.index_pulses);
+    ASSERT_EQ(0u, index_position.index_pulses);
   }
 
   for (int i = 0; i < 30; i++) {
     sim.MoveTo(3.0 * index_diff);
-    sim.GetSensorValues(&position);
-    ASSERT_DOUBLE_EQ(3.0 * index_diff, position.pot);
-    ASSERT_EQ(0u, position.index_pulses);
+    sim.GetSensorValues(&index_position);
+    sim.GetSensorValues(&pot_and_index_position);
+    ASSERT_DOUBLE_EQ(3.0 * index_diff, pot_and_index_position.pot);
+    ASSERT_EQ(0u, pot_and_index_position.index_pulses);
+    ASSERT_EQ(0u, index_position.index_pulses);
   }
 
   for (int i = 0; i < 30; i++) {
     sim.MoveTo(3.99 * index_diff);
-    sim.GetSensorValues(&position);
-    ASSERT_DOUBLE_EQ(3.99 * index_diff, position.pot);
-    ASSERT_EQ(0u, position.index_pulses);
+    sim.GetSensorValues(&index_position);
+    sim.GetSensorValues(&pot_and_index_position);
+    ASSERT_DOUBLE_EQ(3.99 * index_diff, pot_and_index_position.pot);
+    ASSERT_EQ(0u, pot_and_index_position.index_pulses);
+    ASSERT_EQ(0u, index_position.index_pulses);
   }
 
   for (int i = 0; i < 30; i++) {
     sim.MoveTo(3.0 * index_diff);
-    sim.GetSensorValues(&position);
-    ASSERT_DOUBLE_EQ(3.0 * index_diff, position.pot);
-    ASSERT_EQ(0u, position.index_pulses);
+    sim.GetSensorValues(&index_position);
+    sim.GetSensorValues(&pot_and_index_position);
+    ASSERT_DOUBLE_EQ(3.0 * index_diff, pot_and_index_position.pot);
+    ASSERT_EQ(0u, pot_and_index_position.index_pulses);
+    ASSERT_EQ(0u, index_position.index_pulses);
   }
 }
 
@@ -63,43 +72,58 @@
   // again simulate zero noise on the potentiometer to accurately verify the
   // mechanism's position during the index pulses.
   const double index_diff = 0.8;
-  PotAndIndexPosition position;
+  IndexPosition index_position;
+  PotAndIndexPosition pot_and_index_position;
   PositionSensorSimulator sim(index_diff);
   sim.Initialize(4.6 * index_diff, 0);
 
   // Make sure that we get an index pulse on every transition.
-  sim.GetSensorValues(&position);
-  ASSERT_EQ(0u, position.index_pulses);
+  sim.GetSensorValues(&index_position);
+  sim.GetSensorValues(&pot_and_index_position);
+  ASSERT_EQ(0u, index_position.index_pulses);
+  ASSERT_EQ(0u, pot_and_index_position.index_pulses);
 
   sim.MoveTo(3.6 * index_diff);
-  sim.GetSensorValues(&position);
-  ASSERT_DOUBLE_EQ(4.0 * index_diff, position.latched_pot);
-  ASSERT_EQ(1u, position.index_pulses);
+  sim.GetSensorValues(&index_position);
+  sim.GetSensorValues(&pot_and_index_position);
+  ASSERT_DOUBLE_EQ(4.0 * index_diff, pot_and_index_position.latched_pot);
+  ASSERT_EQ(1u, index_position.index_pulses);
+  ASSERT_EQ(1u, pot_and_index_position.index_pulses);
 
   sim.MoveTo(4.5 * index_diff);
-  sim.GetSensorValues(&position);
-  ASSERT_DOUBLE_EQ(4.0 * index_diff, position.latched_pot);
-  ASSERT_EQ(2u, position.index_pulses);
+  sim.GetSensorValues(&index_position);
+  sim.GetSensorValues(&pot_and_index_position);
+  ASSERT_DOUBLE_EQ(4.0 * index_diff, pot_and_index_position.latched_pot);
+  ASSERT_EQ(2u, index_position.index_pulses);
+  ASSERT_EQ(2u, pot_and_index_position.index_pulses);
 
   sim.MoveTo(5.9 * index_diff);
-  sim.GetSensorValues(&position);
-  ASSERT_DOUBLE_EQ(5.0 * index_diff, position.latched_pot);
-  ASSERT_EQ(3u, position.index_pulses);
+  sim.GetSensorValues(&index_position);
+  sim.GetSensorValues(&pot_and_index_position);
+  ASSERT_DOUBLE_EQ(5.0 * index_diff, pot_and_index_position.latched_pot);
+  ASSERT_EQ(3u, index_position.index_pulses);
+  ASSERT_EQ(3u, pot_and_index_position.index_pulses);
 
   sim.MoveTo(6.1 * index_diff);
-  sim.GetSensorValues(&position);
-  ASSERT_DOUBLE_EQ(6.0 * index_diff, position.latched_pot);
-  ASSERT_EQ(4u, position.index_pulses);
+  sim.GetSensorValues(&index_position);
+  sim.GetSensorValues(&pot_and_index_position);
+  ASSERT_DOUBLE_EQ(6.0 * index_diff, pot_and_index_position.latched_pot);
+  ASSERT_EQ(4u, index_position.index_pulses);
+  ASSERT_EQ(4u, pot_and_index_position.index_pulses);
 
   sim.MoveTo(8.7 * index_diff);
-  sim.GetSensorValues(&position);
-  ASSERT_DOUBLE_EQ(8.0 * index_diff, position.latched_pot);
-  ASSERT_EQ(5u, position.index_pulses);
+  sim.GetSensorValues(&index_position);
+  sim.GetSensorValues(&pot_and_index_position);
+  ASSERT_DOUBLE_EQ(8.0 * index_diff, pot_and_index_position.latched_pot);
+  ASSERT_EQ(5u, index_position.index_pulses);
+  ASSERT_EQ(5u, pot_and_index_position.index_pulses);
 
   sim.MoveTo(7.3 * index_diff);
-  sim.GetSensorValues(&position);
-  ASSERT_DOUBLE_EQ(8.0 * index_diff, position.latched_pot);
-  ASSERT_EQ(6u, position.index_pulses);
+  sim.GetSensorValues(&index_position);
+  sim.GetSensorValues(&pot_and_index_position);
+  ASSERT_DOUBLE_EQ(8.0 * index_diff, pot_and_index_position.latched_pot);
+  ASSERT_EQ(6u, index_position.index_pulses);
+  ASSERT_EQ(6u, pot_and_index_position.index_pulses);
 }
 
 // Tests that the simulator handles non-zero specified index pulse locations
@@ -108,46 +132,65 @@
   const double index_diff = 0.5;
   PositionSensorSimulator sim(index_diff);
   sim.Initialize(index_diff * 0.25, 0.0, index_diff * 0.5);
-  PotAndIndexPosition position;
+  IndexPosition index_position;
+  PotAndIndexPosition pot_and_index_position;
 
   sim.MoveTo(0.75 * index_diff);
-  sim.GetSensorValues(&position);
-  EXPECT_EQ(1u, position.index_pulses);
-  EXPECT_DOUBLE_EQ(index_diff * 0.5, position.latched_pot);
-  EXPECT_DOUBLE_EQ(index_diff * 0.25, position.latched_encoder);
+  sim.GetSensorValues(&index_position);
+  sim.GetSensorValues(&pot_and_index_position);
+  EXPECT_EQ(1u, index_position.index_pulses);
+  EXPECT_EQ(1u, pot_and_index_position.index_pulses);
+  EXPECT_DOUBLE_EQ(index_diff * 0.5, pot_and_index_position.latched_pot);
+  EXPECT_DOUBLE_EQ(index_diff * 0.25, index_position.latched_encoder);
+  EXPECT_DOUBLE_EQ(index_diff * 0.25, pot_and_index_position.latched_encoder);
 
   sim.MoveTo(index_diff);
-  sim.GetSensorValues(&position);
-  EXPECT_EQ(1u, position.index_pulses);
-  EXPECT_DOUBLE_EQ(index_diff * 0.5, position.latched_pot);
-  EXPECT_DOUBLE_EQ(index_diff * 0.25, position.latched_encoder);
+  sim.GetSensorValues(&index_position);
+  sim.GetSensorValues(&pot_and_index_position);
+  EXPECT_EQ(1u, index_position.index_pulses);
+  EXPECT_EQ(1u, pot_and_index_position.index_pulses);
+  EXPECT_DOUBLE_EQ(index_diff * 0.5, pot_and_index_position.latched_pot);
+  EXPECT_DOUBLE_EQ(index_diff * 0.25, index_position.latched_encoder);
+  EXPECT_DOUBLE_EQ(index_diff * 0.25, pot_and_index_position.latched_encoder);
 
   sim.MoveTo(1.75 * index_diff);
-  sim.GetSensorValues(&position);
-  EXPECT_EQ(2u, position.index_pulses);
-  EXPECT_DOUBLE_EQ(index_diff * 1.5, position.latched_pot);
-  EXPECT_DOUBLE_EQ(index_diff * 1.25, position.latched_encoder);
+  sim.GetSensorValues(&index_position);
+  sim.GetSensorValues(&pot_and_index_position);
+  EXPECT_EQ(2u, index_position.index_pulses);
+  EXPECT_EQ(2u, pot_and_index_position.index_pulses);
+  EXPECT_DOUBLE_EQ(index_diff * 1.5, pot_and_index_position.latched_pot);
+  EXPECT_DOUBLE_EQ(index_diff * 1.25, index_position.latched_encoder);
+  EXPECT_DOUBLE_EQ(index_diff * 1.25, pot_and_index_position.latched_encoder);
 
   // Try it with our known index pulse not being our first one.
   sim.Initialize(index_diff * 0.25, 0.0, index_diff * 1.5);
 
   sim.MoveTo(0.75 * index_diff);
-  sim.GetSensorValues(&position);
-  EXPECT_EQ(1u, position.index_pulses);
-  EXPECT_DOUBLE_EQ(index_diff * 0.5, position.latched_pot);
-  EXPECT_DOUBLE_EQ(index_diff * 0.25, position.latched_encoder);
+  sim.GetSensorValues(&index_position);
+  sim.GetSensorValues(&pot_and_index_position);
+  EXPECT_EQ(1u, index_position.index_pulses);
+  EXPECT_EQ(1u, pot_and_index_position.index_pulses);
+  EXPECT_DOUBLE_EQ(index_diff * 0.5, pot_and_index_position.latched_pot);
+  EXPECT_DOUBLE_EQ(index_diff * 0.25, index_position.latched_encoder);
+  EXPECT_DOUBLE_EQ(index_diff * 0.25, pot_and_index_position.latched_encoder);
 
   sim.MoveTo(index_diff);
-  sim.GetSensorValues(&position);
-  EXPECT_EQ(1u, position.index_pulses);
-  EXPECT_DOUBLE_EQ(index_diff * 0.5, position.latched_pot);
-  EXPECT_DOUBLE_EQ(index_diff * 0.25, position.latched_encoder);
+  sim.GetSensorValues(&index_position);
+  sim.GetSensorValues(&pot_and_index_position);
+  EXPECT_EQ(1u, index_position.index_pulses);
+  EXPECT_EQ(1u, pot_and_index_position.index_pulses);
+  EXPECT_DOUBLE_EQ(index_diff * 0.5, pot_and_index_position.latched_pot);
+  EXPECT_DOUBLE_EQ(index_diff * 0.25, index_position.latched_encoder);
+  EXPECT_DOUBLE_EQ(index_diff * 0.25, pot_and_index_position.latched_encoder);
 
   sim.MoveTo(1.75 * index_diff);
-  sim.GetSensorValues(&position);
-  EXPECT_EQ(2u, position.index_pulses);
-  EXPECT_DOUBLE_EQ(index_diff * 1.5, position.latched_pot);
-  EXPECT_DOUBLE_EQ(index_diff * 1.25, position.latched_encoder);
+  sim.GetSensorValues(&index_position);
+  sim.GetSensorValues(&pot_and_index_position);
+  EXPECT_EQ(2u, index_position.index_pulses);
+  EXPECT_EQ(2u, pot_and_index_position.index_pulses);
+  EXPECT_DOUBLE_EQ(index_diff * 1.5, pot_and_index_position.latched_pot);
+  EXPECT_DOUBLE_EQ(index_diff * 1.25, index_position.latched_encoder);
+  EXPECT_DOUBLE_EQ(index_diff * 1.25, pot_and_index_position.latched_encoder);
 }
 
 // Tests that the latched values update correctly.
@@ -195,5 +238,70 @@
   EXPECT_DOUBLE_EQ(index_diff, position.latched_encoder);
 }
 
+// This test makes sure that our simulation for an absolute encoder + relative
+// encoder + pot combo works OK. Let's pretend that we know that a reading of
+// 0.07m on the absolute encoder corresponds to 0.2m on the robot. We also know
+// that every 0.1m the absolute encoder resets. Then we can construct a table
+// quickly from there:
+//
+// abs_encoder | robot
+//     0.07m   |  0.20m
+//     0.07m   |  0.30m
+//     0.07m   |  0.40m
+//     0.01m   |  0.34m
+//     0.01m   |  0.24m
+//     0.00m   |  0.23m
+//     0.00m   |  0.13m
+//
+// Since the absolute encoder wraps around, we'll notice that the same reading
+// can correspond to multiple positions on the robot.
+//
+// NOTE: We use EXPECT_NEAR rather than EXPECT_DOUBLE_EQ for the absolute
+// encoder because the modulo operation inside the simulator introduces just
+// enough imprecision to fail the EXPECT_DOUBLE_EQ macro.
+TEST_F(PositionSensorSimTest, PotAndEncodersNoIndexPulse) {
+  const double index_diff = 0.1;
+  PositionSensorSimulator sim(index_diff);
+  sim.Initialize(0.20, 0.05, 0.2, 0.07);
+  PotAndAbsolutePosition position;
+
+  sim.MoveTo(0.20);
+  sim.GetSensorValues(&position);
+  EXPECT_DOUBLE_EQ(0.00, position.relative_encoder);
+  EXPECT_NEAR(0.07, position.absolute_encoder, 0.00000001);
+
+  sim.MoveTo(0.30);
+  sim.GetSensorValues(&position);
+  EXPECT_DOUBLE_EQ(0.10, position.relative_encoder);
+  EXPECT_NEAR(0.07, position.absolute_encoder, 0.00000001);
+
+  sim.MoveTo(0.40);
+  sim.GetSensorValues(&position);
+  EXPECT_DOUBLE_EQ(0.20, position.relative_encoder);
+  EXPECT_NEAR(0.07, position.absolute_encoder, 0.00000001);
+
+  sim.MoveTo(0.34);
+  sim.GetSensorValues(&position);
+  EXPECT_DOUBLE_EQ(0.14, position.relative_encoder);
+  EXPECT_NEAR(0.01, position.absolute_encoder, 0.00000001);
+
+  sim.MoveTo(0.24);
+  sim.GetSensorValues(&position);
+  EXPECT_DOUBLE_EQ(0.04, position.relative_encoder);
+  EXPECT_NEAR(0.01, position.absolute_encoder, 0.00000001);
+
+  sim.MoveTo(0.23);
+  sim.GetSensorValues(&position);
+  EXPECT_DOUBLE_EQ(0.03, position.relative_encoder);
+  EXPECT_NEAR(0.00, position.absolute_encoder, 0.00000001);
+
+  sim.MoveTo(0.13);
+  sim.GetSensorValues(&position);
+  EXPECT_DOUBLE_EQ(-0.07, position.relative_encoder);
+  EXPECT_NEAR(0.00, position.absolute_encoder, 0.00000001);
+
+  // TODO(philipp): Test negative values.
+}
+
 }  // namespace control_loops
 }  // namespace frc971
diff --git a/frc971/wpilib/buffered_pcm.cc b/frc971/wpilib/buffered_pcm.cc
index 35df795..2ddbfa7 100644
--- a/frc971/wpilib/buffered_pcm.cc
+++ b/frc971/wpilib/buffered_pcm.cc
@@ -22,8 +22,8 @@
 
 void BufferedPcm::Flush() {
   LOG(DEBUG, "sending solenoids 0x%" PRIx8 "\n", values_);
-#ifdef WPILIB2015
-  SolenoidBase::Set(values_, 0xFF);
+#ifdef WPILIB2017
+  SolenoidBase::SetAll(m_moduleNumber, values_);
 #else
   SolenoidBase::Set(values_, 0xFF, m_moduleNumber);
 #endif
diff --git a/frc971/wpilib/dma.cc b/frc971/wpilib/dma.cc
index ea9f64c..dbbfc56 100644
--- a/frc971/wpilib/dma.cc
+++ b/frc971/wpilib/dma.cc
@@ -8,6 +8,9 @@
 #include "DigitalSource.h"
 #include "AnalogInput.h"
 #include "Encoder.h"
+#ifdef WPILIB2017
+#include "HAL/HAL.h"
+#endif
 
 // Interface to the roboRIO FPGA's DMA features.
 
@@ -22,14 +25,13 @@
   };
 } t1Output;
 
-static const uint32_t kNumHeaders = 10;
+static const int32_t kNumHeaders = 10;
 
-#ifdef WPILIB2015
-static constexpr ssize_t kChannelSize[18] = {2, 2, 4, 4, 2, 2, 4, 4, 3, 3,
-                                             2, 1, 4, 4, 4, 4, 4, 4};
-#else
 static constexpr ssize_t kChannelSize[20] = {2, 2, 4, 4, 2, 2, 4, 4, 3, 3,
                                              2, 1, 4, 4, 4, 4, 4, 4, 4, 4};
+
+#ifndef WPILIB2017
+#define HAL_GetErrorMessage getHALErrorMessage
 #endif
 
 enum DMAOffsetConstants {
@@ -49,25 +51,17 @@
   kEnable_Counters_High = 13,
   kEnable_CounterTimers_Low = 14,
   kEnable_CounterTimers_High = 15,
-#ifdef WPILIB2015
-  kEnable_Encoders = 16,
-  kEnable_EncoderTimers = 17,
-#else
   kEnable_Encoders_Low = 16,
   kEnable_Encoders_High = 17,
   kEnable_EncoderTimers_Low = 18,
   kEnable_EncoderTimers_High = 19,
-#endif
 };
 
 DMA::DMA() {
   tRioStatusCode status = 0;
   tdma_config_ = tDMA::create(&status);
   tdma_config_->writeConfig_ExternalClock(false, &status);
-  wpi_setErrorWithContext(status, getHALErrorMessage(status));
-#ifdef WPILIB2015
-  NiFpga_WriteU32(0x10000, 0x1832c, 0x0);
-#endif
+  wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
   if (status != 0) {
     return;
   }
@@ -85,7 +79,7 @@
 void DMA::SetPause(bool pause) {
   tRioStatusCode status = 0;
   tdma_config_->writeConfig_Pause(pause, &status);
-  wpi_setErrorWithContext(status, getHALErrorMessage(status));
+  wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
 }
 
 void DMA::SetRate(uint32_t cycles) {
@@ -94,7 +88,7 @@
   }
   tRioStatusCode status = 0;
   tdma_config_->writeRate(cycles, &status);
-  wpi_setErrorWithContext(status, getHALErrorMessage(status));
+  wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
 }
 
 void DMA::Add(Encoder *encoder) {
@@ -107,17 +101,6 @@
   }
   const int index = encoder->GetFPGAIndex();
 
-#ifdef WPILIB2015
-  if (index < 4) {
-    // TODO(austin): Encoder uses a Counter for 1x or 2x; quad for 4x...
-    tdma_config_->writeConfig_Enable_Encoders(true, &status);
-  } else {
-    wpi_setErrorWithContext(
-        NiFpga_Status_InvalidParameter,
-        "FPGA encoder index is not in the 4 that get logged.");
-    return;
-  }
-#else
   if (index < 4) {
     // TODO(austin): Encoder uses a Counter for 1x or 2x; quad for 4x...
     tdma_config_->writeConfig_Enable_Encoders_Low(true, &status);
@@ -130,9 +113,8 @@
         "FPGA encoder index is not in the 4 that get logged.");
     return;
   }
-#endif
 
-  wpi_setErrorWithContext(status, getHALErrorMessage(status));
+  wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
 }
 
 void DMA::Add(DigitalSource * /*input*/) {
@@ -145,7 +127,7 @@
   }
 
   tdma_config_->writeConfig_Enable_DI(true, &status);
-  wpi_setErrorWithContext(status, getHALErrorMessage(status));
+  wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
 }
 
 void DMA::Add(AnalogInput *input) {
@@ -162,7 +144,7 @@
   } else {
     tdma_config_->writeConfig_Enable_AI0_High(true, &status);
   }
-  wpi_setErrorWithContext(status, getHALErrorMessage(status));
+  wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
 }
 
 void DMA::SetExternalTrigger(DigitalSource *input, bool rising, bool falling) {
@@ -190,13 +172,13 @@
   if (status == 0) {
     if (!is_external_clock) {
       tdma_config_->writeConfig_ExternalClock(true, &status);
-      wpi_setErrorWithContext(status, getHALErrorMessage(status));
+      wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
       if (status != 0) {
         return;
       }
     }
   } else {
-    wpi_setErrorWithContext(status, getHALErrorMessage(status));
+    wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
     return;
   }
 
@@ -206,7 +188,12 @@
   new_trigger.RisingEdge = rising;
   new_trigger.ExternalClockSource_AnalogTrigger = false;
   unsigned char module = 0;
-  unsigned char channel = input->GetChannelForRouting();
+  uint32_t channel =
+#ifdef WPILIB2017
+      input->GetChannel();
+#else
+      input->GetChannelForRouting();
+#endif
   if (channel >= kNumHeaders) {
     module = 1;
     channel -= kNumHeaders;
@@ -218,29 +205,12 @@
   new_trigger.ExternalClockSource_Channel = channel;
 
 // Configures the trigger to be external, not off the FPGA clock.
-#ifndef WPILIB2015
   tdma_config_->writeExternalTriggers(channel_index / 4, channel_index % 4,
                                       new_trigger, &status);
   if (status != 0) {
-    wpi_setErrorWithContext(status, getHALErrorMessage(status));
+    wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
     return;
   }
-#else
-  uint32_t current_triggers;
-  tRioStatusCode register_status =
-      NiFpga_ReadU32(0x10000, 0x1832c, &current_triggers);
-  if (register_status != 0) {
-    wpi_setErrorWithContext(register_status, getHALErrorMessage(status));
-    return;
-  }
-  current_triggers = (current_triggers & ~(0xff << (channel_index * 8))) |
-                     (new_trigger.value << (channel_index * 8));
-  register_status = NiFpga_WriteU32(0x10000, 0x1832c, current_triggers);
-  if (register_status != 0) {
-    wpi_setErrorWithContext(register_status, getHALErrorMessage(status));
-    return;
-  }
-#endif
 }
 
 DMA::ReadStatus DMA::Read(DMASample *sample, uint32_t timeout_ms,
@@ -269,7 +239,7 @@
   } else if (status == NiFpga_Status_FifoTimeout) {
     return STATUS_TIMEOUT;
   } else {
-    wpi_setErrorWithContext(status, getHALErrorMessage(status));
+    wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
     return STATUS_ERROR;
   }
 }
@@ -286,7 +256,7 @@
 void DMA::Start(size_t queue_depth) {
   tRioStatusCode status = 0;
   tconfig_ = tdma_config_->readConfig(&status);
-  wpi_setErrorWithContext(status, getHALErrorMessage(status));
+  wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
   if (status != 0) {
     return;
   }
@@ -317,15 +287,10 @@
     SET_SIZE(Enable_Counters_High);
     SET_SIZE(Enable_CounterTimers_Low);
     SET_SIZE(Enable_CounterTimers_High);
-#ifdef WPILIB2015
-    SET_SIZE(Enable_Encoders);
-    SET_SIZE(Enable_EncoderTimers);
-#else
     SET_SIZE(Enable_Encoders_Low);
     SET_SIZE(Enable_Encoders_High);
     SET_SIZE(Enable_EncoderTimers_Low);
     SET_SIZE(Enable_EncoderTimers_High);
-#endif
 #undef SET_SIZE
     capture_size_ = accum_size + 1;
   }
@@ -333,23 +298,23 @@
   manager_.reset(
       new nFPGA::tDMAManager(0, queue_depth * capture_size_, &status));
 
-  wpi_setErrorWithContext(status, getHALErrorMessage(status));
+  wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
   if (status != 0) {
     return;
   }
   // Start, stop, start to clear the buffer.
   manager_->start(&status);
-  wpi_setErrorWithContext(status, getHALErrorMessage(status));
+  wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
   if (status != 0) {
     return;
   }
   manager_->stop(&status);
-  wpi_setErrorWithContext(status, getHALErrorMessage(status));
+  wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
   if (status != 0) {
     return;
   }
   manager_->start(&status);
-  wpi_setErrorWithContext(status, getHALErrorMessage(status));
+  wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
   if (status != 0) {
     return;
   }
@@ -372,36 +337,30 @@
   if (offset(kEnable_DI) == -1) {
     wpi_setStaticErrorWithContext(
         dma_, NiFpga_Status_ResourceNotFound,
-        getHALErrorMessage(NiFpga_Status_ResourceNotFound));
+        HAL_GetErrorMessage(NiFpga_Status_ResourceNotFound));
     return false;
   }
-  if (input->GetChannelForRouting() < kNumHeaders) {
-    return (read_buffer_[offset(kEnable_DI)] >> input->GetChannelForRouting()) &
-           0x1;
+  const uint32_t channel =
+#ifdef WPILIB2017
+      input->GetChannel();
+#else
+      input->GetChannelForRouting();
+#endif
+  if (channel < kNumHeaders) {
+    return (read_buffer_[offset(kEnable_DI)] >> channel) & 0x1;
   } else {
-    return (read_buffer_[offset(kEnable_DI)] >>
-            (input->GetChannelForRouting() + 6)) &
-           0x1;
+    return (read_buffer_[offset(kEnable_DI)] >> (channel + 6)) & 0x1;
   }
 }
 
 int32_t DMASample::GetRaw(Encoder *input) const {
   int index = input->GetFPGAIndex();
   uint32_t dmaWord = 0;
-#ifdef WPILIB2015
-  if (index >= 4 || offset(kEnable_Encoders) == -1) {
-    wpi_setStaticErrorWithContext(
-        dma_, NiFpga_Status_ResourceNotFound,
-        getHALErrorMessage(NiFpga_Status_ResourceNotFound));
-    return -1;
-  }
-  dmaWord = read_buffer_[offset(kEnable_Encoders) + index];
-#else
   if (index < 4) {
     if (offset(kEnable_Encoders_Low) == -1) {
       wpi_setStaticErrorWithContext(
           dma_, NiFpga_Status_ResourceNotFound,
-          getHALErrorMessage(NiFpga_Status_ResourceNotFound));
+          HAL_GetErrorMessage(NiFpga_Status_ResourceNotFound));
       return -1;
     }
     dmaWord = read_buffer_[offset(kEnable_Encoders_Low) + index];
@@ -409,17 +368,16 @@
     if (offset(kEnable_Encoders_High) == -1) {
       wpi_setStaticErrorWithContext(
           dma_, NiFpga_Status_ResourceNotFound,
-          getHALErrorMessage(NiFpga_Status_ResourceNotFound));
+          HAL_GetErrorMessage(NiFpga_Status_ResourceNotFound));
       return -1;
     }
     dmaWord = read_buffer_[offset(kEnable_Encoders_High) + (index - 4)];
   } else {
     wpi_setStaticErrorWithContext(
         dma_, NiFpga_Status_ResourceNotFound,
-        getHALErrorMessage(NiFpga_Status_ResourceNotFound));
+        HAL_GetErrorMessage(NiFpga_Status_ResourceNotFound));
     return 0;
   }
-#endif
 
   int32_t result = 0;
 
@@ -448,7 +406,7 @@
     if (offset(kEnable_AI0_Low) == -1) {
       wpi_setStaticErrorWithContext(
           dma_, NiFpga_Status_ResourceNotFound,
-          getHALErrorMessage(NiFpga_Status_ResourceNotFound));
+          HAL_GetErrorMessage(NiFpga_Status_ResourceNotFound));
       return 0xffff;
     }
     dmaWord = read_buffer_[offset(kEnable_AI0_Low) + channel / 2];
@@ -456,14 +414,14 @@
     if (offset(kEnable_AI0_High) == -1) {
       wpi_setStaticErrorWithContext(
           dma_, NiFpga_Status_ResourceNotFound,
-          getHALErrorMessage(NiFpga_Status_ResourceNotFound));
+          HAL_GetErrorMessage(NiFpga_Status_ResourceNotFound));
       return 0xffff;
     }
     dmaWord = read_buffer_[offset(kEnable_AI0_High) + (channel - 4) / 2];
   } else {
     wpi_setStaticErrorWithContext(
         dma_, NiFpga_Status_ResourceNotFound,
-        getHALErrorMessage(NiFpga_Status_ResourceNotFound));
+        HAL_GetErrorMessage(NiFpga_Status_ResourceNotFound));
     return 0xffff;
   }
   if (channel % 2) {
diff --git a/frc971/wpilib/dma.h b/frc971/wpilib/dma.h
index 970497c..e07ab36 100644
--- a/frc971/wpilib/dma.h
+++ b/frc971/wpilib/dma.h
@@ -9,13 +9,30 @@
 #include <array>
 #include <memory>
 
+#ifdef WPILIB2017
+#include "HAL/ChipObject.h"
+#else
 #include "ChipObject.h"
+#endif
 #include "ErrorBase.h"
 
 class DMA;
+#ifdef WPILIB2017
+namespace frc {
 class DigitalSource;
 class AnalogInput;
 class Encoder;
+}  // namespace frc
+#else
+class DigitalSource;
+class AnalogInput;
+class Encoder;
+namespace frc {
+using ::DigitalSource;
+using ::AnalogInput;
+using ::Encoder;
+}  // namespace frc
+#endif
 
 // A POD class which stores the data from a DMA sample and provides safe ways to
 // access it.
diff --git a/frc971/wpilib/gyro_sender.cc b/frc971/wpilib/gyro_sender.cc
index f410c19..5128110 100644
--- a/frc971/wpilib/gyro_sender.cc
+++ b/frc971/wpilib/gyro_sender.cc
@@ -20,10 +20,15 @@
 namespace frc971 {
 namespace wpilib {
 
-GyroSender::GyroSender() {}
 namespace chrono = ::std::chrono;
 using ::aos::monotonic_clock;
 
+GyroSender::GyroSender() {
+  PCHECK(system(
+             "ps -ef | grep '\\[spi0\\]' | awk '{print $1}' | xargs chrt -f -p "
+             "33") == 0);
+}
+
 void GyroSender::operator()() {
   ::aos::SetCurrentThreadName("Gyro");
 
@@ -47,7 +52,10 @@
   bool zeroed = false;
   double zero_offset = 0;
 
-  ::aos::time::PhasedLoop phased_loop(::aos::time::FromRate(kReadingRate));
+  ::aos::SetCurrentThreadRealtimePriority(33);
+
+  ::aos::time::PhasedLoop phased_loop(::aos::time::FromRate(kReadingRate),
+                                      chrono::milliseconds(4));
   // How many timesteps the next reading represents.
   int number_readings = 0;
 
diff --git a/frc971/wpilib/interrupt_edge_counting.cc b/frc971/wpilib/interrupt_edge_counting.cc
index 5c10b89..ec6f627 100644
--- a/frc971/wpilib/interrupt_edge_counting.cc
+++ b/frc971/wpilib/interrupt_edge_counting.cc
@@ -21,7 +21,11 @@
 
 void EdgeCounter::operator()() {
   ::aos::SetCurrentThreadName("EdgeCounter_" +
+#ifdef WPILIB2017
+                              ::std::to_string(input_->GetChannel()));
+#else
                               ::std::to_string(input_->GetChannelForRouting()));
+#endif
 
   input_->RequestInterrupts();
   input_->SetUpSourceEdge(true, true);
@@ -55,7 +59,11 @@
       current_value_ = hall_value;
     } else {
       LOG(WARNING, "Detected spurious edge on %d.  Dropping it.\n",
+#ifdef WPILIB2017
+          input_->GetChannel());
+#else
           input_->GetChannelForRouting());
+#endif
     }
   }
 }
diff --git a/frc971/wpilib/joystick_sender.cc b/frc971/wpilib/joystick_sender.cc
index 4942eaa..b4866e9 100644
--- a/frc971/wpilib/joystick_sender.cc
+++ b/frc971/wpilib/joystick_sender.cc
@@ -6,7 +6,11 @@
 #include "aos/common/logging/queue_logging.h"
 
 #include "DriverStation.h"
+#ifdef WPILIB2017
+#include "HAL/HAL.h"
+#else
 #include "HAL/HAL.hpp"
+#endif
 
 namespace frc971 {
 namespace wpilib {
@@ -27,8 +31,13 @@
     ds->WaitForData();
     auto new_state = ::aos::joystick_state.MakeMessage();
 
+#ifdef WPILIB2017
+    HAL_ControlWord control_word;
+    HAL_GetControlWord(&control_word);
+#else
     HALControlWord control_word;
     HALGetControlWord(&control_word);
+#endif
     new_state->test_mode = control_word.test;
     new_state->fms_attached = control_word.fmsAttached;
     new_state->enabled = control_word.enabled;
diff --git a/frc971/wpilib/wpilib_interface.cc b/frc971/wpilib/wpilib_interface.cc
index 016f52f..af72f7d 100644
--- a/frc971/wpilib/wpilib_interface.cc
+++ b/frc971/wpilib/wpilib_interface.cc
@@ -7,15 +7,25 @@
 #include "ControllerPower.h"
 #undef ERROR
 
+#ifndef WPILIB2017
+namespace frc {
+using ::DriverStation;
+}  // namespace frc
+#endif
+
 namespace frc971 {
 namespace wpilib {
 
-void SendRobotState(int32_t my_pid, DriverStation *ds) {
+void SendRobotState(int32_t my_pid, frc::DriverStation *ds) {
   auto new_state = ::aos::robot_state.MakeMessage();
 
   new_state->reader_pid = my_pid;
   new_state->outputs_enabled = ds->IsSysActive();
+#ifdef WPILIB2017
+  new_state->browned_out = ds->IsBrownedOut();
+#else
   new_state->browned_out = ds->IsSysBrownedOut();
+#endif
 
   new_state->is_3v3_active = ControllerPower::GetEnabled3V3();
   new_state->is_5v_active = ControllerPower::GetEnabled5V();
diff --git a/frc971/wpilib/wpilib_interface.h b/frc971/wpilib/wpilib_interface.h
index 216bf09..1c55cc7 100644
--- a/frc971/wpilib/wpilib_interface.h
+++ b/frc971/wpilib/wpilib_interface.h
@@ -3,13 +3,22 @@
 
 #include <stdint.h>
 
+#ifdef WPILIB2017
+namespace frc {
 class DriverStation;
+}  // namespace frc
+#else
+class DriverStation;
+namespace frc {
+using ::DriverStation;
+}  // namespace frc
+#endif
 
 namespace frc971 {
 namespace wpilib {
 
 // Sends out a message on ::aos::robot_state.
-void SendRobotState(int32_t my_pid, DriverStation *ds);
+void SendRobotState(int32_t my_pid, ::frc::DriverStation *ds);
 
 }  // namespace wpilib
 }  // namespace frc971
diff --git a/third_party/BUILD b/third_party/BUILD
index da60a79..cef6fcf 100644
--- a/third_party/BUILD
+++ b/third_party/BUILD
@@ -1,6 +1,6 @@
 cc_library(
   name = 'wpilib',
-  deps = ['//third_party/allwpilib_2016:wpilib'],
+  deps = ['//third_party/allwpilib_2017:wpilib'],
   visibility = ['//visibility:public'],
   linkstatic = True,
 )
diff --git a/third_party/allwpilib_2016/BUILD b/third_party/allwpilib_2016/BUILD
index 2d789d7..fbe00dc 100644
--- a/third_party/allwpilib_2016/BUILD
+++ b/third_party/allwpilib_2016/BUILD
@@ -78,7 +78,7 @@
  ],
  deps = [
    '//third_party/ntcore_2016:ntcore',
-   '//external:ni-libraries',
+   '//external:ni-libraries-2016',
    '//aos/common/logging',
  ],
  hdrs = _h_hdrs + _hpp_hdrs + ['wpilibc/shared/include/Task.inc'],
diff --git a/third_party/allwpilib_2017/BUILD b/third_party/allwpilib_2017/BUILD
new file mode 100644
index 0000000..054ef08
--- /dev/null
+++ b/third_party/allwpilib_2017/BUILD
@@ -0,0 +1,113 @@
+licenses(['notice'])
+
+genrule(
+  name = 'wpilib_version',
+  outs = ['shared/src/WPILibVersion.cpp'],
+  cmd = '\n'.join([
+    "cat > \"$@\" << EOF",
+    "// Autogenerated file! Do not manually edit this file.",
+    "#include \"WPILibVersion.h\"",
+    "const char *WPILibVersion = \"2017-frc971\";",
+    "EOF",
+  ]),
+)
+
+_header_dirs = [
+  'wpilibc/shared/include',
+  'wpilibc/athena/include',
+  'hal/include',
+  'hal/lib/athena',
+]
+
+# Names of WPILib "devices" I don't want to deal with making trivial updates to
+# chop out various ugliness or have to vet for sanity.
+_excluded_devices = [
+  'ADXL345_I2C',
+  'GearTooth',
+  'Ultrasonic',
+  'Victor',
+  'TalonSRX',
+  'CANTalon',
+  'CANJaguar',
+  'PIDController',
+  'Spark',
+  'Servo',
+  'VictorSP',
+  'SD540',
+  'RobotDrive',
+  'Jaguar',
+  'ADXL345_SPI',
+  'DoubleSolenoid',
+  'AnalogPotentiometer',
+  'ADXL362',
+  'AnalogGyro',
+  'AnalogAccelerometer',
+  'CameraServer',
+  'ADXRS450_Gyro',
+  'GyroBase',
+  'IterativeRobot',
+]
+
+# Whole subdirectories of WPILib we don't want around.
+_excluded_shared_directories = [
+  'SmartDashboard',
+  'LiveWindow',
+  'Commands',
+  'Buttons',
+  'Filters',
+]
+
+# Header files we don't want to have.
+_bad_hdrs = ([
+  'wpilibc/athena/include/WPILib.h',
+] + ['wpilibc/shared/include/%s/**/*' % d
+     for d in _excluded_shared_directories] +
+ ['wpilibc/*/include/%s.h' % d for d in _excluded_devices])
+_h_hdrs = glob([d + '/**/*.h' for d in _header_dirs], exclude=_bad_hdrs)
+_hpp_hdrs = glob([d + '/**/*.hpp' for d in _header_dirs], exclude=_bad_hdrs)
+
+cc_library(
+  name = 'wpilib',
+  visibility = ['//third_party:__pkg__'],
+  srcs = glob([
+    'wpilibc/athena/src/*.cpp',
+    'wpilibc/athena/src/Internal/*.cpp',
+    'wpilibc/shared/src/**/*.cpp',
+
+    'hal/lib/athena/*.cpp',
+    'hal/lib/athena/cpp/*.cpp',
+    'hal/lib/athena/ctre/*.cpp',
+    'hal/lib/shared/handles/*.cpp',
+  ], exclude = (
+    ['wpilibc/shared/src/%s/**/*' % d for d in _excluded_shared_directories] +
+    ['wpilibc/*/src/%s.cpp' % d for d in _excluded_devices]
+  )) + [
+    ':wpilib_version',
+  ],
+  copts = [
+    '-Wno-unused-parameter',
+    '-Wno-switch-enum',
+    '-Wno-attributes',
+    '-Wno-cast-align',
+    '-Wno-cast-qual',
+    '-Wno-deprecated-declarations',
+    '-Wno-error',
+    '-Wno-unused-const-variable',
+  ],
+  deps = [
+    '//third_party/ntcore_2017:ntcore',
+    '//external:ni-libraries-2017',
+    '//aos/common/logging',
+  ],
+  hdrs = _h_hdrs + _hpp_hdrs + [
+    'wpilibc/shared/include/CircularBuffer.inc',
+    'wpilibc/athena/include/Task.inc',
+  ],
+  includes = _header_dirs,
+  linkopts = [
+    '-lpthread',
+  ],
+  defines = [
+    'WPILIB2017=1',
+  ],
+)
diff --git a/third_party/allwpilib_2017/hal/lib/athena/HAL.cpp b/third_party/allwpilib_2017/hal/lib/athena/HAL.cpp
index 708ca1b..45fa6ce 100644
--- a/third_party/allwpilib_2017/hal/lib/athena/HAL.cpp
+++ b/third_party/allwpilib_2017/hal/lib/athena/HAL.cpp
@@ -36,8 +36,6 @@
 static std::unique_ptr<tSysWatchdog> watchdog;
 
 static priority_mutex timeMutex;
-static uint32_t timeEpoch = 0;
-static uint32_t prevFPGATime = 0;
 static HAL_NotifierHandle rolloverNotifier = 0;
 
 using namespace hal;
@@ -224,14 +222,7 @@
     *status = NiFpga_Status_ResourceNotInitialized;
     return 0;
   }
-  std::lock_guard<priority_mutex> lock(timeMutex);
-  uint32_t fpgaTime = global->readLocalTime(status);
-  if (*status != 0) return 0;
-  // check for rollover
-  if (fpgaTime < prevFPGATime) ++timeEpoch;
-  prevFPGATime = fpgaTime;
-  return static_cast<uint64_t>(timeEpoch) << 32 |
-         static_cast<uint64_t>(fpgaTime);
+  return global->readLocalTime(status);
 }
 
 /**
diff --git a/third_party/allwpilib_2017/wpilibc/athena/include/AnalogInput.h b/third_party/allwpilib_2017/wpilibc/athena/include/AnalogInput.h
index 9353a9b..220a560 100644
--- a/third_party/allwpilib_2017/wpilibc/athena/include/AnalogInput.h
+++ b/third_party/allwpilib_2017/wpilibc/athena/include/AnalogInput.h
@@ -11,7 +11,6 @@
 #include <string>
 
 #include "HAL/Types.h"
-#include "LiveWindow/LiveWindowSendable.h"
 #include "PIDSource.h"
 #include "SensorBase.h"
 
@@ -30,8 +29,7 @@
  * stable values.
  */
 class AnalogInput : public SensorBase,
-                    public PIDSource,
-                    public LiveWindowSendable {
+                    public PIDSource {
   friend class AnalogTrigger;
   friend class AnalogGyro;
 
@@ -74,20 +72,11 @@
 
   double PIDGet() override;
 
-  void UpdateTable() override;
-  void StartLiveWindowMode() override;
-  void StopLiveWindowMode() override;
-  std::string GetSmartDashboardType() const override;
-  void InitTable(std::shared_ptr<ITable> subTable) override;
-  std::shared_ptr<ITable> GetTable() const override;
-
  private:
   int m_channel;
   // TODO: Adjust HAL to avoid use of raw pointers.
   HAL_AnalogInputHandle m_port;
   int64_t m_accumulatorOffset;
-
-  std::shared_ptr<ITable> m_table;
 };
 
 }  // namespace frc
diff --git a/third_party/allwpilib_2017/wpilibc/athena/include/AnalogOutput.h b/third_party/allwpilib_2017/wpilibc/athena/include/AnalogOutput.h
index ea0c819..d948851 100644
--- a/third_party/allwpilib_2017/wpilibc/athena/include/AnalogOutput.h
+++ b/third_party/allwpilib_2017/wpilibc/athena/include/AnalogOutput.h
@@ -13,7 +13,6 @@
 #include <string>
 
 #include "HAL/AnalogOutput.h"
-#include "LiveWindow/LiveWindowSendable.h"
 #include "SensorBase.h"
 
 namespace frc {
@@ -21,7 +20,7 @@
 /**
  * MXP analog output class.
  */
-class AnalogOutput : public SensorBase, public LiveWindowSendable {
+class AnalogOutput : public SensorBase {
  public:
   explicit AnalogOutput(int channel);
   virtual ~AnalogOutput();
@@ -30,18 +29,9 @@
   double GetVoltage() const;
   int GetChannel();
 
-  void UpdateTable() override;
-  void StartLiveWindowMode() override;
-  void StopLiveWindowMode() override;
-  std::string GetSmartDashboardType() const override;
-  void InitTable(std::shared_ptr<ITable> subTable) override;
-  std::shared_ptr<ITable> GetTable() const override;
-
  protected:
   int m_channel;
   HAL_AnalogOutputHandle m_port;
-
-  std::shared_ptr<ITable> m_table;
 };
 
 }  // namespace frc
diff --git a/third_party/allwpilib_2017/wpilibc/athena/include/BuiltInAccelerometer.h b/third_party/allwpilib_2017/wpilibc/athena/include/BuiltInAccelerometer.h
index f1cf030..1234e2e 100644
--- a/third_party/allwpilib_2017/wpilibc/athena/include/BuiltInAccelerometer.h
+++ b/third_party/allwpilib_2017/wpilibc/athena/include/BuiltInAccelerometer.h
@@ -10,7 +10,6 @@
 #include <memory>
 #include <string>
 
-#include "LiveWindow/LiveWindowSendable.h"
 #include "SensorBase.h"
 #include "interfaces/Accelerometer.h"
 
@@ -22,8 +21,7 @@
  * This class allows access to the roboRIO's internal accelerometer.
  */
 class BuiltInAccelerometer : public Accelerometer,
-                             public SensorBase,
-                             public LiveWindowSendable {
+                             public SensorBase {
  public:
   explicit BuiltInAccelerometer(Range range = kRange_8G);
   virtual ~BuiltInAccelerometer() = default;
@@ -33,16 +31,6 @@
   double GetX() override;
   double GetY() override;
   double GetZ() override;
-
-  std::string GetSmartDashboardType() const override;
-  void InitTable(std::shared_ptr<ITable> subtable) override;
-  void UpdateTable() override;
-  std::shared_ptr<ITable> GetTable() const override;
-  void StartLiveWindowMode() override {}
-  void StopLiveWindowMode() override {}
-
- private:
-  std::shared_ptr<ITable> m_table;
 };
 
 }  // namespace frc
diff --git a/third_party/allwpilib_2017/wpilibc/athena/include/Compressor.h b/third_party/allwpilib_2017/wpilibc/athena/include/Compressor.h
index 7bb7e8d..a2464b4 100644
--- a/third_party/allwpilib_2017/wpilibc/athena/include/Compressor.h
+++ b/third_party/allwpilib_2017/wpilibc/athena/include/Compressor.h
@@ -11,18 +11,14 @@
 #include <string>
 
 #include "HAL/Types.h"
-#include "LiveWindow/LiveWindowSendable.h"
 #include "SensorBase.h"
-#include "tables/ITableListener.h"
 
 namespace frc {
 
 /**
  * PCM compressor
  */
-class Compressor : public SensorBase,
-                   public LiveWindowSendable,
-                   public ITableListener {
+class Compressor : public SensorBase {
  public:
   // Default PCM ID is 0
   explicit Compressor(int pcmID = GetDefaultSolenoidModule());
@@ -47,23 +43,12 @@
   bool GetCompressorNotConnectedFault() const;
   void ClearAllPCMStickyFaults();
 
-  void UpdateTable() override;
-  void StartLiveWindowMode() override;
-  void StopLiveWindowMode() override;
-  std::string GetSmartDashboardType() const override;
-  void InitTable(std::shared_ptr<ITable> subTable) override;
-  std::shared_ptr<ITable> GetTable() const override;
-  void ValueChanged(ITable* source, llvm::StringRef key,
-                    std::shared_ptr<nt::Value> value, bool isNew) override;
-
  protected:
   HAL_CompressorHandle m_compressorHandle;
 
  private:
   void SetCompressor(bool on);
   int m_module;
-
-  std::shared_ptr<ITable> m_table;
 };
 
 }  // namespace frc
diff --git a/third_party/allwpilib_2017/wpilibc/athena/include/Counter.h b/third_party/allwpilib_2017/wpilibc/athena/include/Counter.h
index 643f2b2..2dfb9eb 100644
--- a/third_party/allwpilib_2017/wpilibc/athena/include/Counter.h
+++ b/third_party/allwpilib_2017/wpilibc/athena/include/Counter.h
@@ -14,8 +14,8 @@
 #include "CounterBase.h"
 #include "HAL/Counter.h"
 #include "HAL/Types.h"
-#include "LiveWindow/LiveWindowSendable.h"
 #include "SensorBase.h"
+#include "support/deprecated.h"
 
 namespace frc {
 
@@ -31,8 +31,7 @@
  * to be zeroed before use.
  */
 class Counter : public SensorBase,
-                public CounterBase,
-                public LiveWindowSendable {
+                public CounterBase {
  public:
   enum Mode {
     kTwoPulse = 0,
@@ -94,13 +93,6 @@
   int GetSamplesToAverage() const;
   int GetFPGAIndex() const { return m_index; }
 
-  void UpdateTable() override;
-  void StartLiveWindowMode() override;
-  void StopLiveWindowMode() override;
-  std::string GetSmartDashboardType() const override;
-  void InitTable(std::shared_ptr<ITable> subTable) override;
-  std::shared_ptr<ITable> GetTable() const override;
-
  protected:
   // Makes the counter count up.
   std::shared_ptr<DigitalSource> m_upSource;
@@ -112,7 +104,6 @@
  private:
   int m_index = 0;  ///< The index of this counter.
 
-  std::shared_ptr<ITable> m_table;
   friend class DigitalGlitchFilter;
 };
 
diff --git a/third_party/allwpilib_2017/wpilibc/athena/include/DigitalInput.h b/third_party/allwpilib_2017/wpilibc/athena/include/DigitalInput.h
index 92a762f..e7b2d2f 100644
--- a/third_party/allwpilib_2017/wpilibc/athena/include/DigitalInput.h
+++ b/third_party/allwpilib_2017/wpilibc/athena/include/DigitalInput.h
@@ -13,7 +13,6 @@
 #include <string>
 
 #include "DigitalSource.h"
-#include "LiveWindow/LiveWindowSendable.h"
 
 namespace frc {
 
@@ -27,7 +26,7 @@
  * as required. This class is only for devices like switches etc. that aren't
  * implemented anywhere else.
  */
-class DigitalInput : public DigitalSource, public LiveWindowSendable {
+class DigitalInput : public DigitalSource {
  public:
   explicit DigitalInput(int channel);
   virtual ~DigitalInput();
@@ -39,18 +38,10 @@
   AnalogTriggerType GetAnalogTriggerTypeForRouting() const override;
   bool IsAnalogTrigger() const override;
 
-  void UpdateTable();
-  void StartLiveWindowMode();
-  void StopLiveWindowMode();
-  std::string GetSmartDashboardType() const;
-  void InitTable(std::shared_ptr<ITable> subTable);
-  std::shared_ptr<ITable> GetTable() const;
-
  private:
   int m_channel;
   HAL_DigitalHandle m_handle;
 
-  std::shared_ptr<ITable> m_table;
   friend class DigitalGlitchFilter;
 };
 
diff --git a/third_party/allwpilib_2017/wpilibc/athena/include/DigitalOutput.h b/third_party/allwpilib_2017/wpilibc/athena/include/DigitalOutput.h
index 8431191..b262b28 100644
--- a/third_party/allwpilib_2017/wpilibc/athena/include/DigitalOutput.h
+++ b/third_party/allwpilib_2017/wpilibc/athena/include/DigitalOutput.h
@@ -12,8 +12,6 @@
 
 #include "DigitalSource.h"
 #include "HAL/Types.h"
-#include "LiveWindow/LiveWindowSendable.h"
-#include "tables/ITableListener.h"
 
 namespace frc {
 
@@ -23,9 +21,7 @@
  * elsewhere will allocate channels automatically so for those devices it
  * shouldn't be done here.
  */
-class DigitalOutput : public DigitalSource,
-                      public ITableListener,
-                      public LiveWindowSendable {
+class DigitalOutput : public DigitalSource {
  public:
   explicit DigitalOutput(int channel);
   virtual ~DigitalOutput();
@@ -44,21 +40,10 @@
   AnalogTriggerType GetAnalogTriggerTypeForRouting() const override;
   bool IsAnalogTrigger() const override;
 
-  void ValueChanged(ITable* source, llvm::StringRef key,
-                    std::shared_ptr<nt::Value> value, bool isNew) override;
-  void UpdateTable();
-  void StartLiveWindowMode();
-  void StopLiveWindowMode();
-  std::string GetSmartDashboardType() const;
-  void InitTable(std::shared_ptr<ITable> subTable);
-  std::shared_ptr<ITable> GetTable() const;
-
  private:
   int m_channel;
   HAL_DigitalHandle m_handle;
   HAL_DigitalPWMHandle m_pwmGenerator;
-
-  std::shared_ptr<ITable> m_table;
 };
 
 }  // namespace frc
diff --git a/third_party/allwpilib_2017/wpilibc/athena/include/DriverStation.h b/third_party/allwpilib_2017/wpilibc/athena/include/DriverStation.h
index 6bd2ea6..f38b299 100644
--- a/third_party/allwpilib_2017/wpilibc/athena/include/DriverStation.h
+++ b/third_party/allwpilib_2017/wpilibc/athena/include/DriverStation.h
@@ -96,8 +96,6 @@
 
  private:
   DriverStation();
-  void ReportJoystickUnpluggedError(llvm::StringRef message);
-  void ReportJoystickUnpluggedWarning(llvm::StringRef message);
   void Run();
   void UpdateControlWord(bool force, HAL_ControlWord& controlWord) const;
 
@@ -136,8 +134,6 @@
   mutable HAL_ControlWord m_controlWordCache;
   mutable std::chrono::steady_clock::time_point m_lastControlWordUpdate;
   mutable priority_mutex m_controlWordMutex;
-
-  double m_nextMessageTime = 0;
 };
 
 }  // namespace frc
diff --git a/third_party/allwpilib_2017/wpilibc/athena/include/Encoder.h b/third_party/allwpilib_2017/wpilibc/athena/include/Encoder.h
index f429703..ab21a39 100644
--- a/third_party/allwpilib_2017/wpilibc/athena/include/Encoder.h
+++ b/third_party/allwpilib_2017/wpilibc/athena/include/Encoder.h
@@ -13,7 +13,6 @@
 #include "Counter.h"
 #include "CounterBase.h"
 #include "HAL/Encoder.h"
-#include "LiveWindow/LiveWindowSendable.h"
 #include "PIDSource.h"
 #include "SensorBase.h"
 
@@ -38,8 +37,7 @@
  */
 class Encoder : public SensorBase,
                 public CounterBase,
-                public PIDSource,
-                public LiveWindowSendable {
+                public PIDSource {
  public:
   enum IndexingType {
     kResetWhileHigh,
@@ -85,13 +83,6 @@
   void SetIndexSource(const DigitalSource& source,
                       IndexingType type = kResetOnRisingEdge);
 
-  void UpdateTable() override;
-  void StartLiveWindowMode() override;
-  void StopLiveWindowMode() override;
-  std::string GetSmartDashboardType() const override;
-  void InitTable(std::shared_ptr<ITable> subTable) override;
-  std::shared_ptr<ITable> GetTable() const override;
-
   int GetFPGAIndex() const;
 
  private:
@@ -104,7 +95,6 @@
   std::unique_ptr<DigitalSource> m_indexSource = nullptr;
   HAL_EncoderHandle m_encoder = HAL_kInvalidHandle;
 
-  std::shared_ptr<ITable> m_table;
   friend class DigitalGlitchFilter;
 };
 
diff --git a/third_party/allwpilib_2017/wpilibc/athena/include/PWM.h b/third_party/allwpilib_2017/wpilibc/athena/include/PWM.h
index e66a81e..7778036 100644
--- a/third_party/allwpilib_2017/wpilibc/athena/include/PWM.h
+++ b/third_party/allwpilib_2017/wpilibc/athena/include/PWM.h
@@ -11,9 +11,7 @@
 #include <string>
 
 #include "HAL/Types.h"
-#include "LiveWindow/LiveWindowSendable.h"
 #include "SensorBase.h"
-#include "tables/ITableListener.h"
 
 namespace frc {
 
@@ -34,9 +32,7 @@
  *   - 1 = minimum pulse width (currently .5ms)
  *   - 0 = disabled (i.e. PWM output is held low)
  */
-class PWM : public SensorBase,
-            public ITableListener,
-            public LiveWindowSendable {
+class PWM : public SensorBase {
  public:
   enum PeriodMultiplier {
     kPeriodMultiplier_1X = 1,
@@ -65,16 +61,6 @@
   int GetChannel() const { return m_channel; }
 
  protected:
-  void ValueChanged(ITable* source, llvm::StringRef key,
-                    std::shared_ptr<nt::Value> value, bool isNew) override;
-  void UpdateTable() override;
-  void StartLiveWindowMode() override;
-  void StopLiveWindowMode() override;
-  std::string GetSmartDashboardType() const override;
-  void InitTable(std::shared_ptr<ITable> subTable) override;
-  std::shared_ptr<ITable> GetTable() const override;
-
-  std::shared_ptr<ITable> m_table;
 
  private:
   int m_channel;
diff --git a/third_party/allwpilib_2017/wpilibc/athena/include/PowerDistributionPanel.h b/third_party/allwpilib_2017/wpilibc/athena/include/PowerDistributionPanel.h
index f7d643f..f5223f3 100644
--- a/third_party/allwpilib_2017/wpilibc/athena/include/PowerDistributionPanel.h
+++ b/third_party/allwpilib_2017/wpilibc/athena/include/PowerDistributionPanel.h
@@ -10,7 +10,6 @@
 #include <memory>
 #include <string>
 
-#include "LiveWindow/LiveWindowSendable.h"
 #include "SensorBase.h"
 
 namespace frc {
@@ -19,7 +18,7 @@
  * Class for getting voltage, current, temperature, power and energy from the
  * CAN PDP.
  */
-class PowerDistributionPanel : public SensorBase, public LiveWindowSendable {
+class PowerDistributionPanel : public SensorBase {
  public:
   PowerDistributionPanel();
   explicit PowerDistributionPanel(int module);
@@ -33,15 +32,7 @@
   void ResetTotalEnergy();
   void ClearStickyFaults();
 
-  void UpdateTable() override;
-  void StartLiveWindowMode() override;
-  void StopLiveWindowMode() override;
-  std::string GetSmartDashboardType() const override;
-  void InitTable(std::shared_ptr<ITable> subTable) override;
-  std::shared_ptr<ITable> GetTable() const override;
-
  private:
-  std::shared_ptr<ITable> m_table;
   int m_module;
 };
 
diff --git a/third_party/allwpilib_2017/wpilibc/athena/include/Relay.h b/third_party/allwpilib_2017/wpilibc/athena/include/Relay.h
index eac3967..6d7f877 100644
--- a/third_party/allwpilib_2017/wpilibc/athena/include/Relay.h
+++ b/third_party/allwpilib_2017/wpilibc/athena/include/Relay.h
@@ -11,11 +11,8 @@
 #include <string>
 
 #include "HAL/Types.h"
-#include "LiveWindow/LiveWindowSendable.h"
 #include "MotorSafety.h"
 #include "SensorBase.h"
-#include "tables/ITable.h"
-#include "tables/ITableListener.h"
 
 namespace frc {
 
@@ -33,9 +30,7 @@
  * a solenoid).
  */
 class Relay : public MotorSafety,
-              public SensorBase,
-              public ITableListener,
-              public LiveWindowSendable {
+              public SensorBase {
  public:
   enum Value { kOff, kOn, kForward, kReverse };
   enum Direction { kBothDirections, kForwardOnly, kReverseOnly };
@@ -55,17 +50,6 @@
   void SetSafetyEnabled(bool enabled) override;
   void GetDescription(std::ostringstream& desc) const override;
 
-  void ValueChanged(ITable* source, llvm::StringRef key,
-                    std::shared_ptr<nt::Value> value, bool isNew) override;
-  void UpdateTable() override;
-  void StartLiveWindowMode() override;
-  void StopLiveWindowMode() override;
-  std::string GetSmartDashboardType() const override;
-  void InitTable(std::shared_ptr<ITable> subTable) override;
-  std::shared_ptr<ITable> GetTable() const override;
-
-  std::shared_ptr<ITable> m_table;
-
  private:
   int m_channel;
   Direction m_direction;
diff --git a/third_party/allwpilib_2017/wpilibc/athena/include/Solenoid.h b/third_party/allwpilib_2017/wpilibc/athena/include/Solenoid.h
index 3ede182..4db8ac3 100644
--- a/third_party/allwpilib_2017/wpilibc/athena/include/Solenoid.h
+++ b/third_party/allwpilib_2017/wpilibc/athena/include/Solenoid.h
@@ -11,9 +11,7 @@
 #include <string>
 
 #include "HAL/Types.h"
-#include "LiveWindow/LiveWindowSendable.h"
 #include "SolenoidBase.h"
-#include "tables/ITableListener.h"
 
 namespace frc {
 
@@ -23,9 +21,7 @@
  * The Solenoid class is typically used for pneumatics solenoids, but could be
  * used for any device within the current spec of the PCM.
  */
-class Solenoid : public SolenoidBase,
-                 public LiveWindowSendable,
-                 public ITableListener {
+class Solenoid : public SolenoidBase {
  public:
   explicit Solenoid(int channel);
   Solenoid(int moduleNumber, int channel);
@@ -34,19 +30,9 @@
   virtual bool Get() const;
   bool IsBlackListed() const;
 
-  void ValueChanged(ITable* source, llvm::StringRef key,
-                    std::shared_ptr<nt::Value> value, bool isNew);
-  void UpdateTable();
-  void StartLiveWindowMode();
-  void StopLiveWindowMode();
-  std::string GetSmartDashboardType() const;
-  void InitTable(std::shared_ptr<ITable> subTable);
-  std::shared_ptr<ITable> GetTable() const;
-
  private:
   HAL_SolenoidHandle m_solenoidHandle = HAL_kInvalidHandle;
   int m_channel;  ///< The channel on the module to control.
-  std::shared_ptr<ITable> m_table;
 };
 
 }  // namespace frc
diff --git a/third_party/allwpilib_2017/wpilibc/athena/include/SolenoidBase.h b/third_party/allwpilib_2017/wpilibc/athena/include/SolenoidBase.h
index 276f6e6..5cd327e 100644
--- a/third_party/allwpilib_2017/wpilibc/athena/include/SolenoidBase.h
+++ b/third_party/allwpilib_2017/wpilibc/athena/include/SolenoidBase.h
@@ -21,6 +21,7 @@
  public:
   virtual ~SolenoidBase() = default;
   int GetAll(int module = 0) const;
+  void SetAll(int value, int module = 0);
 
   int GetPCMSolenoidBlackList(int module) const;
   bool GetPCMSolenoidVoltageStickyFault(int module) const;
diff --git a/third_party/allwpilib_2017/wpilibc/athena/src/AnalogInput.cpp b/third_party/allwpilib_2017/wpilibc/athena/src/AnalogInput.cpp
index d370c3a..a1098c5 100644
--- a/third_party/allwpilib_2017/wpilibc/athena/src/AnalogInput.cpp
+++ b/third_party/allwpilib_2017/wpilibc/athena/src/AnalogInput.cpp
@@ -13,7 +13,6 @@
 #include "HAL/AnalogAccumulator.h"
 #include "HAL/HAL.h"
 #include "HAL/Ports.h"
-#include "LiveWindow/LiveWindow.h"
 #include "Timer.h"
 #include "WPIErrors.h"
 
@@ -51,7 +50,6 @@
     return;
   }
 
-  LiveWindow::GetInstance()->AddSensor("AnalogInput", channel, this);
   HAL_Report(HALUsageReporting::kResourceType_AnalogChannel, channel);
 }
 
@@ -415,24 +413,3 @@
   if (StatusIsFatal()) return 0.0;
   return GetAverageVoltage();
 }
-
-void AnalogInput::UpdateTable() {
-  if (m_table != nullptr) {
-    m_table->PutNumber("Value", GetAverageVoltage());
-  }
-}
-
-void AnalogInput::StartLiveWindowMode() {}
-
-void AnalogInput::StopLiveWindowMode() {}
-
-std::string AnalogInput::GetSmartDashboardType() const {
-  return "Analog Input";
-}
-
-void AnalogInput::InitTable(std::shared_ptr<ITable> subTable) {
-  m_table = subTable;
-  UpdateTable();
-}
-
-std::shared_ptr<ITable> AnalogInput::GetTable() const { return m_table; }
diff --git a/third_party/allwpilib_2017/wpilibc/athena/src/AnalogOutput.cpp b/third_party/allwpilib_2017/wpilibc/athena/src/AnalogOutput.cpp
index 3905ae1..e4a65fb 100644
--- a/third_party/allwpilib_2017/wpilibc/athena/src/AnalogOutput.cpp
+++ b/third_party/allwpilib_2017/wpilibc/athena/src/AnalogOutput.cpp
@@ -12,7 +12,6 @@
 
 #include "HAL/HAL.h"
 #include "HAL/Ports.h"
-#include "LiveWindow/LiveWindow.h"
 #include "WPIErrors.h"
 
 using namespace frc;
@@ -48,7 +47,6 @@
     return;
   }
 
-  LiveWindow::GetInstance()->AddActuator("AnalogOutput", m_channel, this);
   HAL_Report(HALUsageReporting::kResourceType_AnalogOutput, m_channel);
 }
 
@@ -89,24 +87,3 @@
 
   return voltage;
 }
-
-void AnalogOutput::UpdateTable() {
-  if (m_table != nullptr) {
-    m_table->PutNumber("Value", GetVoltage());
-  }
-}
-
-void AnalogOutput::StartLiveWindowMode() {}
-
-void AnalogOutput::StopLiveWindowMode() {}
-
-std::string AnalogOutput::GetSmartDashboardType() const {
-  return "Analog Output";
-}
-
-void AnalogOutput::InitTable(std::shared_ptr<ITable> subTable) {
-  m_table = subTable;
-  UpdateTable();
-}
-
-std::shared_ptr<ITable> AnalogOutput::GetTable() const { return m_table; }
diff --git a/third_party/allwpilib_2017/wpilibc/athena/src/BuiltInAccelerometer.cpp b/third_party/allwpilib_2017/wpilibc/athena/src/BuiltInAccelerometer.cpp
index 8b80bca..294e053 100644
--- a/third_party/allwpilib_2017/wpilibc/athena/src/BuiltInAccelerometer.cpp
+++ b/third_party/allwpilib_2017/wpilibc/athena/src/BuiltInAccelerometer.cpp
@@ -9,7 +9,6 @@
 
 #include "HAL/Accelerometer.h"
 #include "HAL/HAL.h"
-#include "LiveWindow/LiveWindow.h"
 #include "WPIErrors.h"
 
 using namespace frc;
@@ -24,7 +23,6 @@
 
   HAL_Report(HALUsageReporting::kResourceType_Accelerometer, 0, 0,
              "Built-in accelerometer");
-  LiveWindow::GetInstance()->AddSensor((std::string) "BuiltInAccel", 0, this);
 }
 
 void BuiltInAccelerometer::SetRange(Range range) {
@@ -52,24 +50,3 @@
  * @return The acceleration of the roboRIO along the Z axis in g-forces
  */
 double BuiltInAccelerometer::GetZ() { return HAL_GetAccelerometerZ(); }
-
-std::string BuiltInAccelerometer::GetSmartDashboardType() const {
-  return "3AxisAccelerometer";
-}
-
-void BuiltInAccelerometer::InitTable(std::shared_ptr<ITable> subtable) {
-  m_table = subtable;
-  UpdateTable();
-}
-
-void BuiltInAccelerometer::UpdateTable() {
-  if (m_table != nullptr) {
-    m_table->PutNumber("X", GetX());
-    m_table->PutNumber("Y", GetY());
-    m_table->PutNumber("Z", GetZ());
-  }
-}
-
-std::shared_ptr<ITable> BuiltInAccelerometer::GetTable() const {
-  return m_table;
-}
diff --git a/third_party/allwpilib_2017/wpilibc/athena/src/Compressor.cpp b/third_party/allwpilib_2017/wpilibc/athena/src/Compressor.cpp
index 84722c7..e69a983 100644
--- a/third_party/allwpilib_2017/wpilibc/athena/src/Compressor.cpp
+++ b/third_party/allwpilib_2017/wpilibc/athena/src/Compressor.cpp
@@ -297,32 +297,3 @@
     wpi_setWPIError(Timeout);
   }
 }
-
-void Compressor::UpdateTable() {
-  if (m_table) {
-    m_table->PutBoolean("Enabled", Enabled());
-    m_table->PutBoolean("Pressure switch", GetPressureSwitchValue());
-  }
-}
-
-void Compressor::StartLiveWindowMode() {}
-
-void Compressor::StopLiveWindowMode() {}
-
-std::string Compressor::GetSmartDashboardType() const { return "Compressor"; }
-
-void Compressor::InitTable(std::shared_ptr<ITable> subTable) {
-  m_table = subTable;
-  UpdateTable();
-}
-
-std::shared_ptr<ITable> Compressor::GetTable() const { return m_table; }
-
-void Compressor::ValueChanged(ITable* source, llvm::StringRef key,
-                              std::shared_ptr<nt::Value> value, bool isNew) {
-  if (!value->IsBoolean()) return;
-  if (value->GetBoolean())
-    Start();
-  else
-    Stop();
-}
diff --git a/third_party/allwpilib_2017/wpilibc/athena/src/Counter.cpp b/third_party/allwpilib_2017/wpilibc/athena/src/Counter.cpp
index ca13452..f580c1e 100644
--- a/third_party/allwpilib_2017/wpilibc/athena/src/Counter.cpp
+++ b/third_party/allwpilib_2017/wpilibc/athena/src/Counter.cpp
@@ -616,22 +616,3 @@
   HAL_SetCounterReverseDirection(m_counter, reverseDirection, &status);
   wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
 }
-
-void Counter::UpdateTable() {
-  if (m_table != nullptr) {
-    m_table->PutNumber("Value", Get());
-  }
-}
-
-void Counter::StartLiveWindowMode() {}
-
-void Counter::StopLiveWindowMode() {}
-
-std::string Counter::GetSmartDashboardType() const { return "Counter"; }
-
-void Counter::InitTable(std::shared_ptr<ITable> subTable) {
-  m_table = subTable;
-  UpdateTable();
-}
-
-std::shared_ptr<ITable> Counter::GetTable() const { return m_table; }
diff --git a/third_party/allwpilib_2017/wpilibc/athena/src/DigitalInput.cpp b/third_party/allwpilib_2017/wpilibc/athena/src/DigitalInput.cpp
index 2eeefbb..f7c3f8a 100644
--- a/third_party/allwpilib_2017/wpilibc/athena/src/DigitalInput.cpp
+++ b/third_party/allwpilib_2017/wpilibc/athena/src/DigitalInput.cpp
@@ -13,7 +13,6 @@
 #include "HAL/DIO.h"
 #include "HAL/HAL.h"
 #include "HAL/Ports.h"
-#include "LiveWindow/LiveWindow.h"
 #include "WPIErrors.h"
 
 using namespace frc;
@@ -46,7 +45,6 @@
     return;
   }
 
-  LiveWindow::GetInstance()->AddSensor("DigitalInput", channel, this);
   HAL_Report(HALUsageReporting::kResourceType_DigitalInput, channel);
 }
 
@@ -99,24 +97,3 @@
 AnalogTriggerType DigitalInput::GetAnalogTriggerTypeForRouting() const {
   return (AnalogTriggerType)0;
 }
-
-void DigitalInput::UpdateTable() {
-  if (m_table != nullptr) {
-    m_table->PutBoolean("Value", Get());
-  }
-}
-
-void DigitalInput::StartLiveWindowMode() {}
-
-void DigitalInput::StopLiveWindowMode() {}
-
-std::string DigitalInput::GetSmartDashboardType() const {
-  return "DigitalInput";
-}
-
-void DigitalInput::InitTable(std::shared_ptr<ITable> subTable) {
-  m_table = subTable;
-  UpdateTable();
-}
-
-std::shared_ptr<ITable> DigitalInput::GetTable() const { return m_table; }
diff --git a/third_party/allwpilib_2017/wpilibc/athena/src/DigitalOutput.cpp b/third_party/allwpilib_2017/wpilibc/athena/src/DigitalOutput.cpp
index 381df60..b46ef13 100644
--- a/third_party/allwpilib_2017/wpilibc/athena/src/DigitalOutput.cpp
+++ b/third_party/allwpilib_2017/wpilibc/athena/src/DigitalOutput.cpp
@@ -54,7 +54,6 @@
  * Free the resources associated with a digital output.
  */
 DigitalOutput::~DigitalOutput() {
-  if (m_table != nullptr) m_table->RemoveTableListener(this);
   if (StatusIsFatal()) return;
   // Disable the PWM in case it was running.
   DisablePWM();
@@ -228,34 +227,3 @@
 AnalogTriggerType DigitalOutput::GetAnalogTriggerTypeForRouting() const {
   return (AnalogTriggerType)0;
 }
-
-void DigitalOutput::ValueChanged(ITable* source, llvm::StringRef key,
-                                 std::shared_ptr<nt::Value> value, bool isNew) {
-  if (!value->IsBoolean()) return;
-  Set(value->GetBoolean());
-}
-
-void DigitalOutput::UpdateTable() {}
-
-void DigitalOutput::StartLiveWindowMode() {
-  if (m_table != nullptr) {
-    m_table->AddTableListener("Value", this, true);
-  }
-}
-
-void DigitalOutput::StopLiveWindowMode() {
-  if (m_table != nullptr) {
-    m_table->RemoveTableListener(this);
-  }
-}
-
-std::string DigitalOutput::GetSmartDashboardType() const {
-  return "Digital Output";
-}
-
-void DigitalOutput::InitTable(std::shared_ptr<ITable> subTable) {
-  m_table = subTable;
-  UpdateTable();
-}
-
-std::shared_ptr<ITable> DigitalOutput::GetTable() const { return m_table; }
diff --git a/third_party/allwpilib_2017/wpilibc/athena/src/DriverStation.cpp b/third_party/allwpilib_2017/wpilibc/athena/src/DriverStation.cpp
index fd91d95..95d20b7 100644
--- a/third_party/allwpilib_2017/wpilibc/athena/src/DriverStation.cpp
+++ b/third_party/allwpilib_2017/wpilibc/athena/src/DriverStation.cpp
@@ -97,9 +97,6 @@
     lock.release();
     if (axis >= HAL_kMaxJoystickAxes)
       wpi_setWPIError(BadJoystickAxis);
-    else
-      ReportJoystickUnpluggedWarning(
-          "Joystick Axis missing, check if all controllers are plugged in");
     return 0.0;
   }
 
@@ -122,9 +119,6 @@
     lock.unlock();
     if (pov >= HAL_kMaxJoystickPOVs)
       wpi_setWPIError(BadJoystickAxis);
-    else
-      ReportJoystickUnpluggedWarning(
-          "Joystick POV missing, check if all controllers are plugged in");
     return -1;
   }
 
@@ -159,17 +153,12 @@
     return false;
   }
   if (button == 0) {
-    ReportJoystickUnpluggedError(
-        "ERROR: Button indexes begin at 1 in WPILib for C++ and Java");
     return false;
   }
   std::unique_lock<priority_mutex> lock(m_joystickDataMutex);
   if (button > m_joystickButtons[stick].count) {
     // Unlock early so error printing isn't locked.
     lock.unlock();
-    ReportJoystickUnpluggedWarning(
-        "Joystick Button missing, check if all controllers are "
-        "plugged in");
     return false;
   }
 
@@ -597,31 +586,6 @@
   m_dsThread = std::thread(&DriverStation::Run, this);
 }
 
-/**
- * Reports errors related to unplugged joysticks
- * Throttles the errors so that they don't overwhelm the DS
- */
-void DriverStation::ReportJoystickUnpluggedError(llvm::StringRef message) {
-  double currentTime = Timer::GetFPGATimestamp();
-  if (currentTime > m_nextMessageTime) {
-    ReportError(message);
-    m_nextMessageTime = currentTime + JOYSTICK_UNPLUGGED_MESSAGE_INTERVAL;
-  }
-}
-
-/**
- * Reports errors related to unplugged joysticks.
- *
- * Throttles the errors so that they don't overwhelm the DS.
- */
-void DriverStation::ReportJoystickUnpluggedWarning(llvm::StringRef message) {
-  double currentTime = Timer::GetFPGATimestamp();
-  if (currentTime > m_nextMessageTime) {
-    ReportWarning(message);
-    m_nextMessageTime = currentTime + JOYSTICK_UNPLUGGED_MESSAGE_INTERVAL;
-  }
-}
-
 void DriverStation::Run() {
   m_isRunning = true;
   int period = 0;
diff --git a/third_party/allwpilib_2017/wpilibc/athena/src/Encoder.cpp b/third_party/allwpilib_2017/wpilibc/athena/src/Encoder.cpp
index 7fad9eb..1efe125 100644
--- a/third_party/allwpilib_2017/wpilibc/athena/src/Encoder.cpp
+++ b/third_party/allwpilib_2017/wpilibc/athena/src/Encoder.cpp
@@ -9,7 +9,6 @@
 
 #include "DigitalInput.h"
 #include "HAL/HAL.h"
-#include "LiveWindow/LiveWindow.h"
 #include "WPIErrors.h"
 
 using namespace frc;
@@ -44,8 +43,6 @@
 
   HAL_Report(HALUsageReporting::kResourceType_Encoder, GetFPGAIndex(),
              encodingType);
-  LiveWindow::GetInstance()->AddSensor("Encoder", m_aSource->GetChannel(),
-                                       this);
 }
 
 /**
@@ -492,36 +489,3 @@
   wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
   return val;
 }
-
-void Encoder::UpdateTable() {
-  if (m_table != nullptr) {
-    m_table->PutNumber("Speed", GetRate());
-    m_table->PutNumber("Distance", GetDistance());
-    int32_t status = 0;
-    double distancePerPulse =
-        HAL_GetEncoderDistancePerPulse(m_encoder, &status);
-    wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
-    m_table->PutNumber("Distance per Tick", distancePerPulse);
-  }
-}
-
-void Encoder::StartLiveWindowMode() {}
-
-void Encoder::StopLiveWindowMode() {}
-
-std::string Encoder::GetSmartDashboardType() const {
-  int32_t status = 0;
-  HAL_EncoderEncodingType type = HAL_GetEncoderEncodingType(m_encoder, &status);
-  wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
-  if (type == HAL_EncoderEncodingType::HAL_Encoder_k4X)
-    return "Quadrature Encoder";
-  else
-    return "Encoder";
-}
-
-void Encoder::InitTable(std::shared_ptr<ITable> subTable) {
-  m_table = subTable;
-  UpdateTable();
-}
-
-std::shared_ptr<ITable> Encoder::GetTable() const { return m_table; }
diff --git a/third_party/allwpilib_2017/wpilibc/athena/src/PWM.cpp b/third_party/allwpilib_2017/wpilibc/athena/src/PWM.cpp
index 4883e16..41f63b5 100644
--- a/third_party/allwpilib_2017/wpilibc/athena/src/PWM.cpp
+++ b/third_party/allwpilib_2017/wpilibc/athena/src/PWM.cpp
@@ -70,8 +70,6 @@
 
   HAL_FreePWMPort(m_handle, &status);
   wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
-
-  if (m_table != nullptr) m_table->RemoveTableListener(this);
 }
 
 /**
@@ -311,38 +309,3 @@
   HAL_LatchPWMZero(m_handle, &status);
   wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
 }
-
-void PWM::ValueChanged(ITable* source, llvm::StringRef key,
-                       std::shared_ptr<nt::Value> value, bool isNew) {
-  if (!value->IsDouble()) return;
-  SetSpeed(value->GetDouble());
-}
-
-void PWM::UpdateTable() {
-  if (m_table != nullptr) {
-    m_table->PutNumber("Value", GetSpeed());
-  }
-}
-
-void PWM::StartLiveWindowMode() {
-  SetSpeed(0);
-  if (m_table != nullptr) {
-    m_table->AddTableListener("Value", this, true);
-  }
-}
-
-void PWM::StopLiveWindowMode() {
-  SetSpeed(0);
-  if (m_table != nullptr) {
-    m_table->RemoveTableListener(this);
-  }
-}
-
-std::string PWM::GetSmartDashboardType() const { return "Speed Controller"; }
-
-void PWM::InitTable(std::shared_ptr<ITable> subTable) {
-  m_table = subTable;
-  UpdateTable();
-}
-
-std::shared_ptr<ITable> PWM::GetTable() const { return m_table; }
diff --git a/third_party/allwpilib_2017/wpilibc/athena/src/PowerDistributionPanel.cpp b/third_party/allwpilib_2017/wpilibc/athena/src/PowerDistributionPanel.cpp
index 870c92e..d0c0b7c 100644
--- a/third_party/allwpilib_2017/wpilibc/athena/src/PowerDistributionPanel.cpp
+++ b/third_party/allwpilib_2017/wpilibc/athena/src/PowerDistributionPanel.cpp
@@ -12,7 +12,6 @@
 #include "HAL/HAL.h"
 #include "HAL/PDP.h"
 #include "HAL/Ports.h"
-#include "LiveWindow/LiveWindow.h"
 #include "WPIErrors.h"
 
 using namespace frc;
@@ -176,43 +175,3 @@
     wpi_setWPIErrorWithContext(Timeout, "");
   }
 }
-
-void PowerDistributionPanel::UpdateTable() {
-  if (m_table != nullptr) {
-    m_table->PutNumber("Chan0", GetCurrent(0));
-    m_table->PutNumber("Chan1", GetCurrent(1));
-    m_table->PutNumber("Chan2", GetCurrent(2));
-    m_table->PutNumber("Chan3", GetCurrent(3));
-    m_table->PutNumber("Chan4", GetCurrent(4));
-    m_table->PutNumber("Chan5", GetCurrent(5));
-    m_table->PutNumber("Chan6", GetCurrent(6));
-    m_table->PutNumber("Chan7", GetCurrent(7));
-    m_table->PutNumber("Chan8", GetCurrent(8));
-    m_table->PutNumber("Chan9", GetCurrent(9));
-    m_table->PutNumber("Chan10", GetCurrent(10));
-    m_table->PutNumber("Chan11", GetCurrent(11));
-    m_table->PutNumber("Chan12", GetCurrent(12));
-    m_table->PutNumber("Chan13", GetCurrent(13));
-    m_table->PutNumber("Chan14", GetCurrent(14));
-    m_table->PutNumber("Chan15", GetCurrent(15));
-    m_table->PutNumber("Voltage", GetVoltage());
-    m_table->PutNumber("TotalCurrent", GetTotalCurrent());
-  }
-}
-
-void PowerDistributionPanel::StartLiveWindowMode() {}
-
-void PowerDistributionPanel::StopLiveWindowMode() {}
-
-std::string PowerDistributionPanel::GetSmartDashboardType() const {
-  return "PowerDistributionPanel";
-}
-
-void PowerDistributionPanel::InitTable(std::shared_ptr<ITable> subTable) {
-  m_table = subTable;
-  UpdateTable();
-}
-
-std::shared_ptr<ITable> PowerDistributionPanel::GetTable() const {
-  return m_table;
-}
diff --git a/third_party/allwpilib_2017/wpilibc/athena/src/Relay.cpp b/third_party/allwpilib_2017/wpilibc/athena/src/Relay.cpp
index 33cc876..201963b 100644
--- a/third_party/allwpilib_2017/wpilibc/athena/src/Relay.cpp
+++ b/third_party/allwpilib_2017/wpilibc/athena/src/Relay.cpp
@@ -12,7 +12,6 @@
 
 #include "HAL/HAL.h"
 #include "HAL/Ports.h"
-#include "LiveWindow/LiveWindow.h"
 #include "MotorSafetyHelper.h"
 #include "WPIErrors.h"
 
@@ -86,8 +85,6 @@
 
   m_safetyHelper = std::make_unique<MotorSafetyHelper>(this);
   m_safetyHelper->SetSafetyEnabled(false);
-
-  LiveWindow::GetInstance()->AddActuator("Relay", 1, m_channel, this);
 }
 
 /**
@@ -102,8 +99,6 @@
   // ignore errors, as we want to make sure a free happens.
   if (m_forwardHandle != HAL_kInvalidHandle) HAL_FreeRelayPort(m_forwardHandle);
   if (m_reverseHandle != HAL_kInvalidHandle) HAL_FreeRelayPort(m_reverseHandle);
-
-  if (m_table != nullptr) m_table->RemoveTableListener(this);
 }
 
 /**
@@ -265,51 +260,3 @@
 void Relay::GetDescription(std::ostringstream& desc) const {
   desc << "Relay " << GetChannel();
 }
-
-void Relay::ValueChanged(ITable* source, llvm::StringRef key,
-                         std::shared_ptr<nt::Value> value, bool isNew) {
-  if (!value->IsString()) return;
-  if (value->GetString() == "Off")
-    Set(kOff);
-  else if (value->GetString() == "Forward")
-    Set(kForward);
-  else if (value->GetString() == "Reverse")
-    Set(kReverse);
-  else if (value->GetString() == "On")
-    Set(kOn);
-}
-
-void Relay::UpdateTable() {
-  if (m_table != nullptr) {
-    if (Get() == kOn) {
-      m_table->PutString("Value", "On");
-    } else if (Get() == kForward) {
-      m_table->PutString("Value", "Forward");
-    } else if (Get() == kReverse) {
-      m_table->PutString("Value", "Reverse");
-    } else {
-      m_table->PutString("Value", "Off");
-    }
-  }
-}
-
-void Relay::StartLiveWindowMode() {
-  if (m_table != nullptr) {
-    m_table->AddTableListener("Value", this, true);
-  }
-}
-
-void Relay::StopLiveWindowMode() {
-  if (m_table != nullptr) {
-    m_table->RemoveTableListener(this);
-  }
-}
-
-std::string Relay::GetSmartDashboardType() const { return "Relay"; }
-
-void Relay::InitTable(std::shared_ptr<ITable> subTable) {
-  m_table = subTable;
-  UpdateTable();
-}
-
-std::shared_ptr<ITable> Relay::GetTable() const { return m_table; }
diff --git a/third_party/allwpilib_2017/wpilibc/athena/src/RobotBase.cpp b/third_party/allwpilib_2017/wpilibc/athena/src/RobotBase.cpp
index e242d03..b821fa5 100644
--- a/third_party/allwpilib_2017/wpilibc/athena/src/RobotBase.cpp
+++ b/third_party/allwpilib_2017/wpilibc/athena/src/RobotBase.cpp
@@ -14,10 +14,8 @@
 #include "HLUsageReporting.h"
 #include "Internal/HardwareHLReporting.h"
 #include "RobotState.h"
-#include "SmartDashboard/SmartDashboard.h"
 #include "Utility.h"
 #include "WPILibVersion.h"
-#include "networktables/NetworkTable.h"
 
 using namespace frc;
 
@@ -40,11 +38,6 @@
   RobotState::SetImplementation(DriverStation::GetInstance());
   HLUsageReporting::SetImplementation(new HardwareHLReporting());
 
-  NetworkTable::SetNetworkIdentity("Robot");
-  NetworkTable::SetPersistentFilename("/home/lvuser/networktables.ini");
-
-  SmartDashboard::init();
-
   std::FILE* file = nullptr;
   file = std::fopen("/tmp/frc_versions/FRC_Lib_Version.ini", "w");
 
diff --git a/third_party/allwpilib_2017/wpilibc/athena/src/SampleRobot.cpp b/third_party/allwpilib_2017/wpilibc/athena/src/SampleRobot.cpp
index 014730e..eaa828b 100644
--- a/third_party/allwpilib_2017/wpilibc/athena/src/SampleRobot.cpp
+++ b/third_party/allwpilib_2017/wpilibc/athena/src/SampleRobot.cpp
@@ -8,9 +8,7 @@
 #include "SampleRobot.h"
 
 #include "DriverStation.h"
-#include "LiveWindow/LiveWindow.h"
 #include "Timer.h"
-#include "networktables/NetworkTable.h"
 
 using namespace frc;
 
@@ -100,15 +98,9 @@
  * robot to be enabled again.
  */
 void SampleRobot::StartCompetition() {
-  LiveWindow* lw = LiveWindow::GetInstance();
-
   HAL_Report(HALUsageReporting::kResourceType_Framework,
              HALUsageReporting::kFramework_Simple);
 
-  NetworkTable::GetTable("LiveWindow")
-      ->GetSubTable("~STATUS~")
-      ->PutBoolean("LW Enabled", false);
-
   RobotInit();
 
   // Tell the DS that the robot is ready to be enabled
@@ -118,7 +110,6 @@
 
   if (!m_robotMainOverridden) {
     // first and one-time initialization
-    lw->SetEnabled(false);
 
     while (true) {
       if (IsDisabled()) {
@@ -132,12 +123,10 @@
         m_ds.InAutonomous(false);
         while (IsAutonomous() && IsEnabled()) m_ds.WaitForData();
       } else if (IsTest()) {
-        lw->SetEnabled(true);
         m_ds.InTest(true);
         Test();
         m_ds.InTest(false);
         while (IsTest() && IsEnabled()) m_ds.WaitForData();
-        lw->SetEnabled(false);
       } else {
         m_ds.InOperatorControl(true);
         OperatorControl();
diff --git a/third_party/allwpilib_2017/wpilibc/athena/src/Solenoid.cpp b/third_party/allwpilib_2017/wpilibc/athena/src/Solenoid.cpp
index d70c3c3..324e673 100644
--- a/third_party/allwpilib_2017/wpilibc/athena/src/Solenoid.cpp
+++ b/third_party/allwpilib_2017/wpilibc/athena/src/Solenoid.cpp
@@ -12,7 +12,6 @@
 
 #include "HAL/HAL.h"
 #include "HAL/Ports.h"
-#include "LiveWindow/LiveWindow.h"
 #include "WPIErrors.h"
 
 using namespace frc;
@@ -55,8 +54,6 @@
     return;
   }
 
-  LiveWindow::GetInstance()->AddActuator("Solenoid", m_moduleNumber, m_channel,
-                                         this);
   HAL_Report(HALUsageReporting::kResourceType_Solenoid, m_channel,
              m_moduleNumber);
 }
@@ -66,7 +63,6 @@
  */
 Solenoid::~Solenoid() {
   HAL_FreeSolenoidPort(m_solenoidHandle);
-  if (m_table != nullptr) m_table->RemoveTableListener(this);
 }
 
 /**
@@ -108,38 +104,3 @@
   int value = GetPCMSolenoidBlackList(m_moduleNumber) & (1 << m_channel);
   return (value != 0);
 }
-
-void Solenoid::ValueChanged(ITable* source, llvm::StringRef key,
-                            std::shared_ptr<nt::Value> value, bool isNew) {
-  if (!value->IsBoolean()) return;
-  Set(value->GetBoolean());
-}
-
-void Solenoid::UpdateTable() {
-  if (m_table != nullptr) {
-    m_table->PutBoolean("Value", Get());
-  }
-}
-
-void Solenoid::StartLiveWindowMode() {
-  Set(false);
-  if (m_table != nullptr) {
-    m_table->AddTableListener("Value", this, true);
-  }
-}
-
-void Solenoid::StopLiveWindowMode() {
-  Set(false);
-  if (m_table != nullptr) {
-    m_table->RemoveTableListener(this);
-  }
-}
-
-std::string Solenoid::GetSmartDashboardType() const { return "Solenoid"; }
-
-void Solenoid::InitTable(std::shared_ptr<ITable> subTable) {
-  m_table = subTable;
-  UpdateTable();
-}
-
-std::shared_ptr<ITable> Solenoid::GetTable() const { return m_table; }
diff --git a/third_party/allwpilib_2017/wpilibc/athena/src/SolenoidBase.cpp b/third_party/allwpilib_2017/wpilibc/athena/src/SolenoidBase.cpp
index 4b53744..e61208e 100644
--- a/third_party/allwpilib_2017/wpilibc/athena/src/SolenoidBase.cpp
+++ b/third_party/allwpilib_2017/wpilibc/athena/src/SolenoidBase.cpp
@@ -32,6 +32,12 @@
   return value;
 }
 
+void SolenoidBase::SetAll(int value, int module) {
+  int32_t status = 0;
+  HAL_SetAllSolenoids(static_cast<int>(module), value, &status);
+  wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
+}
+
 /**
  * Reads complete solenoid blacklist for all 8 solenoids as a single byte.
  *
diff --git a/third_party/allwpilib_2017/wpilibc/athena/src/Talon.cpp b/third_party/allwpilib_2017/wpilibc/athena/src/Talon.cpp
index 58bc109..8143865 100644
--- a/third_party/allwpilib_2017/wpilibc/athena/src/Talon.cpp
+++ b/third_party/allwpilib_2017/wpilibc/athena/src/Talon.cpp
@@ -8,7 +8,6 @@
 #include "Talon.h"
 
 #include "HAL/HAL.h"
-#include "LiveWindow/LiveWindow.h"
 
 using namespace frc;
 
@@ -38,5 +37,4 @@
   SetZeroLatch();
 
   HAL_Report(HALUsageReporting::kResourceType_Talon, GetChannel());
-  LiveWindow::GetInstance()->AddActuator("Talon", GetChannel(), this);
 }
diff --git a/third_party/allwpilib_2017/wpilibc/shared/include/GyroBase.h b/third_party/allwpilib_2017/wpilibc/shared/include/GyroBase.h
index 03b4617..3d55241 100644
--- a/third_party/allwpilib_2017/wpilibc/shared/include/GyroBase.h
+++ b/third_party/allwpilib_2017/wpilibc/shared/include/GyroBase.h
@@ -10,7 +10,6 @@
 #include <memory>
 #include <string>
 
-#include "LiveWindow/LiveWindowSendable.h"
 #include "PIDSource.h"
 #include "SensorBase.h"
 #include "interfaces/Gyro.h"
@@ -23,23 +22,12 @@
  */
 class GyroBase : public Gyro,
                  public SensorBase,
-                 public PIDSource,
-                 public LiveWindowSendable {
+                 public PIDSource {
  public:
   virtual ~GyroBase() = default;
 
   // PIDSource interface
   double PIDGet() override;
-
-  void UpdateTable() override;
-  void StartLiveWindowMode() override;
-  void StopLiveWindowMode() override;
-  std::string GetSmartDashboardType() const override;
-  void InitTable(std::shared_ptr<ITable> subTable) override;
-  std::shared_ptr<ITable> GetTable() const override;
-
- private:
-  std::shared_ptr<ITable> m_table;
 };
 
 }  // namespace frc
diff --git a/third_party/ntcore_2017/BUILD b/third_party/ntcore_2017/BUILD
new file mode 100644
index 0000000..82f3545
--- /dev/null
+++ b/third_party/ntcore_2017/BUILD
@@ -0,0 +1,28 @@
+licenses(['notice'])
+
+cc_library(
+  name = 'ntcore',
+  visibility = ['//visibility:public'],
+  srcs = glob([
+    'src/**/*.cpp',
+    'src/**/*.h',
+    'wpiutil/src/**/*.cpp',
+  ], exclude = [
+    'src/networktables/**',
+  ]),
+  copts = [
+    '-Wno-switch-enum',
+    '-Wno-cast-align',
+  ],
+  hdrs = glob([
+    'include/**/*.h',
+    'wpiutil/include/**/*.h',
+  ]),
+  includes = [
+    'include',
+    'wpiutil/include',
+  ],
+  linkopts = [
+    '-lpthread',
+  ],
+)
diff --git a/tools/build_rules/gtk_dependent.bzl b/tools/build_rules/gtk_dependent.bzl
new file mode 100644
index 0000000..0a21a9a
--- /dev/null
+++ b/tools/build_rules/gtk_dependent.bzl
@@ -0,0 +1,9 @@
+disable_gtk_binaries = True
+
+def gtk_dependent_cc_library(**kwargs):
+  if not disable_gtk_binaries:
+    native.cc_library(**kwargs)
+
+def gtk_dependent_cc_binary(**kwargs):
+  if not disable_gtk_binaries:
+    native.cc_binary(**kwargs)
diff --git a/y2017/control_loops/superstructure/superstructure.q b/y2017/control_loops/superstructure/superstructure.q
index a3b2715..5fe207e 100644
--- a/y2017/control_loops/superstructure/superstructure.q
+++ b/y2017/control_loops/superstructure/superstructure.q
@@ -52,11 +52,12 @@
 };
 
 struct TurretGoal {
-  // An azimuth angle of zero means the turrent faces toward the front of the
+  // An angle of zero means the turrent faces toward the front of the
   // robot where the intake is located. The angle increases when the turret
-  // turns clockwise, and decreases when the turrent turns counter-clockwise.
+  // turns clockwise (towards right from the front), and decreases when
+  // the turrent turns counter-clockwise (towards left from the front).
   // These are from a top view above the robot.
-  double angle_azimuth;
+  double angle;
 
   // Caps on velocity/acceleration for profiling. 0 for the default.
   .frc971.ProfileParameters profile_params;
@@ -109,7 +110,7 @@
   bool estopped;
 
   // Estimate angles and angular velocities.
-  JointState azimuth;
+  JointState turret;
 };
 
 struct HoodStatus {
@@ -172,9 +173,9 @@
     // Serializer angle in radians.
     double theta_serializer;
 
-    // The sensor readings for the azimuth. The units and sign are defined the
+    // The sensor readings for the turret. The units and sign are defined the
     // same as what's in the Goal message.
-    .frc971.PotAndAbsolutePosition azimuth_turret;
+    .frc971.PotAndAbsolutePosition turret;
 
     // Position of the hood in radians
     double theta_hood;
@@ -194,7 +195,7 @@
     // Roller on the serializer
     float voltage_serializer_rollers;
 
-    float voltage_azimuth_turret;
+    float voltage_turret;
     float voltage_hood;
   };