Merge "Copy flatbuffers efficiently and respecting DAGs."
diff --git a/aos/BUILD b/aos/BUILD
index 243e7f3..2761cbf 100644
--- a/aos/BUILD
+++ b/aos/BUILD
@@ -5,8 +5,8 @@
     name = "prime_binaries",
     srcs = [
         "//aos:aos_dump",
-        "//aos/starter",
         "//aos:aos_dump_autocomplete.sh",
+        "//aos/starter",
     ],
     visibility = ["//visibility:public"],
 )
@@ -167,6 +167,7 @@
     deps = [
         "//aos/ipc_lib:aos_sync",
         "//aos/time",
+        "//aos/type_traits",
         "@com_github_google_glog//:glog",
     ],
 )
diff --git a/aos/aos_dump.cc b/aos/aos_dump.cc
index 8e4c6f3..3484b73 100644
--- a/aos/aos_dump.cc
+++ b/aos/aos_dump.cc
@@ -29,7 +29,7 @@
     _bash_autocomplete, false,
     "Internal use: Outputs channel list for use with autocomplete script.");
 DEFINE_string(_bash_autocomplete_word, "",
-              "Intenal use: Index of current word being autocompleted");
+              "Intenal use: Current word being autocompleted");
 
 namespace {
 
@@ -70,15 +70,17 @@
                   std::string_view channel_name,
                   std::string_view message_type) {
   const bool unique_match =
-      std::count_if(
-          config_msg->channels()->begin(), config_msg->channels()->end(),
-          [channel_name, message_type](const aos::Channel *channel) {
-            return channel->name()->string_view() == channel_name &&
-                   channel->type()->string_view() == message_type;
-          }) == 1;
+      std::count_if(config_msg->channels()->begin(),
+                    config_msg->channels()->end(),
+                    [channel_name, message_type](const aos::Channel *channel) {
+                      return channel->name()->string_view() == channel_name &&
+                             channel->type()->string_view() == message_type;
+                    }) == 1;
 
-  const bool editing_message = !channel_name.empty() && FLAGS__bash_autocomplete_word == message_type;
-  const bool editing_channel = !editing_message && FLAGS__bash_autocomplete_word == channel_name;
+  const bool editing_message =
+      !channel_name.empty() && FLAGS__bash_autocomplete_word == message_type;
+  const bool editing_channel =
+      !editing_message && FLAGS__bash_autocomplete_word == channel_name;
 
   std::cout << "COMPREPLY=(";
 
@@ -117,8 +119,9 @@
 
 bool EndsWith(std::string_view str, std::string_view ending) {
   const std::size_t offset = str.size() - ending.size();
-  return str.size() >= ending.size() && std::equal(str.begin() + offset, str.end(),
-                                   ending.begin(), ending.end());
+  return str.size() >= ending.size() &&
+         std::equal(str.begin() + offset, str.end(), ending.begin(),
+                    ending.end());
 }
 
 }  // namespace
diff --git a/aos/ipc_lib/BUILD b/aos/ipc_lib/BUILD
index 9532e82..0fb307a 100644
--- a/aos/ipc_lib/BUILD
+++ b/aos/ipc_lib/BUILD
@@ -198,7 +198,6 @@
         ":signalfd",
         "//aos:event",
         "//aos/events:epoll",
-        "//aos/libc:aos_strsignal",
         "//aos/testing:googletest",
         "//aos/testing:prevent_exit",
     ],
diff --git a/aos/time/BUILD b/aos/time/BUILD
index 7051564..c9f8d6d 100644
--- a/aos/time/BUILD
+++ b/aos/time/BUILD
@@ -10,8 +10,6 @@
     ],
     visibility = ["//visibility:public"],
     deps = [
-        "//aos:macros",
-        "//aos/type_traits",
         "@com_github_google_glog//:glog",
     ],
 )
@@ -28,8 +26,6 @@
     restricted_to = mcu_cpus,
     visibility = ["//visibility:public"],
     deps = [
-        "//aos:macros",
-        "//aos/type_traits",
         "//motors/core",
     ],
 )
