blob: f17164cb836137a0373df4332f4eddee4085b2d3 [file] [log] [blame]
Austin Schuh00558222013-03-03 14:16:16 -08001#include "frc971/control_loops/index/index.h"
Austin Schuhd78ab542013-03-01 22:22:19 -08002
3#include <stdio.h>
4
5#include <algorithm>
6
7#include "aos/aos_core.h"
8
9#include "aos/common/messages/RobotState.q.h"
10#include "aos/common/control_loop/control_loops.q.h"
11#include "aos/common/logging/logging.h"
12
13#include "frc971/constants.h"
Austin Schuh00558222013-03-03 14:16:16 -080014#include "frc971/control_loops/index/index_motor_plant.h"
Austin Schuhd78ab542013-03-01 22:22:19 -080015
16using ::aos::time::Time;
17
18namespace frc971 {
19namespace control_loops {
20
21IndexMotor::IndexMotor(control_loops::IndexLoop *my_index)
22 : aos::control_loops::ControlLoop<control_loops::IndexLoop>(my_index),
23 wrist_loop_(new StateFeedbackLoop<2, 1, 1>(MakeIndexLoop())),
24 hopper_disc_count_(0),
25 total_disc_count_(0),
Austin Schuhf8c52252013-03-03 02:25:49 -080026 safe_goal_(Goal::HOLD),
27 loader_goal_(LoaderGoal::READY),
28 loader_state_(LoaderState::READY),
Austin Schuhd78ab542013-03-01 22:22:19 -080029 loader_up_(false),
30 disc_clamped_(false),
31 disc_ejected_(false),
Austin Schuhbcdb90c2013-03-03 23:24:58 -080032 last_bottom_disc_detect_(false),
33 no_prior_position_(true),
34 missing_position_count_(0) {
Austin Schuhd78ab542013-03-01 22:22:19 -080035}
36
Austin Schuhf8c52252013-03-03 02:25:49 -080037/*static*/ const double IndexMotor::kTransferStartPosition = 0.0;
38/*static*/ const double IndexMotor::kIndexStartPosition = 0.2159;
39/*static*/ const double IndexMotor::kIndexFreeLength =
40 IndexMotor::ConvertDiscAngleToDiscPosition((360 * 2 + 14) * M_PI / 180);
41/*static*/ const double IndexMotor::kLoaderFreeStopPosition =
42 kIndexStartPosition + kIndexFreeLength;
43/*static*/ const double IndexMotor::kReadyToLiftPosition =
44 kLoaderFreeStopPosition + 0.2921;
45/*static*/ const double IndexMotor::kGrabberLength = 0.03175;
46/*static*/ const double IndexMotor::kGrabberStartPosition =
47 kReadyToLiftPosition - kGrabberLength;
48/*static*/ const double IndexMotor::kGrabberMovementVelocity = 0.5;
49/*static*/ const double IndexMotor::kLifterStopPosition =
50 kReadyToLiftPosition + 0.161925;
51/*static*/ const double IndexMotor::kLifterMovementVelocity = 1.0;
52/*static*/ const double IndexMotor::kEjectorStopPosition =
53 kLifterStopPosition + 0.01;
54/*static*/ const double IndexMotor::kEjectorMovementVelocity = 1.0;
55/*static*/ const double IndexMotor::kBottomDiscDetectStart = -0.08;
56/*static*/ const double IndexMotor::kBottomDiscDetectStop = 0.200025;
57
58// TODO(aschuh): Figure these out.
59/*static*/ const double IndexMotor::kTopDiscDetectStart = 18.0;
60/*static*/ const double IndexMotor::kTopDiscDetectStop = 19.0;
61
Austin Schuhd78ab542013-03-01 22:22:19 -080062const /*static*/ double IndexMotor::kDiscRadius = 10.875 * 0.0254 / 2;
63const /*static*/ double IndexMotor::kRollerRadius = 2.0 * 0.0254 / 2;
Austin Schuhf8c52252013-03-03 02:25:49 -080064const /*static*/ double IndexMotor::kTransferRollerRadius = 1.25 * 0.0254 / 2;
Austin Schuhd78ab542013-03-01 22:22:19 -080065
Austin Schuhf8c52252013-03-03 02:25:49 -080066/*static*/ const int IndexMotor::kGrabbingDelay = 5;
67/*static*/ const int IndexMotor::kLiftingDelay = 20;
68/*static*/ const int IndexMotor::kShootingDelay = 5;
69/*static*/ const int IndexMotor::kLoweringDelay = 20;
Austin Schuhd78ab542013-03-01 22:22:19 -080070
71// Distance to move the indexer when grabbing a disc.
72const double kNextPosition = 10.0;
73
74/*static*/ double IndexMotor::ConvertDiscAngleToIndex(const double angle) {
75 return (angle * (1 + (kDiscRadius * 2 + kRollerRadius) / kRollerRadius));
76}
77
Austin Schuhf8c52252013-03-03 02:25:49 -080078/*static*/ double IndexMotor::ConvertDiscAngleToDiscPosition(
79 const double angle) {
Austin Schuhd78ab542013-03-01 22:22:19 -080080 return angle * (kDiscRadius + kRollerRadius);
81}
82
Austin Schuhf8c52252013-03-03 02:25:49 -080083/*static*/ double IndexMotor::ConvertDiscPositionToDiscAngle(
84 const double position) {
85 return position / (kDiscRadius + kRollerRadius);
86}
87
Austin Schuhd78ab542013-03-01 22:22:19 -080088/*static*/ double IndexMotor::ConvertIndexToDiscAngle(const double angle) {
89 return (angle / (1 + (kDiscRadius * 2 + kRollerRadius) / kRollerRadius));
90}
91
92/*static*/ double IndexMotor::ConvertIndexToDiscPosition(const double angle) {
93 return IndexMotor::ConvertDiscAngleToDiscPosition(
94 ConvertIndexToDiscAngle(angle));
95}
96
Austin Schuhf8c52252013-03-03 02:25:49 -080097/*static*/ double IndexMotor::ConvertTransferToDiscPosition(
98 const double angle) {
99 const double gear_ratio = (1 + (kDiscRadius * 2 + kTransferRollerRadius) /
100 kTransferRollerRadius);
101 return angle / gear_ratio * (kDiscRadius + kTransferRollerRadius);
102}
103
104/*static*/ double IndexMotor::ConvertDiscPositionToIndex(
105 const double position) {
106 return IndexMotor::ConvertDiscAngleToIndex(
107 ConvertDiscPositionToDiscAngle(position));
108}
109
110bool IndexMotor::MinDiscPosition(double *disc_position) {
111 bool found_start = false;
112 for (unsigned int i = 0; i < frisbees_.size(); ++i) {
113 const Frisbee &frisbee = frisbees_[i];
114 if (!found_start) {
115 if (frisbee.has_position()) {
116 *disc_position = frisbee.position();
117 found_start = true;
118 }
119 } else {
120 *disc_position = ::std::min(frisbee.position(),
121 *disc_position);
122 }
123 }
124 return found_start;
125}
126
127bool IndexMotor::MaxDiscPosition(double *disc_position) {
128 bool found_start = false;
129 for (unsigned int i = 0; i < frisbees_.size(); ++i) {
130 const Frisbee &frisbee = frisbees_[i];
131 if (!found_start) {
132 if (frisbee.has_position()) {
133 *disc_position = frisbee.position();
134 found_start = true;
135 }
136 } else {
137 *disc_position = ::std::max(frisbee.position(),
138 *disc_position);
139 }
140 }
141 return found_start;
142}
143
Austin Schuhd78ab542013-03-01 22:22:19 -0800144// Positive angle is towards the shooter, and positive power is towards the
145// shooter.
146void IndexMotor::RunIteration(
147 const control_loops::IndexLoop::Goal *goal,
148 const control_loops::IndexLoop::Position *position,
149 control_loops::IndexLoop::Output *output,
150 control_loops::IndexLoop::Status *status) {
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800151 // Make goal easy to work with and sanity check it.
Austin Schuhd78ab542013-03-01 22:22:19 -0800152 Goal goal_enum = static_cast<Goal>(goal->goal_state);
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800153 if (goal->goal_state < 0 || goal->goal_state > 4) {
154 LOG(ERROR, "Goal state is %d which is out of range. Going to HOLD.\n",
155 goal->goal_state);
156 goal_enum = Goal::HOLD;
157 }
Austin Schuhd78ab542013-03-01 22:22:19 -0800158
159 // Disable the motors now so that all early returns will return with the
160 // motors disabled.
Austin Schuhb6d898b2013-03-03 15:34:35 -0800161 double intake_voltage = 0.0;
Austin Schuhf8c52252013-03-03 02:25:49 -0800162 double transfer_voltage = 0.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800163 if (output) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800164 output->intake_voltage = 0.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800165 output->transfer_voltage = 0.0;
166 output->index_voltage = 0.0;
167 }
168
169 status->ready_to_intake = false;
170
Austin Schuhf8c52252013-03-03 02:25:49 -0800171 // Compute a safe index position that we can use.
Austin Schuhd78ab542013-03-01 22:22:19 -0800172 if (position) {
173 wrist_loop_->Y << position->index_position;
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800174 // Set the goal to be the current position if this is the first time through
175 // so we don't always spin the indexer to the 0 position before starting.
176 if (no_prior_position_) {
177 wrist_loop_->R << wrist_loop_->Y(0, 0), 0.0;
178 no_prior_position_ = false;
179 }
180
181 // If the cRIO is gone for 1/2 of a second, assume that it rebooted.
182 if (missing_position_count_ > 50) {
183 // Adjust the disc positions so that they don't have to move.
184 const double disc_offset =
185 position->index_position - wrist_loop_->X_hat(0, 0);
186 for (auto frisbee = frisbees_.begin();
187 frisbee != frisbees_.end(); ++frisbee) {
188 frisbee->OffsetDisc(disc_offset);
189 }
190 }
191 missing_position_count_ = 0;
192 } else {
193 ++missing_position_count_;
Austin Schuhd78ab542013-03-01 22:22:19 -0800194 }
195 const double index_position = wrist_loop_->X_hat(0, 0);
196
Austin Schuhf8c52252013-03-03 02:25:49 -0800197 // TODO(aschuh): Watch for top disc detect and update the frisbee
198 // position.
199
Austin Schuhf8c52252013-03-03 02:25:49 -0800200 // Bool to track if it is safe for the goal to change yet.
Austin Schuhd78ab542013-03-01 22:22:19 -0800201 bool safe_to_change_state_ = true;
202 switch (safe_goal_) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800203 case Goal::HOLD:
Austin Schuhd78ab542013-03-01 22:22:19 -0800204 // The goal should already be good, so sit tight with everything the same
205 // as it was.
Austin Schuhd78ab542013-03-01 22:22:19 -0800206 break;
Austin Schuhf8c52252013-03-03 02:25:49 -0800207 case Goal::READY_LOWER:
208 case Goal::INTAKE:
Austin Schuhd78ab542013-03-01 22:22:19 -0800209 {
210 Time now = Time::Now();
Austin Schuhd78ab542013-03-01 22:22:19 -0800211 // Posedge of the disc entering the beam break.
212 if (position) {
Austin Schuh89955e42013-03-03 02:37:08 -0800213 // TODO(aschuh): Catch the edges on the FPGA since this is too slow.
214 // This means that we need to pass back enough data so that we can
215 // miss packets and everything works.
Austin Schuhd78ab542013-03-01 22:22:19 -0800216 if (position->bottom_disc_detect && !last_bottom_disc_detect_) {
217 transfer_frisbee_.Reset();
218 transfer_frisbee_.bottom_posedge_time_ = now;
219 printf("Posedge of bottom disc %f\n",
220 transfer_frisbee_.bottom_posedge_time_.ToSeconds());
221 ++hopper_disc_count_;
Austin Schuhf8c52252013-03-03 02:25:49 -0800222 ++total_disc_count_;
Austin Schuhd78ab542013-03-01 22:22:19 -0800223 }
224
225 // Disc exited the beam break now.
226 if (!position->bottom_disc_detect && last_bottom_disc_detect_) {
227 transfer_frisbee_.bottom_negedge_time_ = now;
228 printf("Negedge of bottom disc %f\n",
229 transfer_frisbee_.bottom_negedge_time_.ToSeconds());
230 frisbees_.push_front(transfer_frisbee_);
231 }
232
233 if (position->bottom_disc_detect) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800234 intake_voltage = transfer_voltage = 12.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800235 // Must wait until the disc gets out before we can change state.
236 safe_to_change_state_ = false;
237
Austin Schuhf8c52252013-03-03 02:25:49 -0800238 // TODO(aschuh): A disc on the way through needs to start moving
239 // the indexer if it isn't already moving. Maybe?
Austin Schuhd78ab542013-03-01 22:22:19 -0800240
241 Time elapsed_posedge_time = now -
242 transfer_frisbee_.bottom_posedge_time_;
243 if (elapsed_posedge_time >= Time::InSeconds(0.3)) {
244 // It has been too long. The disc must be jammed.
245 LOG(ERROR, "Been way too long. Jammed disc?\n");
246 printf("Been way too long. Jammed disc?\n");
247 }
248 }
249
Austin Schuhf8c52252013-03-03 02:25:49 -0800250 // Check all non-indexed discs and see if they should be indexed.
Austin Schuhb6d898b2013-03-03 15:34:35 -0800251 for (auto frisbee = frisbees_.begin();
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800252 frisbee != frisbees_.end(); ++frisbee) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800253 if (!frisbee->has_been_indexed_) {
254 intake_voltage = transfer_voltage = 12.0;
Austin Schuhf8c52252013-03-03 02:25:49 -0800255 Time elapsed_negedge_time = now -
Austin Schuhb6d898b2013-03-03 15:34:35 -0800256 frisbee->bottom_negedge_time_;
Austin Schuhf8c52252013-03-03 02:25:49 -0800257 if (elapsed_negedge_time >= Time::InSeconds(0.005)) {
Austin Schuhd78ab542013-03-01 22:22:19 -0800258 // Should have just engaged.
259 // Save the indexer position, and the time.
260
261 // It has been long enough since the disc entered the indexer.
262 // Treat now as the time at which it contacted the indexer.
263 LOG(INFO, "Grabbed on the index now at %f\n", index_position);
264 printf("Grabbed on the index now at %f\n", index_position);
Austin Schuhb6d898b2013-03-03 15:34:35 -0800265 frisbee->has_been_indexed_ = true;
266 frisbee->index_start_position_ = index_position;
Austin Schuhd78ab542013-03-01 22:22:19 -0800267 }
268 }
Austin Schuhb6d898b2013-03-03 15:34:35 -0800269 if (!frisbee->has_been_indexed_) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800270 // All discs must be indexed before it is safe to stop indexing.
Austin Schuhd78ab542013-03-01 22:22:19 -0800271 safe_to_change_state_ = false;
272 }
273 }
274
Austin Schuhf8c52252013-03-03 02:25:49 -0800275 // Figure out where the indexer should be to move the discs down to
276 // the right position.
277 double max_disc_position;
278 if (MaxDiscPosition(&max_disc_position)) {
279 printf("There is a disc down here!\n");
280 // TODO(aschuh): Figure out what to do if grabbing the next one
281 // would cause things to jam into the loader.
282 // Say we aren't ready any more. Undefined behavior will result if
283 // that isn't observed.
284 double bottom_disc_position =
285 max_disc_position + ConvertDiscAngleToIndex(M_PI);
286 wrist_loop_->R << bottom_disc_position, 0.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800287
Austin Schuhf8c52252013-03-03 02:25:49 -0800288 // Verify that we are close enough to the goal so that we should be
289 // fine accepting the next disc.
290 double disc_error_meters = ConvertIndexToDiscPosition(
291 wrist_loop_->X_hat(0, 0) - bottom_disc_position);
292 // We are ready for the next disc if the first one is in the first
293 // half circle of the indexer. It will take time for the disc to
294 // come into the indexer, so we will be able to move it out of the
295 // way in time.
296 // This choice also makes sure that we don't claim that we aren't
297 // ready between full speed intaking.
298 if (-ConvertDiscAngleToIndex(M_PI) < disc_error_meters &&
299 disc_error_meters < 0.04) {
300 // We are only ready if we aren't being asked to change state or
301 // are full.
302 status->ready_to_intake =
303 (safe_goal_ == goal_enum) && hopper_disc_count_ < 4;
304 } else {
305 status->ready_to_intake = false;
Austin Schuhd78ab542013-03-01 22:22:19 -0800306 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800307 } else {
308 // No discs! We are always ready for more if we aren't being
309 // asked to change state.
310 status->ready_to_intake = (safe_goal_ == goal_enum);
Austin Schuhd78ab542013-03-01 22:22:19 -0800311 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800312
313 // Turn on the transfer roller if we are ready.
314 if (status->ready_to_intake && hopper_disc_count_ < 4 &&
315 safe_goal_ == Goal::INTAKE) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800316 intake_voltage = transfer_voltage = 12.0;
Austin Schuhf8c52252013-03-03 02:25:49 -0800317 }
Austin Schuhd78ab542013-03-01 22:22:19 -0800318 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800319 printf("INTAKE\n");
Austin Schuhd78ab542013-03-01 22:22:19 -0800320 }
321 break;
Austin Schuhf8c52252013-03-03 02:25:49 -0800322 case Goal::READY_SHOOTER:
323 case Goal::SHOOT:
324 // Check if we have any discs to shoot or load and handle them.
325 double min_disc_position;
326 if (MinDiscPosition(&min_disc_position)) {
327 const double ready_disc_position =
328 min_disc_position + ConvertDiscPositionToIndex(kIndexFreeLength) -
329 ConvertDiscAngleToIndex(M_PI / 6.0);
330
331 const double grabbed_disc_position =
332 min_disc_position +
333 ConvertDiscPositionToIndex(kReadyToLiftPosition -
334 kIndexStartPosition + 0.03);
335
336 // Check the state of the loader FSM.
337 // If it is ready to load discs, position the disc so that it is ready
338 // to be grabbed.
339 // If it isn't ready, there is a disc in there. It needs to finish it's
340 // cycle first.
341 if (loader_state_ != LoaderState::READY) {
342 // We already have a disc in the loader.
343 // Stage the discs back a bit.
344 wrist_loop_->R << ready_disc_position, 0.0;
345
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800346 // Shoot if we are grabbed and being asked to shoot.
347 if (loader_state_ == LoaderState::GRABBED &&
348 safe_goal_ == Goal::SHOOT) {
349 loader_goal_ = LoaderGoal::SHOOT_AND_RESET;
350 }
351
Austin Schuhf8c52252013-03-03 02:25:49 -0800352 // Must wait until it has been grabbed to continue.
353 if (loader_state_ == LoaderState::GRABBING) {
354 safe_to_change_state_ = false;
355 }
356 } else {
357 // No disc up top right now.
358 wrist_loop_->R << grabbed_disc_position, 0.0;
359
360 // See if the disc has gotten pretty far up yet.
361 if (wrist_loop_->X_hat(0, 0) > ready_disc_position) {
362 // Point of no return. We are committing to grabbing it now.
363 safe_to_change_state_ = false;
364 const double robust_grabbed_disc_position =
365 (grabbed_disc_position -
366 ConvertDiscPositionToIndex(kGrabberLength));
367
368 // If close, start grabbing and/or shooting.
369 if (wrist_loop_->X_hat(0, 0) > robust_grabbed_disc_position) {
370 // Start the state machine.
371 if (safe_goal_ == Goal::SHOOT) {
372 loader_goal_ = LoaderGoal::SHOOT_AND_RESET;
373 } else {
374 loader_goal_ = LoaderGoal::GRAB;
375 }
376 // This frisbee is now gone. Take it out of the queue.
377 frisbees_.pop_back();
378 --hopper_disc_count_;
379 }
380 }
381 }
382 }
383
384 printf("READY_SHOOTER or SHOOT\n");
Austin Schuhd78ab542013-03-01 22:22:19 -0800385 break;
Austin Schuhf8c52252013-03-03 02:25:49 -0800386 }
387
388 // The only way out of the loader is to shoot the disc. The FSM can only go
389 // forwards.
390 switch (loader_state_) {
391 case LoaderState::READY:
392 printf("Loader READY\n");
393 // Open and down, ready to accept a disc.
394 loader_up_ = false;
395 disc_clamped_ = false;
396 disc_ejected_ = false;
397 if (loader_goal_ == LoaderGoal::GRAB ||
398 loader_goal_ == LoaderGoal::SHOOT_AND_RESET) {
399 if (loader_goal_ == LoaderGoal::GRAB) {
400 printf("Told to GRAB, moving on\n");
401 } else {
402 printf("Told to SHOOT_AND_RESET, moving on\n");
403 }
404 loader_state_ = LoaderState::GRABBING;
405 loader_countdown_ = kGrabbingDelay;
406 } else {
407 break;
408 }
409 case LoaderState::GRABBING:
410 printf("Loader GRABBING %d\n", loader_countdown_);
411 // Closing the grabber.
412 loader_up_ = false;
413 disc_clamped_ = true;
414 disc_ejected_ = false;
415 if (loader_countdown_ > 0) {
416 --loader_countdown_;
417 break;
418 } else {
419 loader_state_ = LoaderState::GRABBED;
420 }
421 case LoaderState::GRABBED:
422 printf("Loader GRABBED\n");
423 // Grabber closed.
424 loader_up_ = false;
425 disc_clamped_ = true;
426 disc_ejected_ = false;
427 if (loader_goal_ == LoaderGoal::SHOOT_AND_RESET) {
428 // TODO(aschuh): Only shoot if the shooter is up to speed.
429 // Seems like that would have us shooting a bit later than we could be,
430 // but it also probably spins back up real fast.
431 loader_state_ = LoaderState::LIFTING;
432 loader_countdown_ = kLiftingDelay;
433 printf("Told to SHOOT_AND_RESET, moving on\n");
434 } else if (loader_goal_ == LoaderGoal::READY) {
435 LOG(ERROR, "Can't go to ready when we have something grabbed.\n");
436 printf("Can't go to ready when we have something grabbed.\n");
437 break;
438 } else {
439 break;
440 }
441 case LoaderState::LIFTING:
442 printf("Loader LIFTING %d\n", loader_countdown_);
443 // Lifting the disc.
444 loader_up_ = true;
445 disc_clamped_ = true;
446 disc_ejected_ = false;
447 if (loader_countdown_ > 0) {
448 --loader_countdown_;
449 break;
450 } else {
451 loader_state_ = LoaderState::LIFTED;
452 }
453 case LoaderState::LIFTED:
454 printf("Loader LIFTED\n");
455 // Disc lifted. Time to eject it out.
456 loader_up_ = true;
457 disc_clamped_ = true;
458 disc_ejected_ = false;
459 loader_state_ = LoaderState::SHOOTING;
460 loader_countdown_ = kShootingDelay;
461 case LoaderState::SHOOTING:
462 printf("Loader SHOOTING %d\n", loader_countdown_);
463 // Ejecting the disc into the shooter.
464 loader_up_ = true;
465 disc_clamped_ = false;
466 disc_ejected_ = true;
467 if (loader_countdown_ > 0) {
468 --loader_countdown_;
469 break;
470 } else {
471 loader_state_ = LoaderState::SHOOT;
472 }
473 case LoaderState::SHOOT:
474 printf("Loader SHOOT\n");
475 // The disc has been shot.
476 loader_up_ = true;
477 disc_clamped_ = false;
478 disc_ejected_ = true;
479 loader_state_ = LoaderState::LOWERING;
480 loader_countdown_ = kLoweringDelay;
481 case LoaderState::LOWERING:
482 printf("Loader LOWERING %d\n", loader_countdown_);
483 // Lowering the loader back down.
484 loader_up_ = false;
485 disc_clamped_ = false;
486 disc_ejected_ = true;
487 if (loader_countdown_ > 0) {
488 --loader_countdown_;
489 break;
490 } else {
491 loader_state_ = LoaderState::LOWERED;
492 }
493 case LoaderState::LOWERED:
494 printf("Loader LOWERED\n");
495 // The indexer is lowered.
496 loader_up_ = false;
497 disc_clamped_ = false;
498 disc_ejected_ = false;
499 loader_state_ = LoaderState::READY;
500 // Once we have shot, we need to hang out in READY until otherwise
501 // notified.
502 loader_goal_ = LoaderGoal::READY;
Austin Schuhd78ab542013-03-01 22:22:19 -0800503 break;
504 }
505
506 // Update the observer.
507 wrist_loop_->Update(position != NULL, output == NULL);
508
509 if (position) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800510 LOG(DEBUG, "pos=%f\n", position->index_position);
Austin Schuhd78ab542013-03-01 22:22:19 -0800511 last_bottom_disc_detect_ = position->bottom_disc_detect;
512 }
513
514 status->hopper_disc_count = hopper_disc_count_;
515 status->total_disc_count = total_disc_count_;
Austin Schuhf8c52252013-03-03 02:25:49 -0800516 status->preloaded = (loader_state_ != LoaderState::READY);
Austin Schuhd78ab542013-03-01 22:22:19 -0800517
518 if (output) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800519 output->intake_voltage = intake_voltage;
Austin Schuhf8c52252013-03-03 02:25:49 -0800520 output->transfer_voltage = transfer_voltage;
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800521 // TODO(aschuh): Count the number of cycles with power below
522 // kFrictionVoltage and if it is too high, turn the motor off.
523 // 50 cycles, 5 volts? Need data...
Austin Schuhd78ab542013-03-01 22:22:19 -0800524 output->index_voltage = wrist_loop_->U(0, 0);
Austin Schuhf8c52252013-03-03 02:25:49 -0800525 output->loader_up = loader_up_;
526 output->disc_clamped = disc_clamped_;
527 output->disc_ejected = disc_ejected_;
Austin Schuhd78ab542013-03-01 22:22:19 -0800528 }
529
530 if (safe_to_change_state_) {
531 safe_goal_ = goal_enum;
532 }
533}
534
535} // namespace control_loops
536} // namespace frc971