Merge "make the claw and fridge tests use standard infrastructure"
diff --git a/aos/linux_code/ipc_lib/queue.cc b/aos/linux_code/ipc_lib/queue.cc
index 9219192..81ad51b 100644
--- a/aos/linux_code/ipc_lib/queue.cc
+++ b/aos/linux_code/ipc_lib/queue.cc
@@ -169,7 +169,7 @@
   MessageHeader *header = __atomic_load_n(&free_messages_, __ATOMIC_RELAXED);
   do {
     if (__builtin_expect(header == nullptr, 0)) {
-      LOG(FATAL, "overused pool of queue %p\n", this);
+      LOG(FATAL, "overused pool of queue %p (%s)\n", this, name_);
     }
   } while (__builtin_expect(
       !__atomic_compare_exchange_n(&free_messages_, &header, header->next, true,
@@ -193,7 +193,8 @@
                 "need to revalidate size/alignent assumptions");
 
   if (queue_length < 1) {
-    LOG(FATAL, "queue length %d needs to be at least 1\n", queue_length);
+    LOG(FATAL, "queue length %d of %s needs to be at least 1\n", queue_length,
+        name_);
   }
 
   const size_t name_size = strlen(name) + 1;
diff --git a/frc971/wpilib/encoder_and_potentiometer.cc b/frc971/wpilib/encoder_and_potentiometer.cc
new file mode 100644
index 0000000..800c49b
--- /dev/null
+++ b/frc971/wpilib/encoder_and_potentiometer.cc
@@ -0,0 +1,57 @@
+#include "frc971/wpilib/encoder_and_potentiometer.h"
+
+#include "aos/linux_code/init.h"
+#include "aos/common/logging/logging.h"
+
+namespace frc971 {
+namespace wpilib {
+
+void DMAEncoderAndPotentiometer::UpdateFromSample(const DMASample &sample) {
+  if (index_last_value_) {
+    // It was already true last time, so check if it's reset back to false yet.
+    index_last_value_ = sample.Get(index_.get());
+  } else if (sample.Get(index_.get())) {
+    // This sample is posedge, so record all the values.
+    index_last_value_ = true;
+    ++index_posedge_count_;
+    last_encoder_value_ = sample.GetRaw(encoder_.get());
+    last_potentiometer_voltage_ = sample.GetVoltage(potentiometer_.get());
+  }
+}
+
+void InterruptEncoderAndPotentiometer::Start() {
+  CHECK_NE(nullptr, encoder_);
+  CHECK_NE(nullptr, index_);
+  CHECK_NE(nullptr, potentiometer_);
+  CHECK_NE(0, priority_);
+  thread_ = ::std::thread(::std::ref(*this));
+}
+
+void InterruptEncoderAndPotentiometer::operator()() {
+  ::aos::SetCurrentThreadName("IntEncPot_" +
+                              ::std::to_string(potentiometer_->GetChannel()));
+
+  index_->RequestInterrupts();
+  index_->SetUpSourceEdge(true, false);
+
+  ::aos::SetCurrentThreadRealtimePriority(priority_);
+
+  InterruptableSensorBase::WaitResult result = InterruptableSensorBase::kBoth;
+  while (run_) {
+    result = index_->WaitForInterrupt(
+        0.1, result != InterruptableSensorBase::kTimeout);
+    if (result == InterruptableSensorBase::kTimeout) {
+      continue;
+    }
+
+    {
+      ::aos::MutexLocker locker(&mutex_);
+      last_potentiometer_voltage_ = potentiometer_->GetVoltage();
+      last_encoder_value_ = encoder_->GetRaw();
+      ++index_posedge_count_;
+    }
+  }
+}
+
+}  // namespace wpilib
+}  // namespace frc971
diff --git a/frc971/wpilib/encoder_and_potentiometer.h b/frc971/wpilib/encoder_and_potentiometer.h
new file mode 100644
index 0000000..82fa9a8
--- /dev/null
+++ b/frc971/wpilib/encoder_and_potentiometer.h
@@ -0,0 +1,169 @@
+#ifndef FRC971_ENCODER_AND_POTENTIOMETER_H_
+#define FRC971_ENCODER_AND_POTENTIOMETER_H_
+
+#include <atomic>
+#include <thread>
+
+#include "aos/common/macros.h"
+#include "aos/common/mutex.h"
+
+#include "Encoder.h"
+#include "DigitalSource.h"
+#include "AnalogInput.h"
+#include "dma.h"
+
+#include "frc971/wpilib/dma_edge_counting.h"
+
+namespace frc971 {
+namespace wpilib {
+
+// Latches values from an encoder and potentiometer on positive edges from
+// another input using an interrupt.
+class InterruptEncoderAndPotentiometer {
+ public:
+  // priority is the priority the thread will run at.
+  InterruptEncoderAndPotentiometer(int priority) : priority_(priority) {}
+
+  // Starts the thread running so it can receive interrupts.
+  void Start();
+
+  // Tells the thread to stop running and then waits for it to finish.
+  void Stop() {
+    run_ = false;
+    thread_.join();
+  }
+
+  // Loops until Stop() is called, reading interrupts.
+  // Designed to be called by ::std::thread internally.
+  void operator()();
+
+  // Returns the mutex which must be held while calling index_posedge_count(),
+  // last_encoder_value(), and last_potentiometer_voltage().
+  // Holding this mutex will increase the handling latency.
+  ::aos::Mutex *mutex() { return &mutex_; }
+
+  void set_encoder(::std::unique_ptr<Encoder> encoder) {
+    encoder_ = ::std::move(encoder);
+  }
+  Encoder *encoder() const { return encoder_.get(); }
+
+  void set_index(::std::unique_ptr<DigitalSource> index) {
+    index_ = ::std::move(index);
+  }
+  DigitalSource *index() const { return index_.get(); }
+
+  void set_potentiometer(::std::unique_ptr<AnalogInput> potentiometer) {
+    potentiometer_ = ::std::move(potentiometer);
+  }
+  AnalogInput *potentiometer() const { return potentiometer_.get(); }
+
+  // Returns the number of poseges that have happened on the index input.
+  // mutex() must be held while calling this.
+  uint32_t index_posedge_count() const { return index_posedge_count_; }
+  // Returns the value of the encoder at the last index posedge.
+  // mutex() must be held while calling this.
+  int32_t last_encoder_value() const { return last_encoder_value_; }
+  // Returns the voltage of the potentiometer at the last index posedge.
+  // mutex() must be held while calling this.
+  float last_potentiometer_voltage() const {
+    return last_potentiometer_voltage_;
+  }
+
+ private:
+  ::std::unique_ptr<Encoder> encoder_;
+  ::std::unique_ptr<DigitalSource> index_;
+  ::std::unique_ptr<AnalogInput> potentiometer_;
+
+  int32_t last_encoder_value_{0};
+  float last_potentiometer_voltage_{0.0f};
+  uint32_t index_posedge_count_{0};
+
+  ::aos::Mutex mutex_;
+
+  const int priority_;
+
+  ::std::atomic<bool> run_{true};
+  ::std::thread thread_;
+
+  DISALLOW_COPY_AND_ASSIGN(InterruptEncoderAndPotentiometer);
+};
+
+// Latches values from an encoder and potentiometer on positive edges from
+// another input using DMA.
+class DMAEncoderAndPotentiometer : public DMASampleHandlerInterface {
+ public:
+  DMAEncoderAndPotentiometer() {}
+
+  void set_encoder(::std::unique_ptr<Encoder> encoder) {
+    encoder_ = ::std::move(encoder);
+  }
+  Encoder *encoder() const { return encoder_.get(); }
+
+  void set_index(::std::unique_ptr<DigitalSource> index) {
+    index_ = ::std::move(index);
+  }
+  DigitalSource *index() const { return index_.get(); }
+
+  void set_potentiometer(::std::unique_ptr<AnalogInput> potentiometer) {
+    potentiometer_ = ::std::move(potentiometer);
+  }
+  AnalogInput *potentiometer() const { return potentiometer_.get(); }
+
+  // Returns the most recent polled value of the encoder.
+  uint32_t polled_encoder_value() const { return polled_encoder_value_; }
+  // Returns the most recent polled voltage of the potentiometer.
+  float polled_potentiometer_voltage() const {
+    return polled_potentiometer_voltage_;
+  }
+
+  // Returns the number of poseges that have happened on the index input.
+  uint32_t index_posedge_count() const { return index_posedge_count_; }
+  // Returns the value of the encoder at the last index posedge.
+  int32_t last_encoder_value() const { return last_encoder_value_; }
+  // Returns the voltage of the potentiometer at the last index posedge.
+  float last_potentiometer_voltage() const {
+    return last_potentiometer_voltage_;
+  }
+
+  virtual void UpdateFromSample(const DMASample &sample) override;
+
+  virtual void PollFromSample(const DMASample &sample) override {
+    polled_encoder_value_ = sample.GetRaw(encoder_.get());
+    polled_potentiometer_voltage_ = sample.GetVoltage(potentiometer_.get());
+  }
+
+  virtual void UpdatePolledValue() override {
+    polled_encoder_value_ = encoder_->GetRaw();
+    polled_potentiometer_voltage_ = potentiometer_->GetVoltage();
+  }
+
+  virtual void AddToDMA(DMA *dma) override {
+    dma->Add(encoder_.get());
+    dma->Add(index_.get());
+    dma->Add(potentiometer_.get());
+    dma->SetExternalTrigger(index_.get(), true, false);
+  }
+
+ private:
+  ::std::unique_ptr<Encoder> encoder_;
+  ::std::unique_ptr<DigitalSource> index_;
+  ::std::unique_ptr<AnalogInput> potentiometer_;
+
+  int32_t polled_encoder_value_ = 0;
+  float polled_potentiometer_voltage_ = 0.0f;
+
+  int32_t last_encoder_value_ = 0;
+  float last_potentiometer_voltage_ = 0.0f;
+
+  uint32_t index_posedge_count_ = 0;
+
+  // Whether or not it was triggered in the last sample.
+  bool index_last_value_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(DMAEncoderAndPotentiometer);
+};
+
+}  // namespace wpilib
+}  // namespace frc971
+
+#endif  // FRC971_ENCODER_AND_POTENTIOMETER_H_
diff --git a/frc971/wpilib/wpilib.gyp b/frc971/wpilib/wpilib.gyp
index 14da17a..b1dcd7e 100644
--- a/frc971/wpilib/wpilib.gyp
+++ b/frc971/wpilib/wpilib.gyp
@@ -17,7 +17,6 @@
         '<(AOS)/common/controls/controls.gyp:control_loop',
         '<(AOS)/common/controls/controls.gyp:sensor_generation',
         '<(AOS)/common/util/util.gyp:log_interval',
-        '../frc971.gyp:constants',
         '<(AOS)/common/common.gyp:time',
         '<(AOS)/common/logging/logging.gyp:queue_logging',
         '<(AOS)/common/controls/controls.gyp:output_check',
@@ -30,6 +29,27 @@
         'gyro_sender',
         'dma_edge_counting',
         'interrupt_edge_counting',
+        'encoder_and_potentiometer',
+        '<(DEPTH)/frc971/control_loops/control_loops.gyp:queues',
+      ],
+    },
+    {
+      'target_name': 'encoder_and_potentiometer',
+      'type': 'static_library',
+      'sources': [
+        'encoder_and_potentiometer.cc',
+      ],
+      'dependencies': [
+        '<(EXTERNALS):WPILib',
+        'dma_edge_counting',
+        '<(AOS)/linux_code/linux_code.gyp:init',
+        '<(AOS)/build/aos.gyp:logging',
+        '<(AOS)/common/common.gyp:mutex',
+      ],
+      'export_dependent_settings': [
+        '<(EXTERNALS):WPILib',
+        'dma_edge_counting',
+        '<(AOS)/common/common.gyp:mutex',
       ],
     },
     {
diff --git a/frc971/wpilib/wpilib_interface.cc b/frc971/wpilib/wpilib_interface.cc
index b82e2b5..554993a 100644
--- a/frc971/wpilib/wpilib_interface.cc
+++ b/frc971/wpilib/wpilib_interface.cc
@@ -19,11 +19,10 @@
 #include "aos/common/stl_mutex.h"
 #include "aos/linux_code/init.h"
 
+#include "frc971/control_loops/control_loops.q.h"
 #include "frc971/control_loops/drivetrain/drivetrain.q.h"
 #include "frc971/control_loops/fridge/fridge.q.h"
 #include "frc971/control_loops/claw/claw.q.h"
-#include "frc971/constants.h"
-#include "frc971/shifter_hall_effect.h"
 
 #include "frc971/wpilib/hall_effect.h"
 #include "frc971/wpilib/joystick_sender.h"
@@ -33,6 +32,7 @@
 #include "frc971/wpilib/gyro_sender.h"
 #include "frc971/wpilib/dma_edge_counting.h"
 #include "frc971/wpilib/interrupt_edge_counting.h"
+#include "frc971/wpilib/encoder_and_potentiometer.h"
 
 #include "Encoder.h"
 #include "Talon.h"
@@ -40,6 +40,7 @@
 #include "AnalogInput.h"
 #include "Compressor.h"
 #include "RobotBase.h"
+#include "dma.h"
 
 #ifndef M_PI
 #define M_PI 3.14159265358979323846
@@ -47,7 +48,8 @@
 
 using ::aos::util::SimpleLogInterval;
 using ::frc971::control_loops::drivetrain_queue;
-using ::aos::util::WrappingCounter;
+using ::frc971::control_loops::fridge_queue;
+using ::frc971::control_loops::claw_queue;
 
 namespace frc971 {
 namespace wpilib {
@@ -68,8 +70,8 @@
           (2 * M_PI /*radians*/);
 }
 
-double arm_pot_translate(int32_t in) {
-  return static_cast<double>(in) /
+double arm_pot_translate(double voltage) {
+  return voltage /
           (14.0 / 17.0 /*output sprockets*/) *
           (5.0 /*volts*/ / 5.0 /*turns*/) *
           (2 * M_PI /*radians*/);
@@ -82,8 +84,8 @@
           (32 * 5 / 2.54 / 10 /*pulley circumference (in)*/);
 }
 
-double elevator_pot_translate(int32_t in) {
-  return static_cast<double>(in) /
+double elevator_pot_translate(double voltage) {
+  return voltage /
           (32 * 5 / 2.54 / 10 /*pulley circumference (in)*/) *
           (5.0 /*volts*/ / 5.0 /*turns*/);
 }
@@ -95,17 +97,99 @@
           (2 * M_PI /*radians*/);
 }
 
-double claw_pot_translate(int32_t in) {
-  return static_cast<double>(in) /
+double claw_pot_translate(double voltage) {
+  return voltage /
           (16.0 / 72.0 /*output sprockets*/) *
           (5.0 /*volts*/ / 5.0 /*turns*/) *
           (2 * M_PI /*radians*/);
 }
 
+static const double kMaximumEncoderPulsesPerSecond =
+    19500.0 /* free speed RPM */ * 12.0 / 56.0 /* belt reduction */ /
+    60.0 /* seconds / minute */ * 256.0 /* CPR */ *
+    4.0 /* index pulse = 1/4 cycle */;
+
 class SensorReader {
  public:
   SensorReader() {
-    filter_.SetPeriodNanoSeconds(100000);
+    // Set it to filter out anything shorter than 1/4 of the minimum pulse width
+    // we should ever see.
+    filter_.SetPeriodNanoSeconds(
+        static_cast<int>(1 / 4.0 / kMaximumEncoderPulsesPerSecond * 1e9 + 0.5));
+  }
+
+  void set_arm_left_encoder(::std::unique_ptr<Encoder> encoder) {
+    filter_.Add(encoder.get());
+    arm_left_encoder_.set_encoder(::std::move(encoder));
+  }
+
+  void set_arm_left_index(::std::unique_ptr<DigitalSource> index) {
+    filter_.Add(index.get());
+    arm_left_encoder_.set_index(::std::move(index));
+  }
+
+  void set_arm_left_potentiometer(
+      ::std::unique_ptr<AnalogInput> potentiometer) {
+    arm_left_encoder_.set_potentiometer(::std::move(potentiometer));
+  }
+
+  void set_arm_right_encoder(::std::unique_ptr<Encoder> encoder) {
+    filter_.Add(encoder.get());
+    arm_right_encoder_.set_encoder(::std::move(encoder));
+  }
+
+  void set_arm_right_index(::std::unique_ptr<DigitalSource> index) {
+    filter_.Add(index.get());
+    arm_right_encoder_.set_index(::std::move(index));
+  }
+
+  void set_arm_right_potentiometer(
+      ::std::unique_ptr<AnalogInput> potentiometer) {
+    arm_right_encoder_.set_potentiometer(::std::move(potentiometer));
+  }
+
+  void set_elevator_left_encoder(::std::unique_ptr<Encoder> encoder) {
+    filter_.Add(encoder.get());
+    elevator_left_encoder_.set_encoder(::std::move(encoder));
+  }
+
+  void set_elevator_left_index(::std::unique_ptr<DigitalSource> index) {
+    filter_.Add(index.get());
+    elevator_left_encoder_.set_index(::std::move(index));
+  }
+
+  void set_elevator_left_potentiometer(
+      ::std::unique_ptr<AnalogInput> potentiometer) {
+    elevator_left_encoder_.set_potentiometer(::std::move(potentiometer));
+  }
+
+  void set_elevator_right_encoder(::std::unique_ptr<Encoder> encoder) {
+    filter_.Add(encoder.get());
+    elevator_right_encoder_.set_encoder(::std::move(encoder));
+  }
+
+  void set_elevator_right_index(::std::unique_ptr<DigitalSource> index) {
+    filter_.Add(index.get());
+    elevator_right_encoder_.set_index(::std::move(index));
+  }
+
+  void set_elevator_right_potentiometer(
+      ::std::unique_ptr<AnalogInput> potentiometer) {
+    elevator_right_encoder_.set_potentiometer(::std::move(potentiometer));
+  }
+
+  void set_wrist_encoder(::std::unique_ptr<Encoder> encoder) {
+    filter_.Add(encoder.get());
+    wrist_encoder_.set_encoder(::std::move(encoder));
+  }
+
+  void set_wrist_index(::std::unique_ptr<DigitalSource> index) {
+    filter_.Add(index.get());
+    wrist_encoder_.set_index(::std::move(index));
+  }
+
+  void set_wrist_potentiometer(::std::unique_ptr<AnalogInput> potentiometer) {
+    wrist_encoder_.set_potentiometer(::std::move(potentiometer));
   }
 
   void set_left_encoder(::std::unique_ptr<Encoder> left_encoder) {
@@ -116,17 +200,29 @@
     right_encoder_ = ::std::move(right_encoder);
   }
 
+  // All of the DMA-related set_* calls must be made before this, and it doesn't
+  // hurt to do all of them.
+  void set_dma(::std::unique_ptr<DMA> dma) {
+    dma_synchronizer_.reset(new DMASynchronizer(::std::move(dma)));
+    dma_synchronizer_->Add(&arm_left_encoder_);
+    dma_synchronizer_->Add(&arm_right_encoder_);
+    dma_synchronizer_->Add(&elevator_left_encoder_);
+    dma_synchronizer_->Add(&elevator_right_encoder_);
+  }
+
   void operator()() {
     ::aos::SetCurrentThreadName("SensorReader");
 
-    static const int kPriority = 30;
-    //static const int kInterruptPriority = 55;
+    wrist_encoder_.Start();
+    dma_synchronizer_->Start();
 
     ::aos::SetCurrentThreadRealtimePriority(kPriority);
     while (run_) {
       ::aos::time::PhasedLoopXMS(5, 9000);
       RunIteration();
     }
+
+    wrist_encoder_.Stop();
   }
 
   void RunIteration() {
@@ -152,11 +248,79 @@
         .reader_pid(getpid())
         .cape_resets(0)
         .Send();
+
+    dma_synchronizer_->RunIteration();
+
+    {
+      auto fridge_message = fridge_queue.position.MakeMessage();
+      CopyPotAndIndexPosition(arm_left_encoder_, &fridge_message->arm.left,
+                              arm_translate, arm_pot_translate, false);
+      CopyPotAndIndexPosition(arm_right_encoder_, &fridge_message->arm.right,
+                              arm_translate, arm_pot_translate, true);
+      CopyPotAndIndexPosition(
+          elevator_left_encoder_, &fridge_message->elevator.left,
+          elevator_translate, elevator_pot_translate, false);
+      CopyPotAndIndexPosition(elevator_right_encoder_,
+                              &fridge_message->elevator.right,
+                              elevator_translate, elevator_pot_translate, true);
+      fridge_message.Send();
+    }
+
+    {
+      auto claw_message = claw_queue.position.MakeMessage();
+      CopyPotAndIndexPosition(wrist_encoder_, &claw_message->joint,
+                              claw_translate, claw_pot_translate, false);
+      claw_message.Send();
+    }
   }
 
   void Quit() { run_ = false; }
 
  private:
+  static const int kPriority = 30;
+  static const int kInterruptPriority = 55;
+
+  void CopyPotAndIndexPosition(
+      const DMAEncoderAndPotentiometer &encoder, PotAndIndexPosition *position,
+      ::std::function<double(int32_t)> encoder_translate,
+      ::std::function<double(double)> pot_translate, bool reverse) {
+    const double multiplier = reverse ? -1.0 : 1.0;
+    position->encoder =
+        multiplier * encoder_translate(encoder.polled_encoder_value());
+    position->pot =
+        multiplier * pot_translate(encoder.polled_potentiometer_voltage());
+    position->latched_encoder =
+        multiplier * encoder_translate(encoder.last_encoder_value());
+    position->latched_pot =
+        multiplier * pot_translate(encoder.last_potentiometer_voltage());
+    position->index_pulses = encoder.index_posedge_count();
+  }
+
+  void CopyPotAndIndexPosition(
+      const InterruptEncoderAndPotentiometer &encoder,
+      PotAndIndexPosition *position,
+      ::std::function<double(int32_t)> encoder_translate,
+      ::std::function<double(double)> pot_translate, bool reverse) {
+    const double multiplier = reverse ? -1.0 : 1.0;
+    position->encoder =
+        multiplier * encoder_translate(encoder.encoder()->GetRaw());
+    position->pot =
+        multiplier * pot_translate(encoder.potentiometer()->GetVoltage());
+    position->latched_encoder =
+        multiplier * encoder_translate(encoder.last_encoder_value());
+    position->latched_pot =
+        multiplier * pot_translate(encoder.last_potentiometer_voltage());
+    position->index_pulses = encoder.index_posedge_count();
+  }
+
+
+  ::std::unique_ptr<DMASynchronizer> dma_synchronizer_;
+
+  DMAEncoderAndPotentiometer arm_left_encoder_, arm_right_encoder_,
+      elevator_left_encoder_, elevator_right_encoder_;
+
+  InterruptEncoderAndPotentiometer wrist_encoder_{kInterruptPriority};
+
   ::std::unique_ptr<Encoder> left_encoder_;
   ::std::unique_ptr<Encoder> right_encoder_;
 
@@ -369,8 +533,28 @@
 
     SensorReader reader;
     // TODO(sensors): Replace all the 99s with real port numbers.
+    reader.set_arm_left_encoder(
+        make_unique<Encoder>(99, 99, false, Encoder::k4X));
+    reader.set_arm_left_index(make_unique<DigitalInput>(99));
+    reader.set_arm_left_potentiometer(make_unique<AnalogInput>(99));
+    reader.set_arm_right_encoder(
+        make_unique<Encoder>(99, 99, false, Encoder::k4X));
+    reader.set_arm_right_index(make_unique<DigitalInput>(99));
+    reader.set_arm_right_potentiometer(make_unique<AnalogInput>(99));
+    reader.set_elevator_left_encoder(
+        make_unique<Encoder>(99, 99, false, Encoder::k4X));
+    reader.set_elevator_left_index(make_unique<DigitalInput>(99));
+    reader.set_elevator_left_potentiometer(make_unique<AnalogInput>(99));
+    reader.set_elevator_right_encoder(
+        make_unique<Encoder>(99, 99, false, Encoder::k4X));
+    reader.set_elevator_right_index(make_unique<DigitalInput>(99));
+    reader.set_elevator_right_potentiometer(make_unique<AnalogInput>(99));
+    reader.set_wrist_encoder(make_unique<Encoder>(99, 99, false, Encoder::k4X));
+    reader.set_wrist_index(make_unique<DigitalInput>(99));
+    reader.set_wrist_potentiometer(make_unique<AnalogInput>(99));
     reader.set_left_encoder(make_unique<Encoder>(99, 99, false, Encoder::k4X));
     reader.set_right_encoder(make_unique<Encoder>(99, 99, false, Encoder::k4X));
+    reader.set_dma(make_unique<DMA>());
     ::std::thread reader_thread(::std::ref(reader));
     GyroSender gyro_sender;
     ::std::thread gyro_thread(::std::ref(gyro_sender));