diff --git a/aos/time/time.h b/aos/time/time.h
index 0bd0708..36c7ad2 100644
--- a/aos/time/time.h
+++ b/aos/time/time.h
@@ -9,10 +9,6 @@
 #include <optional>
 #include <ostream>
 #include <thread>
-#include <type_traits>
-
-#include "aos/macros.h"
-#include "aos/type_traits/type_traits.h"
 
 namespace aos {
 
diff --git a/frc971/config/setup_roborio.sh b/frc971/config/setup_roborio.sh
index f288df8..36d91b7 100755
--- a/frc971/config/setup_roborio.sh
+++ b/frc971/config/setup_roborio.sh
@@ -32,6 +32,8 @@
   ssh "admin@${ROBOT_HOSTNAME}" ln -s /media/sda1 logs
   ssh "admin@${ROBOT_HOSTNAME}" mkdir robot_code
   ssh "admin@${ROBOT_HOSTNAME}" ln -s /media/sda1/aos_log-current robot_code/aos_log-current
+  echo "Adding aos_dump autocomplete to profile"
+  ssh "admin@${ROBOT_HOSTNAME}" 'echo "if [ -f /home/admin/robot_code/aos_dump_autocomplete.sh ]; then; source /home/admin/robot_code/aos_dump_autocomplete.sh; fi;" >> /etc/profile'
 fi
 
 if [[ "$(ssh admin@${ROBOT_HOSTNAME} uname -r)" != "4.14.87-rt49-cg-7.0.0f0-xilinx-zynq-189" ]]; then
diff --git a/frc971/control_loops/control_loops.fbs b/frc971/control_loops/control_loops.fbs
index a95d407..4377541 100644
--- a/frc971/control_loops/control_loops.fbs
+++ b/frc971/control_loops/control_loops.fbs
@@ -61,6 +61,23 @@
   absolute_encoder:double;
 }
 
+// Represents all of the data for an absolute and relative encoder pair,
+// along with an absolute encoder.
+// They operate similarly to a pot and absolute encoder, but another absolute
+// encoder is used in place of the potentiometer.
+// The units on all of the positions are the same.
+// The relative encoder values are relative to where the encoder was at some
+// arbitrary point in time.
+table AbsoluteAndAbsolutePosition {
+  // Current position read from each encoder.
+  encoder:double;
+  absolute_encoder:double;
+
+  // Current position read from the single turn absolute encoder.
+  // This can not turn more than one rotation.
+  single_turn_absolute_encoder:double;
+}
+
 // Represents all of the data for a single encoder.
 // The relative encoder values are relative to where the encoder was at some
 // arbitrary point in time.
diff --git a/frc971/control_loops/position_sensor_sim.cc b/frc971/control_loops/position_sensor_sim.cc
index b605196..222af5d 100644
--- a/frc971/control_loops/position_sensor_sim.cc
+++ b/frc971/control_loops/position_sensor_sim.cc
@@ -56,22 +56,26 @@
  * remainder of the graph.
  */
 
-PositionSensorSimulator::PositionSensorSimulator(double distance_per_revolution,
-                                                 unsigned int noise_seed)
+PositionSensorSimulator::PositionSensorSimulator(
+    double distance_per_revolution, double single_turn_distance_per_revolution,
+    unsigned int noise_seed)
     : lower_index_edge_(distance_per_revolution, noise_seed),
       upper_index_edge_(distance_per_revolution, noise_seed),
