blob: ed37229189384693a72212be3256a35f61ae8c81 [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);
97
98 // How big of a buffer we're going to give the usb transfer stuff.
99 static const size_t kDataLength = 128;
100 static_assert(kDataLength >= sizeof(GyroBoardData), "buffer is too small");
Brian Silverman2e0dcfd2013-03-30 22:44:40 -0700101
Brian Silvermanf4937f62013-10-16 10:32:00 -0700102 static const int kPacketsPerLoopCycle = 10;
Brian Silverman2e0dcfd2013-03-30 22:44:40 -0700103
Brian Silverman7643bbb2013-10-24 15:58:37 -0700104 // How long before the control loops run we want to use a packet.
105 static constexpr ::aos::time::Time kDesiredOffset =
106 ::aos::time::Time::InSeconds(-0.0025);
107
Brian Silvermanf4937f62013-10-16 10:32:00 -0700108 // Contains all of the complicated state and logic for locking onto the the
109 // correct phase.
110 class {
111 public:
112 void Reset() {
Brian Silverman7643bbb2013-10-24 15:58:37 -0700113 LOG(INFO, "resetting\n");
Brian Silvermanf4937f62013-10-16 10:32:00 -0700114 last_guessed_time_ = ::aos::time::Time(0, 0);
115 good_phase_ = guess_phase_ = kUnknownPhase;
116 guess_phase_good_ = guess_phase_bad_ = 0;
117 good_phase_early_ = good_phase_late_ = 0;
118 }
Brian Silverman9a574d52013-03-31 00:53:53 -0700119
Brian Silvermanf4937f62013-10-16 10:32:00 -0700120 // Gets called for every packet received.
121 // Returns whether or not to process the values from this packet.
122 bool IsCurrentPacketGood(const ::aos::time::Time &received_time,
123 int32_t sequence) {
124 // How often we (should) receive packets.
125 static const ::aos::time::Time kPacketFrequency =
126 ::aos::control_loops::kLoopFrequency / kPacketsPerLoopCycle;
Brian Silverman7643bbb2013-10-24 15:58:37 -0700127 static const ::aos::time::Time kPacketClose =
128 kPacketFrequency * 65 / 100;
129 static const ::aos::time::Time kSwitchOffset =
130 kPacketFrequency * 6 / 10;
Brian Silvermanf4937f62013-10-16 10:32:00 -0700131
132 // When we want to receive a packet for the next cycle of control loops.
133 const ::aos::time::Time next_desired =
134 ::aos::control_loops::NextLoopTime(received_time + kDesiredOffset);
135 // How far off of when we want the next packet this one is.
136 const ::aos::time::Time offset = next_desired - received_time;
137
138 const int received_phase = sequence % kPacketsPerLoopCycle;
139
140 assert(!(good_phase_early_ != 0 && good_phase_late_ != 0));
141
142 if (guess_phase_good_ > kMinGoodGuessCycles) {
143 good_phase_ = guess_phase_;
Brian Silverman7643bbb2013-10-24 15:58:37 -0700144 if (guess_phase_offset_ < kPacketFrequency / -2) {
Brian Silvermanf4937f62013-10-16 10:32:00 -0700145 ++good_phase_;
Brian Silverman7643bbb2013-10-24 15:58:37 -0700146 } else if (guess_phase_offset_ > kPacketFrequency / 2) {
Brian Silvermanf4937f62013-10-16 10:32:00 -0700147 --good_phase_;
148 }
149 } else if (guess_phase_bad_ > kMaxBadGuessCycles) {
Brian Silverman4bde0172013-10-25 15:53:25 -0700150 LOG(INFO, "guessed wrong phase too many times\n");
Brian Silvermanf4937f62013-10-16 10:32:00 -0700151 Reset();
152 }
153 if (good_phase_early_ > kSwitchCycles) {
154 good_phase_early_ = 0;
Brian Silverman7643bbb2013-10-24 15:58:37 -0700155 LOG(INFO, "switching to 1 phase earlier\n");
Brian Silvermanf4937f62013-10-16 10:32:00 -0700156 --good_phase_;
157 } else if (good_phase_late_ > kSwitchCycles) {
158 good_phase_late_ = 0;
Brian Silverman7643bbb2013-10-24 15:58:37 -0700159 LOG(INFO, "switching to 1 phase later\n");
Brian Silvermanf4937f62013-10-16 10:32:00 -0700160 ++good_phase_;
161 }
162 if (good_phase_ == kUnknownPhase) {
163 LOG(INFO, "guessing which packet is good\n");
164
165 // If we're going to call this packet a good guess.
166 bool guess_is_good = false;
167 // If it's close to the right time.
Brian Silverman7643bbb2013-10-24 15:58:37 -0700168 if (offset.abs() < kPacketClose) {
Brian Silvermanf4937f62013-10-16 10:32:00 -0700169 // If we didn't (also) guess that the previous one was good.
170 if (received_time - last_guessed_time_ > kPacketFrequency * 2) {
Brian Silverman7643bbb2013-10-24 15:58:37 -0700171 LOG(DEBUG, "guessing this one is good\n");
Brian Silvermanf4937f62013-10-16 10:32:00 -0700172 guess_is_good = true;
Brian Silverman7643bbb2013-10-24 15:58:37 -0700173 } else {
174 LOG(DEBUG, "just guessed\n");
Brian Silvermanf4937f62013-10-16 10:32:00 -0700175 }
176 if (guess_phase_ == kUnknownPhase) {
Brian Silverman7643bbb2013-10-24 15:58:37 -0700177 if (offset.abs() < kPacketFrequency * 55 / 100) {
Brian Silvermanf4937f62013-10-16 10:32:00 -0700178 guess_phase_ = received_phase;
179 guess_phase_offset_ = offset;
180 }
181 } else if (received_phase == guess_phase_) {
Brian Silverman4bde0172013-10-25 15:53:25 -0700182 LOG(DEBUG, "guessed right phase %d\n", received_phase);
Brian Silvermanf4937f62013-10-16 10:32:00 -0700183 ++guess_phase_good_;
184 guess_phase_bad_ = 0;
185 guess_phase_offset_ = (guess_phase_offset_ * 9 + offset) / 10;
186 }
187 } else if (guess_phase_ != kUnknownPhase &&
188 received_phase == guess_phase_) {
Brian Silverman4bde0172013-10-25 15:53:25 -0700189 LOG(DEBUG, "guessed wrong phase %d\n", received_phase);
Brian Silvermanf4937f62013-10-16 10:32:00 -0700190 ++guess_phase_bad_;
191 guess_phase_good_ = ::std::max(0, guess_phase_good_ -
192 (kMinGoodGuessCycles / 10));
193 }
194 if (guess_is_good) {
195 last_guessed_time_ = received_time;
196 return true;
197 } else {
198 return false;
199 }
200 } else { // we know what phase we're looking for
201 // Deal with it if the above logic for tweaking the phase that we're
202 // using wrapped it around.
203 if (good_phase_ == -1) {
204 good_phase_ = kPacketsPerLoopCycle;
205 } else if (good_phase_ == kPacketsPerLoopCycle) {
206 good_phase_ = 0;
207 }
208 assert(good_phase_ >= 0);
209 assert(good_phase_ < kPacketsPerLoopCycle);
210
211 if (received_phase == good_phase_) {
Brian Silverman7643bbb2013-10-24 15:58:37 -0700212 if (offset < -kSwitchOffset) {
Brian Silvermanf4937f62013-10-16 10:32:00 -0700213 ++good_phase_early_;
214 good_phase_late_ = 0;
Brian Silverman7643bbb2013-10-24 15:58:37 -0700215 } else if (offset > kSwitchOffset) {
Brian Silvermanf4937f62013-10-16 10:32:00 -0700216 ++good_phase_late_;
217 good_phase_early_ = 0;
218 } else {
219 good_phase_early_ = good_phase_late_ = 0;
220 }
221 return true;
222 } else {
223 return false;
224 }
225 }
226 }
227
228 private:
Brian Silvermanf4937f62013-10-16 10:32:00 -0700229 // How many times the packet we guessed has to be close to right to use the
230 // guess.
231 static const int kMinGoodGuessCycles = 30;
232 // How many times in a row we have to guess the wrong packet before trying
233 // again.
234 static const int kMaxBadGuessCycles = 3;
235
236 // How many times in a row a different packet has to be better than the one
237 // that we're using befor switching to it.
238 static const int kSwitchCycles = 15;
239
240 ::aos::time::Time last_guessed_time_{0, 0};
241
242 const int kUnknownPhase = -11;
243 // kUnknownPhase or the sequence number (%kPacketsPerLoopCycle) to
244 // use or think about using.
245 // If not kUnknownPhase, 0 <= these < kPacketsPerLoopCycle.
246 int good_phase_, guess_phase_;
247 int guess_phase_good_, guess_phase_bad_;
248 ::aos::time::Time guess_phase_offset_{0, 0};
249 int good_phase_early_, good_phase_late_;
250 } phase_locker_;
251
Brian Silverman4bde0172013-10-25 15:53:25 -0700252 static void StaticTransferCallback(libusb::Transfer *transfer, void *self) {
253 static_cast<GyroSensorReceiver *>(self)->TransferCallback(transfer);
254 }
255 void TransferCallback(libusb::Transfer *transfer) {
256 if (transfer->status() == LIBUSB_TRANSFER_COMPLETED) {
257 LOG(DEBUG, "transfer %p completed\n", transfer);
258 completed_transfer_ = transfer;
259 } else if (transfer->status() == LIBUSB_TRANSFER_TIMED_OUT) {
260 LOG(WARNING, "transfer %p timed out\n", transfer);
261 completed_transfer_ = kTransferFailed;
262 } else if (transfer->status() == LIBUSB_TRANSFER_CANCELLED) {
263 LOG(DEBUG, "transfer %p cancelled\n", transfer);
264 } else {
265 LOG(FATAL, "transfer %p has status %d\n", transfer, transfer->status());
266 }
267 transfer->Submit();
268 }
269
Brian Silvermanf4937f62013-10-16 10:32:00 -0700270 // Returns true if receiving failed and we should try a Reset().
271 bool ReceiveData() {
Brian Silverman2e0dcfd2013-03-30 22:44:40 -0700272 // Loop and then return once we get a good one.
273 while (true) {
Brian Silverman4bde0172013-10-25 15:53:25 -0700274 completed_transfer_ = NULL;
275 while (completed_transfer_ == NULL) {
276 libusb_.HandleEvents();
Brian Silverman2e0dcfd2013-03-30 22:44:40 -0700277 }
Brian Silverman4bde0172013-10-25 15:53:25 -0700278 if (completed_transfer_ == kTransferFailed) {
279 LOG(WARNING, "transfer failed\n");
280 return true;
281 }
282
283 if (completed_transfer_->read_bytes() <
284 static_cast<ssize_t>(sizeof(GyroBoardData))) {
285 LOG(ERROR, "read %d bytes instead of at least %zd\n",
286 completed_transfer_->read_bytes(), sizeof(GyroBoardData));
287 continue;
288 }
289
290 memcpy(data(), completed_transfer_->data(),
291 sizeof(GyroBoardData));
292 sequence_.Update(data()->sequence);
293 return false;
Brian Silverman2e0dcfd2013-03-30 22:44:40 -0700294 }
295 }
296
Brian Silvermanf4937f62013-10-16 10:32:00 -0700297 GyroBoardData *data() {
Brian Silverman4bde0172013-10-25 15:53:25 -0700298 return &data_;
Brian Silvermanf4937f62013-10-16 10:32:00 -0700299 }
300
301 void Reset() {
Brian Silverman4bde0172013-10-25 15:53:25 -0700302 transfer1_.reset();
303 transfer2_.reset();
304 dev_handle_ = ::std::unique_ptr<LibUSBDeviceHandle>(
305 libusb_.FindDeviceWithVIDPID(kVid, kPid));
306 if (!dev_handle_) {
307 LOG(ERROR, "couldn't find device. exiting\n");
308 exit(1);
309 }
310 transfer1_ = ::std::unique_ptr<libusb::IsochronousTransfer>(
311 new libusb::IsochronousTransfer(kDataLength, 1,
312 StaticTransferCallback, this));
313 transfer2_ = ::std::unique_ptr<libusb::IsochronousTransfer>(
314 new libusb::IsochronousTransfer(kDataLength, 1,
315 StaticTransferCallback, this));
316 transfer1_->FillIsochronous(dev_handle_.get(), kEndpoint, kReadTimeout);
317 transfer2_->FillIsochronous(dev_handle_.get(), kEndpoint, kReadTimeout);
318 transfer1_->Submit();
319 transfer2_->Submit();
320
Brian Silvermanf4937f62013-10-16 10:32:00 -0700321 sequence_.Reset();
322 phase_locker_.Reset();
Brian Silverman2e0dcfd2013-03-30 22:44:40 -0700323 }
324
Brian Silvermanf4937f62013-10-16 10:32:00 -0700325 void ProcessData() {
326 if (data()->robot_id != 0) {
327 LOG(ERROR, "gyro board sent data for robot id %hhd!"
328 " dip switches are %x\n",
329 data()->robot_id, data()->base_status & 0xF);
330 return;
331 } else {
332 LOG(DEBUG, "processing a packet dip switches %x\n",
333 data()->base_status & 0xF);
334 }
335
336 static ::aos::time::Time last_time = ::aos::time::Time::Now();
337 if ((last_time - ::aos::time::Time::Now()) >
338 ::aos::time::Time::InMS(0.0011)) {
339 LOG(INFO, "missed one\n");
340 }
341
342 gyro.MakeWithBuilder()
343 .angle(data()->gyro_angle / 16.0 / 1000.0 / 180.0 * M_PI)
344 .Send();
345
346 drivetrain.position.MakeWithBuilder()
347 .right_encoder(drivetrain_translate(data()->main.right_drive))
348 .left_encoder(-drivetrain_translate(data()->main.left_drive))
349 .Send();
350
351 wrist.position.MakeWithBuilder()
352 .pos(wrist_translate(data()->main.wrist))
353 .hall_effect(!data()->main.wrist_hall_effect)
354 .calibration(wrist_translate(data()->main.capture_wrist_rise))
355 .Send();
356
357 angle_adjust.position.MakeWithBuilder()
358 .angle(angle_adjust_translate(data()->main.shooter_angle))
359 .bottom_hall_effect(!data()->main.angle_adjust_bottom_hall_effect)
360 .middle_hall_effect(false)
361 .bottom_calibration(angle_adjust_translate(
362 data()->main.capture_shooter_angle_rise))
363 .middle_calibration(angle_adjust_translate(
364 0))
365 .Send();
366
367 shooter.position.MakeWithBuilder()
368 .position(shooter_translate(data()->main.shooter))
369 .Send();
370
371 index_loop.position.MakeWithBuilder()
372 .index_position(index_translate(data()->main.indexer))
373 .top_disc_detect(!data()->main.top_disc)
374 .top_disc_posedge_count(top_rise_.Update(data()->main.top_rise_count))
375 .top_disc_posedge_position(
376 index_translate(data()->main.capture_top_rise))
377 .top_disc_negedge_count(top_fall_.Update(data()->main.top_fall_count))
378 .top_disc_negedge_position(
379 index_translate(data()->main.capture_top_fall))
380 .bottom_disc_detect(!data()->main.bottom_disc)
381 .bottom_disc_posedge_count(
382 bottom_rise_.Update(data()->main.bottom_rise_count))
383 .bottom_disc_negedge_count(
384 bottom_fall_.Update(data()->main.bottom_fall_count))
385 .bottom_disc_negedge_wait_position(index_translate(
386 data()->main.capture_bottom_fall_delay))
387 .bottom_disc_negedge_wait_count(
388 bottom_fall_delay_.Update(data()->main.bottom_fall_delay_count))
389 .loader_top(data()->main.loader_top)
390 .loader_bottom(data()->main.loader_bottom)
391 .Send();
Brian Silverman2e0dcfd2013-03-30 22:44:40 -0700392 }
393
Brian Silverman4bde0172013-10-25 15:53:25 -0700394 GyroBoardData data_;
Brian Silverman2e0dcfd2013-03-30 22:44:40 -0700395
Brian Silvermanf4937f62013-10-16 10:32:00 -0700396 WrappingCounter sequence_;
Brian Silverman2e0dcfd2013-03-30 22:44:40 -0700397
Brian Silverman4bde0172013-10-25 15:53:25 -0700398 LibUSB libusb_;
399 ::std::unique_ptr<LibUSBDeviceHandle> dev_handle_;
400 ::std::unique_ptr<libusb::IsochronousTransfer> transfer1_, transfer2_;
401 // Temporary variable for holding a completed transfer to communicate that
402 // information from the callback to the code that wants it.
403 libusb::Transfer *completed_transfer_;
Brian Silvermanf4937f62013-10-16 10:32:00 -0700404
405 WrappingCounter top_rise_;
406 WrappingCounter top_fall_;
407 WrappingCounter bottom_rise_;
408 WrappingCounter bottom_fall_delay_;
409 WrappingCounter bottom_fall_;
Brian Silverman2e0dcfd2013-03-30 22:44:40 -0700410};
Brian Silvermanf4937f62013-10-16 10:32:00 -0700411constexpr ::aos::time::Time GyroSensorReceiver::kReadTimeout;
Brian Silverman7643bbb2013-10-24 15:58:37 -0700412constexpr ::aos::time::Time GyroSensorReceiver::kDesiredOffset;
Brian Silverman2e0dcfd2013-03-30 22:44:40 -0700413
414} // namespace frc971
415
416int main() {
417 ::aos::Init();
Brian Silvermanf4937f62013-10-16 10:32:00 -0700418 ::frc971::GyroSensorReceiver receiver;
Brian Silverman2e0dcfd2013-03-30 22:44:40 -0700419 while (true) {
420 receiver.RunIteration();
421 }
422 ::aos::Cleanup();
423}