blob: c6a2541b31fe58a8c5d80338a72b04fcc7bd60e8 [file] [log] [blame]
Brian Silvermanf4937f62013-10-16 10:32:00 -07001#include <string.h>
2
Brian Silverman2e0dcfd2013-03-30 22:44:40 -07003#include <memory>
4
5#include "aos/common/inttypes.h"
6#include "aos/atom_code/init.h"
7#include "aos/common/logging/logging.h"
8#include "aos/common/time.h"
Brian Silvermanf4937f62013-10-16 10:32:00 -07009#include "aos/common/util/wrapping_counter.h"
10#include "aos/common/control_loop/ControlLoop.h"
Brian Silverman2e0dcfd2013-03-30 22:44:40 -070011
12#include "frc971/control_loops/drivetrain/drivetrain.q.h"
13#include "frc971/control_loops/wrist/wrist_motor.q.h"
14#include "frc971/control_loops/angle_adjust/angle_adjust_motor.q.h"
15#include "frc971/control_loops/index/index_motor.q.h"
16#include "frc971/control_loops/shooter/shooter_motor.q.h"
17#include "frc971/input/gyro_board_data.h"
Brian Silverman2e0dcfd2013-03-30 22:44:40 -070018#include "frc971/queues/GyroAngle.q.h"
Brian Silverman4bde0172013-10-25 15:53:25 -070019#include "gyro_board/src/libusb-driver/libusb_wrap.h"
Brian Silverman2e0dcfd2013-03-30 22:44:40 -070020
21#ifndef M_PI
22#define M_PI 3.14159265358979323846
23#endif
24
25using ::frc971::control_loops::drivetrain;
26using ::frc971::control_loops::wrist;
27using ::frc971::control_loops::angle_adjust;
28using ::frc971::control_loops::shooter;
29using ::frc971::control_loops::index_loop;
30using ::frc971::sensors::gyro;
Brian Silvermanf4937f62013-10-16 10:32:00 -070031using ::aos::util::WrappingCounter;
Brian Silverman2e0dcfd2013-03-30 22:44:40 -070032
33namespace frc971 {
34namespace {
35
36inline double drivetrain_translate(int32_t in) {
37 return static_cast<double>(in) / (256.0 /*cpr*/ * 4.0 /*quad*/) *
38 (19.0 / 50.0) /*output reduction*/ * (64.0 / 24.0) /*encoder gears*/ *
39 (3.5 /*wheel diameter*/ * 2.54 / 100.0 * M_PI);
40}
41
42inline double wrist_translate(int32_t in) {
43 return static_cast<double>(in) / (256.0 /*cpr*/ * 4.0 /*quad*/) *
44 (14.0 / 50.0 * 20.0 / 84.0) /*gears*/ * (2 * M_PI);
45}
46
47inline double angle_adjust_translate(int32_t in) {
48 static const double kCableDiameter = 0.060;
49 return -static_cast<double>(in) / (256.0 /*cpr*/ * 4.0 /*quad*/) *
50 ((0.75 + kCableDiameter) / (16.61125 + kCableDiameter)) /*pulleys*/ *
51 (2 * M_PI);
52}
53
54inline double shooter_translate(int32_t in) {
55 return static_cast<double>(in) / (32.0 /*cpr*/ * 4.0 /*quad*/) *
56 (15.0 / 34.0) /*gears*/ * (2 * M_PI);
57}
58
59inline double index_translate(int32_t in) {
60 return -static_cast<double>(in) / (128.0 /*cpr*/ * 4.0 /*quad*/) *
61 (1.0) /*gears*/ * (2 * M_PI);
62}
63
64} // namespace
65
Brian Silvermanf4937f62013-10-16 10:32:00 -070066class GyroSensorReceiver {
Brian Silverman2e0dcfd2013-03-30 22:44:40 -070067 public:
Brian Silvermanf4937f62013-10-16 10:32:00 -070068 GyroSensorReceiver() {
69 Reset();
Brian Silverman2e0dcfd2013-03-30 22:44:40 -070070 }
71
Brian Silvermanf4937f62013-10-16 10:32:00 -070072 void RunIteration() {
73 if (ReceiveData()) {
74 Reset();
Brian Silvermanf92396c2013-09-12 20:13:13 -070075 } else {
Brian Silvermanf4937f62013-10-16 10:32:00 -070076 const ::aos::time::Time received_time = ::aos::time::Time::Now();
77 if (phase_locker_.IsCurrentPacketGood(received_time, sequence_.count())) {
Brian Silverman7643bbb2013-10-24 15:58:37 -070078 LOG(DEBUG, "processing data\n");
Brian Silvermanf4937f62013-10-16 10:32:00 -070079 ProcessData();
80 }
Brian Silvermanf92396c2013-09-12 20:13:13 -070081 }
Brian Silverman2e0dcfd2013-03-30 22:44:40 -070082 }
83
84 private:
Brian Silverman4bde0172013-10-25 15:53:25 -070085 static const unsigned char kEndpoint = 0x83;
Brian Silverman2e0dcfd2013-03-30 22:44:40 -070086 // 0 is unlimited
Brian Silvermanf4937f62013-10-16 10:32:00 -070087 static constexpr ::aos::time::Time kReadTimeout =
88 ::aos::time::Time::InSeconds(1.5);
Brian Silverman4bde0172013-10-25 15:53:25 -070089 // vendor ID
90 static const int32_t kVid = 0x1424;
91 // product ID
92 static const int32_t kPid = 0xd243;
93
94 // A value to put into completed_transfer_ to indicate that it failed.
95 static constexpr libusb::Transfer *kTransferFailed =
96 reinterpret_cast<libusb::Transfer *>(-1);
Brian Silverman1c3cfc02013-10-25 17:02:19 -070097 // The kernel on the fitpc seems to miss ~11-15 packets in a row if it misses
98 // any with just 2, so 25 should be enough to ride over any holes.
99 static const int kNumTransfers = 25;
Brian Silverman4bde0172013-10-25 15:53:25 -0700100
101 // How big of a buffer we're going to give the usb transfer stuff.
102 static const size_t kDataLength = 128;
103 static_assert(kDataLength >= sizeof(GyroBoardData), "buffer is too small");
Brian Silverman2e0dcfd2013-03-30 22:44:40 -0700104
Brian Silvermanf4937f62013-10-16 10:32:00 -0700105 static const int kPacketsPerLoopCycle = 10;
Brian Silverman2e0dcfd2013-03-30 22:44:40 -0700106
Brian Silverman1c3cfc02013-10-25 17:02:19 -0700107 // How long "after" the control loops run we want to use a packet.
Brian Silverman7643bbb2013-10-24 15:58:37 -0700108 static constexpr ::aos::time::Time kDesiredOffset =
109 ::aos::time::Time::InSeconds(-0.0025);
110
Brian Silverman1c3cfc02013-10-25 17:02:19 -0700111 // How long without a good packet until we give up and Reset().
112 static constexpr ::aos::time::Time kResetTime =
113 ::aos::time::Time::InSeconds(0.75);
114 // How old of a packet we log about.
115 static constexpr ::aos::time::Time kStaleTime =
116 ::aos::time::Time::InSeconds(0.005);
117
Brian Silvermanf4937f62013-10-16 10:32:00 -0700118 // Contains all of the complicated state and logic for locking onto the the
119 // correct phase.
120 class {
121 public:
122 void Reset() {
Brian Silverman7643bbb2013-10-24 15:58:37 -0700123 LOG(INFO, "resetting\n");
Brian Silverman1c3cfc02013-10-25 17:02:19 -0700124 last_good_packet_time_ = ::aos::time::Time(0, 0);
Brian Silvermanf4937f62013-10-16 10:32:00 -0700125 good_phase_ = guess_phase_ = kUnknownPhase;
126 guess_phase_good_ = guess_phase_bad_ = 0;
127 good_phase_early_ = good_phase_late_ = 0;
128 }
Brian Silverman9a574d52013-03-31 00:53:53 -0700129
Brian Silvermanf4937f62013-10-16 10:32:00 -0700130 // Gets called for every packet received.
131 // Returns whether or not to process the values from this packet.
132 bool IsCurrentPacketGood(const ::aos::time::Time &received_time,
133 int32_t sequence) {
Brian Silverman1c3cfc02013-10-25 17:02:19 -0700134 if (last_good_packet_time_ != ::aos::time::Time(0, 0) &&
135 received_time - last_good_packet_time_ > kResetTime) {
136 LOG(WARNING, "no good packet received in too long\n");
137 Reset();
138 return false;
139 }
140
141 using ::aos::control_loops::kLoopFrequency;
Brian Silvermanf4937f62013-10-16 10:32:00 -0700142 // How often we (should) receive packets.
143 static const ::aos::time::Time kPacketFrequency =
Brian Silverman1c3cfc02013-10-25 17:02:19 -0700144 kLoopFrequency / kPacketsPerLoopCycle;
Brian Silverman7643bbb2013-10-24 15:58:37 -0700145 static const ::aos::time::Time kPacketClose =
146 kPacketFrequency * 65 / 100;
147 static const ::aos::time::Time kSwitchOffset =
148 kPacketFrequency * 6 / 10;
Brian Silvermanf4937f62013-10-16 10:32:00 -0700149
150 // When we want to receive a packet for the next cycle of control loops.
Brian Silverman1c3cfc02013-10-25 17:02:19 -0700151 ::aos::time::Time next_desired =
152 ::aos::control_loops::NextLoopTime(received_time) + kDesiredOffset;
153 // If we came up with something more than 1 packet in the past.
154 if (next_desired - received_time < -kPacketFrequency) {
155 next_desired += kLoopFrequency;
156 }
Brian Silvermanf4937f62013-10-16 10:32:00 -0700157 // How far off of when we want the next packet this one is.
158 const ::aos::time::Time offset = next_desired - received_time;
159
160 const int received_phase = sequence % kPacketsPerLoopCycle;
161
162 assert(!(good_phase_early_ != 0 && good_phase_late_ != 0));
163
Brian Silverman1c3cfc02013-10-25 17:02:19 -0700164 if (good_phase_ == kUnknownPhase &&
165 guess_phase_good_ > kMinGoodGuessCycles) {
Brian Silvermanf4937f62013-10-16 10:32:00 -0700166 good_phase_ = guess_phase_;
Brian Silverman7643bbb2013-10-24 15:58:37 -0700167 if (guess_phase_offset_ < kPacketFrequency / -2) {
Brian Silvermanf4937f62013-10-16 10:32:00 -0700168 ++good_phase_;
Brian Silverman7643bbb2013-10-24 15:58:37 -0700169 } else if (guess_phase_offset_ > kPacketFrequency / 2) {
Brian Silvermanf4937f62013-10-16 10:32:00 -0700170 --good_phase_;
171 }
Brian Silverman1c3cfc02013-10-25 17:02:19 -0700172 LOG(INFO, "locked on to phase %d\n", good_phase_);
Brian Silvermanf4937f62013-10-16 10:32:00 -0700173 } else if (guess_phase_bad_ > kMaxBadGuessCycles) {
Brian Silverman4bde0172013-10-25 15:53:25 -0700174 LOG(INFO, "guessed wrong phase too many times\n");
Brian Silvermanf4937f62013-10-16 10:32:00 -0700175 Reset();
176 }
177 if (good_phase_early_ > kSwitchCycles) {
178 good_phase_early_ = 0;
Brian Silverman1c3cfc02013-10-25 17:02:19 -0700179 LOG(INFO, "switching from phase %d to %d-1\n",
180 good_phase_, good_phase_);
Brian Silvermanf4937f62013-10-16 10:32:00 -0700181 --good_phase_;
182 } else if (good_phase_late_ > kSwitchCycles) {
183 good_phase_late_ = 0;
Brian Silverman1c3cfc02013-10-25 17:02:19 -0700184 LOG(INFO, "switching from phase %d to %d+1\n",
185 good_phase_, good_phase_);
Brian Silvermanf4937f62013-10-16 10:32:00 -0700186 ++good_phase_;
187 }
188 if (good_phase_ == kUnknownPhase) {
189 LOG(INFO, "guessing which packet is good\n");
190
Brian Silvermanf4937f62013-10-16 10:32:00 -0700191 // If it's close to the right time.
Brian Silverman7643bbb2013-10-24 15:58:37 -0700192 if (offset.abs() < kPacketClose) {
Brian Silvermanf4937f62013-10-16 10:32:00 -0700193 if (guess_phase_ == kUnknownPhase) {
Brian Silverman7643bbb2013-10-24 15:58:37 -0700194 if (offset.abs() < kPacketFrequency * 55 / 100) {
Brian Silvermanf4937f62013-10-16 10:32:00 -0700195 guess_phase_ = received_phase;
196 guess_phase_offset_ = offset;
197 }
198 } else if (received_phase == guess_phase_) {
Brian Silverman4bde0172013-10-25 15:53:25 -0700199 LOG(DEBUG, "guessed right phase %d\n", received_phase);
Brian Silvermanf4937f62013-10-16 10:32:00 -0700200 ++guess_phase_good_;
201 guess_phase_bad_ = 0;
202 guess_phase_offset_ = (guess_phase_offset_ * 9 + offset) / 10;
203 }
204 } else if (guess_phase_ != kUnknownPhase &&
205 received_phase == guess_phase_) {
Brian Silverman4bde0172013-10-25 15:53:25 -0700206 LOG(DEBUG, "guessed wrong phase %d\n", received_phase);
Brian Silvermanf4937f62013-10-16 10:32:00 -0700207 ++guess_phase_bad_;
208 guess_phase_good_ = ::std::max(0, guess_phase_good_ -
209 (kMinGoodGuessCycles / 10));
210 }
Brian Silverman1c3cfc02013-10-25 17:02:19 -0700211 return false;
Brian Silvermanf4937f62013-10-16 10:32:00 -0700212 } else { // we know what phase we're looking for
213 // Deal with it if the above logic for tweaking the phase that we're
214 // using wrapped it around.
215 if (good_phase_ == -1) {
216 good_phase_ = kPacketsPerLoopCycle;
217 } else if (good_phase_ == kPacketsPerLoopCycle) {
Brian Silverman1c3cfc02013-10-25 17:02:19 -0700218 LOG(DEBUG, "dewrapping\n");
Brian Silvermanf4937f62013-10-16 10:32:00 -0700219 good_phase_ = 0;
220 }
221 assert(good_phase_ >= 0);
222 assert(good_phase_ < kPacketsPerLoopCycle);
223
224 if (received_phase == good_phase_) {
Brian Silverman7643bbb2013-10-24 15:58:37 -0700225 if (offset < -kSwitchOffset) {
Brian Silvermanf4937f62013-10-16 10:32:00 -0700226 ++good_phase_early_;
227 good_phase_late_ = 0;
Brian Silverman7643bbb2013-10-24 15:58:37 -0700228 } else if (offset > kSwitchOffset) {
Brian Silvermanf4937f62013-10-16 10:32:00 -0700229 ++good_phase_late_;
230 good_phase_early_ = 0;
231 } else {
232 good_phase_early_ = good_phase_late_ = 0;
233 }
Brian Silverman1c3cfc02013-10-25 17:02:19 -0700234 last_good_packet_time_ = received_time;
235
236 if (::aos::time::Time::Now() - received_time > kStaleTime) {
237 // TODO(brians): Do we actually want to use this one or what?
238 LOG(WARNING, "received a stale packet\n");
239 }
240
Brian Silvermanf4937f62013-10-16 10:32:00 -0700241 return true;
242 } else {
243 return false;
244 }
245 }
246 }
247
248 private:
Brian Silvermanf4937f62013-10-16 10:32:00 -0700249 // How many times the packet we guessed has to be close to right to use the
250 // guess.
251 static const int kMinGoodGuessCycles = 30;
252 // How many times in a row we have to guess the wrong packet before trying
253 // again.
254 static const int kMaxBadGuessCycles = 3;
255
256 // How many times in a row a different packet has to be better than the one
257 // that we're using befor switching to it.
258 static const int kSwitchCycles = 15;
259
Brian Silverman1c3cfc02013-10-25 17:02:19 -0700260 ::aos::time::Time last_good_packet_time_{0, 0};
Brian Silvermanf4937f62013-10-16 10:32:00 -0700261
262 const int kUnknownPhase = -11;
263 // kUnknownPhase or the sequence number (%kPacketsPerLoopCycle) to
264 // use or think about using.
265 // If not kUnknownPhase, 0 <= these < kPacketsPerLoopCycle.
266 int good_phase_, guess_phase_;
267 int guess_phase_good_, guess_phase_bad_;
268 ::aos::time::Time guess_phase_offset_{0, 0};
269 int good_phase_early_, good_phase_late_;
270 } phase_locker_;
271
Brian Silverman4bde0172013-10-25 15:53:25 -0700272 static void StaticTransferCallback(libusb::Transfer *transfer, void *self) {
273 static_cast<GyroSensorReceiver *>(self)->TransferCallback(transfer);
274 }
275 void TransferCallback(libusb::Transfer *transfer) {
276 if (transfer->status() == LIBUSB_TRANSFER_COMPLETED) {
277 LOG(DEBUG, "transfer %p completed\n", transfer);
278 completed_transfer_ = transfer;
279 } else if (transfer->status() == LIBUSB_TRANSFER_TIMED_OUT) {
280 LOG(WARNING, "transfer %p timed out\n", transfer);
281 completed_transfer_ = kTransferFailed;
282 } else if (transfer->status() == LIBUSB_TRANSFER_CANCELLED) {
283 LOG(DEBUG, "transfer %p cancelled\n", transfer);
284 } else {
285 LOG(FATAL, "transfer %p has status %d\n", transfer, transfer->status());
286 }
287 transfer->Submit();
288 }
289
Brian Silvermanf4937f62013-10-16 10:32:00 -0700290 // Returns true if receiving failed and we should try a Reset().
291 bool ReceiveData() {
Brian Silverman2e0dcfd2013-03-30 22:44:40 -0700292 // Loop and then return once we get a good one.
293 while (true) {
Brian Silverman4bde0172013-10-25 15:53:25 -0700294 completed_transfer_ = NULL;
295 while (completed_transfer_ == NULL) {
296 libusb_.HandleEvents();
Brian Silverman2e0dcfd2013-03-30 22:44:40 -0700297 }
Brian Silverman4bde0172013-10-25 15:53:25 -0700298 if (completed_transfer_ == kTransferFailed) {
299 LOG(WARNING, "transfer failed\n");
300 return true;
301 }
302
303 if (completed_transfer_->read_bytes() <
304 static_cast<ssize_t>(sizeof(GyroBoardData))) {
305 LOG(ERROR, "read %d bytes instead of at least %zd\n",
306 completed_transfer_->read_bytes(), sizeof(GyroBoardData));
307 continue;
308 }
309
310 memcpy(data(), completed_transfer_->data(),
311 sizeof(GyroBoardData));
Brian Silverman1c3cfc02013-10-25 17:02:19 -0700312
313 int32_t count_before = sequence_.count();
Brian Silverman4bde0172013-10-25 15:53:25 -0700314 sequence_.Update(data()->sequence);
Brian Silverman1c3cfc02013-10-25 17:02:19 -0700315 if (sequence_.count() - count_before != 1) {
316 LOG(WARNING, "count went from %" PRId32" to %" PRId32 "\n",
317 count_before, sequence_.count());
318 }
319
Brian Silverman4bde0172013-10-25 15:53:25 -0700320 return false;
Brian Silverman2e0dcfd2013-03-30 22:44:40 -0700321 }
322 }
323
Brian Silvermanf4937f62013-10-16 10:32:00 -0700324 GyroBoardData *data() {
Brian Silverman4bde0172013-10-25 15:53:25 -0700325 return &data_;
Brian Silvermanf4937f62013-10-16 10:32:00 -0700326 }
327
328 void Reset() {
Brian Silverman1c3cfc02013-10-25 17:02:19 -0700329 typedef ::std::unique_ptr<libusb::IsochronousTransfer> TransferType;
330 for (TransferType &c : transfers_) {
331 c.reset();
332 }
Brian Silverman4bde0172013-10-25 15:53:25 -0700333 dev_handle_ = ::std::unique_ptr<LibUSBDeviceHandle>(
334 libusb_.FindDeviceWithVIDPID(kVid, kPid));
335 if (!dev_handle_) {
336 LOG(ERROR, "couldn't find device. exiting\n");
337 exit(1);
338 }
Brian Silverman1c3cfc02013-10-25 17:02:19 -0700339 for (TransferType &c : transfers_) {
340 c.reset(new libusb::IsochronousTransfer(kDataLength, 1,
341 StaticTransferCallback, this));
342 c->FillIsochronous(dev_handle_.get(), kEndpoint, kReadTimeout);
343 c->Submit();
344 }
Brian Silverman4bde0172013-10-25 15:53:25 -0700345
Brian Silvermanf4937f62013-10-16 10:32:00 -0700346 sequence_.Reset();
347 phase_locker_.Reset();
Brian Silverman2e0dcfd2013-03-30 22:44:40 -0700348 }
349
Brian Silvermanf4937f62013-10-16 10:32:00 -0700350 void ProcessData() {
351 if (data()->robot_id != 0) {
352 LOG(ERROR, "gyro board sent data for robot id %hhd!"
353 " dip switches are %x\n",
354 data()->robot_id, data()->base_status & 0xF);
355 return;
356 } else {
357 LOG(DEBUG, "processing a packet dip switches %x\n",
358 data()->base_status & 0xF);
359 }
360
361 static ::aos::time::Time last_time = ::aos::time::Time::Now();
362 if ((last_time - ::aos::time::Time::Now()) >
363 ::aos::time::Time::InMS(0.0011)) {
364 LOG(INFO, "missed one\n");
365 }
366
367 gyro.MakeWithBuilder()
368 .angle(data()->gyro_angle / 16.0 / 1000.0 / 180.0 * M_PI)
369 .Send();
370
371 drivetrain.position.MakeWithBuilder()
372 .right_encoder(drivetrain_translate(data()->main.right_drive))
373 .left_encoder(-drivetrain_translate(data()->main.left_drive))
374 .Send();
375
376 wrist.position.MakeWithBuilder()
377 .pos(wrist_translate(data()->main.wrist))
378 .hall_effect(!data()->main.wrist_hall_effect)
379 .calibration(wrist_translate(data()->main.capture_wrist_rise))
380 .Send();
381
382 angle_adjust.position.MakeWithBuilder()
383 .angle(angle_adjust_translate(data()->main.shooter_angle))
384 .bottom_hall_effect(!data()->main.angle_adjust_bottom_hall_effect)
385 .middle_hall_effect(false)
386 .bottom_calibration(angle_adjust_translate(
387 data()->main.capture_shooter_angle_rise))
388 .middle_calibration(angle_adjust_translate(
389 0))
390 .Send();
391
392 shooter.position.MakeWithBuilder()
393 .position(shooter_translate(data()->main.shooter))
394 .Send();
395
396 index_loop.position.MakeWithBuilder()
397 .index_position(index_translate(data()->main.indexer))
398 .top_disc_detect(!data()->main.top_disc)
399 .top_disc_posedge_count(top_rise_.Update(data()->main.top_rise_count))
400 .top_disc_posedge_position(
401 index_translate(data()->main.capture_top_rise))
402 .top_disc_negedge_count(top_fall_.Update(data()->main.top_fall_count))
403 .top_disc_negedge_position(
404 index_translate(data()->main.capture_top_fall))
405 .bottom_disc_detect(!data()->main.bottom_disc)
406 .bottom_disc_posedge_count(
407 bottom_rise_.Update(data()->main.bottom_rise_count))
408 .bottom_disc_negedge_count(
409 bottom_fall_.Update(data()->main.bottom_fall_count))
410 .bottom_disc_negedge_wait_position(index_translate(
411 data()->main.capture_bottom_fall_delay))
412 .bottom_disc_negedge_wait_count(
413 bottom_fall_delay_.Update(data()->main.bottom_fall_delay_count))
414 .loader_top(data()->main.loader_top)
415 .loader_bottom(data()->main.loader_bottom)
416 .Send();
Brian Silverman2e0dcfd2013-03-30 22:44:40 -0700417 }
418
Brian Silverman4bde0172013-10-25 15:53:25 -0700419 GyroBoardData data_;
Brian Silverman2e0dcfd2013-03-30 22:44:40 -0700420
Brian Silvermanf4937f62013-10-16 10:32:00 -0700421 WrappingCounter sequence_;
Brian Silverman2e0dcfd2013-03-30 22:44:40 -0700422
Brian Silverman4bde0172013-10-25 15:53:25 -0700423 LibUSB libusb_;
424 ::std::unique_ptr<LibUSBDeviceHandle> dev_handle_;
Brian Silverman1c3cfc02013-10-25 17:02:19 -0700425 ::std::unique_ptr<libusb::IsochronousTransfer> transfers_[kNumTransfers];
Brian Silverman4bde0172013-10-25 15:53:25 -0700426 // Temporary variable for holding a completed transfer to communicate that
427 // information from the callback to the code that wants it.
428 libusb::Transfer *completed_transfer_;
Brian Silvermanf4937f62013-10-16 10:32:00 -0700429
430 WrappingCounter top_rise_;
431 WrappingCounter top_fall_;
432 WrappingCounter bottom_rise_;
433 WrappingCounter bottom_fall_delay_;
434 WrappingCounter bottom_fall_;
Brian Silverman2e0dcfd2013-03-30 22:44:40 -0700435};
Brian Silvermanf4937f62013-10-16 10:32:00 -0700436constexpr ::aos::time::Time GyroSensorReceiver::kReadTimeout;
Brian Silverman7643bbb2013-10-24 15:58:37 -0700437constexpr ::aos::time::Time GyroSensorReceiver::kDesiredOffset;
Brian Silverman1c3cfc02013-10-25 17:02:19 -0700438constexpr ::aos::time::Time GyroSensorReceiver::kResetTime;
439constexpr ::aos::time::Time GyroSensorReceiver::kStaleTime;
Brian Silverman2e0dcfd2013-03-30 22:44:40 -0700440
441} // namespace frc971
442
443int main() {
444 ::aos::Init();
Brian Silvermanf4937f62013-10-16 10:32:00 -0700445 ::frc971::GyroSensorReceiver receiver;
Brian Silverman2e0dcfd2013-03-30 22:44:40 -0700446 while (true) {
447 receiver.RunIteration();
448 }
449 ::aos::Cleanup();
450}