-      distance_per_revolution_(distance_per_revolution) {
+      distance_per_revolution_(distance_per_revolution),
+      single_turn_distance_per_revolution_(
+          single_turn_distance_per_revolution) {
   Initialize(0.0, 0.0);
 }
 
 void PositionSensorSimulator::Initialize(
-    double start_position, double pot_noise_stddev,
-    double known_index_position /* = 0*/,
-    double known_absolute_encoder_pos /* = 0*/) {
+    double start_position, double pot_noise_stddev, double known_index_position,
+    double known_absolute_encoder_pos,
+    double single_turn_known_absolute_encoder_pos) {
   InitializeHallEffectAndPosition(start_position, known_index_position,
                                   known_index_position);
 
   known_absolute_encoder_ = known_absolute_encoder_pos;
+  single_turn_known_absolute_encoder_ = single_turn_known_absolute_encoder_pos;
 
   lower_index_edge_.mutable_pot_noise()->set_standard_deviation(
       pot_noise_stddev);
@@ -180,15 +184,7 @@
   builder->add_pot(lower_index_edge_.mutable_pot_noise()->AddNoiseToSample(
       current_position_));
   builder->add_encoder(current_position_ - 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.
-  double absolute_encoder = ::std::remainder(
-      current_position_ + known_absolute_encoder_, distance_per_revolution_);
-  if (absolute_encoder < 0) {
-    absolute_encoder += distance_per_revolution_;
-  }
-  builder->add_absolute_encoder(absolute_encoder);
+  builder->add_absolute_encoder(WrapAbsoluteEncoder());
   return builder->Finish();
 }
 
@@ -197,15 +193,24 @@
 PositionSensorSimulator::GetSensorValues<AbsolutePositionBuilder>(
     AbsolutePositionBuilder *builder) {
   builder->add_encoder(current_position_ - 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.
-  double absolute_encoder = ::std::remainder(
-      current_position_ + known_absolute_encoder_, distance_per_revolution_);
-  if (absolute_encoder < 0) {
-    absolute_encoder += distance_per_revolution_;
+  builder->add_absolute_encoder(WrapAbsoluteEncoder());
+  return builder->Finish();
+}
+
+template <>
+flatbuffers::Offset<AbsoluteAndAbsolutePosition>
+PositionSensorSimulator::GetSensorValues<AbsoluteAndAbsolutePositionBuilder>(
+    AbsoluteAndAbsolutePositionBuilder *builder) {
+  builder->add_encoder(current_position_ - start_position_);
+  builder->add_absolute_encoder(WrapAbsoluteEncoder());
+
+  double single_turn_absolute_encoder =
+      ::std::remainder(current_position_ + single_turn_known_absolute_encoder_,
+                       single_turn_distance_per_revolution_);
+  if (single_turn_absolute_encoder < 0) {
+    single_turn_absolute_encoder += single_turn_distance_per_revolution_;
   }
-  builder->add_absolute_encoder(absolute_encoder);
+  builder->add_single_turn_absolute_encoder(single_turn_absolute_encoder);
   return builder->Finish();
 }
 
diff --git a/frc971/control_loops/position_sensor_sim.h b/frc971/control_loops/position_sensor_sim.h
index faa5dd5..77e2d5f 100644
--- a/frc971/control_loops/position_sensor_sim.h
+++ b/frc971/control_loops/position_sensor_sim.h
@@ -21,11 +21,18 @@
   //     NOTE: When retrieving the sensor values for a PotAndAbsolutePosition
   //     message this field represents the interval between when the absolute
   //     encoder reads 0.
+  // single_turn_distance_per_revolution:
+  //    This is only used when retrieving
+  //    sensor values for an AbsoluteAndAbsolutePosition.
+  //    The interval between when the single turn abosolute encoder reads 0.
+  //    This will be the whole range of the single turn absolute encoder
+  //    as it cannot turn more than once.
   // noise_seed:
   //     The seed to feed into the random number generator for the potentiometer
   //     values.
   PositionSensorSimulator(
       double distance_per_revolution,
+      double single_turn_distance_per_revolution = 0.0,
       unsigned int noise_seed = ::aos::testing::RandomSeed());
 
   // Set new parameters for the sensors. This is useful for unit tests to change
@@ -37,9 +44,15 @@
   //                   This specifies the standard deviation of that
   //                   distribution.
   // known_index_pos: The absolute position of an index pulse.
+  // known_absolute_encoder_pos: The readout of the absolute encoder when the
+  //                             robot's mechanism is at zero.
+  // single_turn_known_absolute_encoder_pos: The readout of the single turn
+  //                                         absolute encoder when the robot's
+  //                                         mechanism is at zero.
   void Initialize(double start_position, double pot_noise_stddev,
                   double known_index_pos = 0.0,
-                  double known_absolute_encoder_pos = 0.0);
+                  double known_absolute_encoder_pos = 0.0,
+                  double single_turn_known_absolute_encoder_pos = 0.0);
 
   // Initializes a sensor simulation which is pretending to be a hall effect +
   // encoder setup.  This is assuming that the hall effect sensor triggers once
@@ -158,15 +171,33 @@
     GaussianNoise pot_noise_;
   };
 
+  double WrapAbsoluteEncoder() {
+    // 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.
+    double absolute_encoder = ::std::remainder(
+        current_position_ + known_absolute_encoder_, distance_per_revolution_);
+    if (absolute_encoder < 0) {
+      absolute_encoder += distance_per_revolution_;
+    }
+    return absolute_encoder;
+  }
+
   IndexEdge lower_index_edge_;
   IndexEdge upper_index_edge_;
 
   // Distance between index pulses on the mechanism.
   double distance_per_revolution_;
 
+  // The distance that the mechanism travels for one revolution of the single
+  // turn absolue encoder
+  double single_turn_distance_per_revolution_;
+
   // The readout of the absolute encoder when the robot's mechanism is at
   // zero.
   double known_absolute_encoder_;
+  // The other absolute encoder's value when the mechanism is at zero.
+  double single_turn_known_absolute_encoder_;
   // Current position of the mechanism relative to absolute zero.
   double current_position_;
   // 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 424a3af..e8beabf 100644
--- a/frc971/control_loops/position_sensor_sim_test.cc
+++ b/frc971/control_loops/position_sensor_sim_test.cc
@@ -409,5 +409,56 @@
   }
 }
 
+TEST_F(PositionSensorSimTest, AbsoluteAndAbsoluteEncoderTest) {
+  const double full_range = 4.0;
+
+  const double distance_per_revolution = 1.0;
+  const double single_turn_distance_per_revolution = full_range;
+
+  const double start_pos = 2.1;
+
+  const double measured_absolute_position = 0.3 * distance_per_revolution;
+  const double single_turn_measured_absolute_position =
+      0.4 * single_turn_distance_per_revolution;
+
+  PositionSensorSimulator sim(distance_per_revolution,
+                              single_turn_distance_per_revolution);
+  sim.Initialize(start_pos, 0 /* Pot noise N/A */, 0.0,
+                 measured_absolute_position,
+                 single_turn_measured_absolute_position);
+
+  flatbuffers::FlatBufferBuilder fbb;
+
+  sim.MoveTo(0.0);
+  const AbsoluteAndAbsolutePosition *position =
+      sim.FillSensorValues<AbsoluteAndAbsolutePosition>(&fbb);
+
+  EXPECT_NEAR(position->encoder(), -start_pos, 1e-10);
+  EXPECT_NEAR(position->absolute_encoder(), measured_absolute_position, 1e-10);
+  EXPECT_NEAR(position->single_turn_absolute_encoder(),
+              single_turn_measured_absolute_position, 1e-10);
+
+  sim.MoveTo(1.0);
+  position = sim.FillSensorValues<AbsoluteAndAbsolutePosition>(&fbb);
+
+  EXPECT_NEAR(position->encoder(), 1.0 - start_pos, 1e-10);
+
+  // because it has moved to exactly distance_per_revolution, it will wrap,
+  // and come back to measured_absolute_position.
+  EXPECT_NEAR(position->absolute_encoder(), measured_absolute_position, 1e-10);
+  EXPECT_NEAR(position->single_turn_absolute_encoder(), 2.6, 1e-10);
+
+  sim.MoveTo(2.5);
+  position = sim.FillSensorValues<AbsoluteAndAbsolutePosition>(&fbb);
+
+  EXPECT_NEAR(position->encoder(), 2.5 - start_pos, 1e-10);
+
+  // (position + measured_absolute_position) % distance_per_revolution,
+  EXPECT_NEAR(position->absolute_encoder(), 0.8, 1e-10);
+  // (position + single_turn_measured_absolute_position) %
+  // single_turn_distance_per_revolution,
+  EXPECT_NEAR(position->single_turn_absolute_encoder(), 0.1, 1e-10);
+}
+
 }  // namespace control_loops
 }  // namespace frc971