blob: 14c9800152a8471f5c6955d4a748ad300df9f3f7 [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"
Brian Silverman94195052013-03-09 13:45:05 -080012#include "aos/common/inttypes.h"
Austin Schuhd78ab542013-03-01 22:22:19 -080013
14#include "frc971/constants.h"
Austin Schuh00558222013-03-03 14:16:16 -080015#include "frc971/control_loops/index/index_motor_plant.h"
Austin Schuhd78ab542013-03-01 22:22:19 -080016
17using ::aos::time::Time;
18
19namespace frc971 {
20namespace control_loops {
21
Austin Schuhdff24e22013-03-06 00:41:21 -080022double IndexMotor::Frisbee::ObserveNoTopDiscSensor(
Austin Schuh825bde92013-03-06 00:16:46 -080023 double index_position, double index_velocity) {
Austin Schuhdff24e22013-03-06 00:41:21 -080024 // The absolute disc position in meters.
Austin Schuh1b864a12013-03-07 00:46:50 -080025 double disc_position = absolute_position(index_position);
Austin Schuh825bde92013-03-06 00:16:46 -080026 if (IndexMotor::kTopDiscDetectStart <= disc_position &&
27 disc_position <= IndexMotor::kTopDiscDetectStop) {
28 // Whoops, this shouldn't be happening.
29 // Move the disc off the way that makes most sense.
Austin Schuhdff24e22013-03-06 00:41:21 -080030 double distance_to_above = IndexMotor::ConvertDiscPositionToIndex(
31 ::std::abs(disc_position - IndexMotor::kTopDiscDetectStop));
32 double distance_to_below = IndexMotor::ConvertDiscPositionToIndex(
33 ::std::abs(disc_position - IndexMotor::kTopDiscDetectStart));
Austin Schuh825bde92013-03-06 00:16:46 -080034 if (::std::abs(index_velocity) < 100) {
35 if (distance_to_above < distance_to_below) {
Austin Schuhdff24e22013-03-06 00:41:21 -080036 printf("Moving disc to top slow.\n");
Austin Schuh825bde92013-03-06 00:16:46 -080037 // Move it up.
Austin Schuhdff24e22013-03-06 00:41:21 -080038 index_start_position_ -= distance_to_above;
39 return -distance_to_above;
Austin Schuh825bde92013-03-06 00:16:46 -080040 } else {
Austin Schuhdff24e22013-03-06 00:41:21 -080041 printf("Moving disc to bottom slow.\n");
42 index_start_position_ += distance_to_below;
43 return distance_to_below;
Austin Schuh825bde92013-03-06 00:16:46 -080044 }
45 } else {
46 if (index_velocity > 0) {
47 // Now going up. If we didn't see it before, and we don't see it
48 // now but it should be in view, it must still be below. If it were
49 // above, it would be going further away from us.
Austin Schuh1b864a12013-03-07 00:46:50 -080050 printf("Moving fast up, shifting disc down. Disc was at %f\n",
51 absolute_position(index_position));
Austin Schuhdff24e22013-03-06 00:41:21 -080052 index_start_position_ += distance_to_below;
Austin Schuh1b864a12013-03-07 00:46:50 -080053 printf("Moving fast up, shifting disc down. Disc now at %f\n",
54 absolute_position(index_position));
Austin Schuhdff24e22013-03-06 00:41:21 -080055 return distance_to_below;
Austin Schuh825bde92013-03-06 00:16:46 -080056 } else {
Austin Schuh1b864a12013-03-07 00:46:50 -080057 printf("Moving fast down, shifting disc up. Disc was at %f\n",
58 absolute_position(index_position));
Austin Schuhdff24e22013-03-06 00:41:21 -080059 index_start_position_ -= distance_to_above;
Austin Schuh1b864a12013-03-07 00:46:50 -080060 printf("Moving fast down, shifting disc up. Disc now at %f\n",
61 absolute_position(index_position));
Austin Schuhdff24e22013-03-06 00:41:21 -080062 return -distance_to_above;
Austin Schuh825bde92013-03-06 00:16:46 -080063 }
64 }
65 }
Austin Schuhdff24e22013-03-06 00:41:21 -080066 return 0.0;
Austin Schuh825bde92013-03-06 00:16:46 -080067}
68
Austin Schuhd78ab542013-03-01 22:22:19 -080069IndexMotor::IndexMotor(control_loops::IndexLoop *my_index)
70 : aos::control_loops::ControlLoop<control_loops::IndexLoop>(my_index),
Austin Schuh93485832013-03-04 00:01:34 -080071 wrist_loop_(new IndexStateFeedbackLoop(MakeIndexLoop())),
Austin Schuhd78ab542013-03-01 22:22:19 -080072 hopper_disc_count_(0),
73 total_disc_count_(0),
Austin Schuhf8c52252013-03-03 02:25:49 -080074 safe_goal_(Goal::HOLD),
75 loader_goal_(LoaderGoal::READY),
76 loader_state_(LoaderState::READY),
Austin Schuhd78ab542013-03-01 22:22:19 -080077 loader_up_(false),
78 disc_clamped_(false),
79 disc_ejected_(false),
Austin Schuhbcdb90c2013-03-03 23:24:58 -080080 last_bottom_disc_detect_(false),
Austin Schuh825bde92013-03-06 00:16:46 -080081 last_top_disc_detect_(false),
Austin Schuhbcdb90c2013-03-03 23:24:58 -080082 no_prior_position_(true),
Austin Schuh7c0e2aa2013-03-09 02:01:16 -080083 missing_position_count_(0),
84 upper_open_index_position_(0.0),
85 upper_open_index_position_was_negedge_(false),
86 lower_open_index_position_(0.0),
87 lower_open_index_position_was_negedge_(false) {
Austin Schuhd78ab542013-03-01 22:22:19 -080088}
89
Austin Schuhf8c52252013-03-03 02:25:49 -080090/*static*/ const double IndexMotor::kTransferStartPosition = 0.0;
91/*static*/ const double IndexMotor::kIndexStartPosition = 0.2159;
92/*static*/ const double IndexMotor::kIndexFreeLength =
93 IndexMotor::ConvertDiscAngleToDiscPosition((360 * 2 + 14) * M_PI / 180);
94/*static*/ const double IndexMotor::kLoaderFreeStopPosition =
95 kIndexStartPosition + kIndexFreeLength;
Austin Schuh1b864a12013-03-07 00:46:50 -080096/*static*/ const double IndexMotor::kReadyToPreload =
97 kLoaderFreeStopPosition - ConvertDiscAngleToDiscPosition(M_PI / 6.0);
Austin Schuhf8c52252013-03-03 02:25:49 -080098/*static*/ const double IndexMotor::kReadyToLiftPosition =
99 kLoaderFreeStopPosition + 0.2921;
100/*static*/ const double IndexMotor::kGrabberLength = 0.03175;
101/*static*/ const double IndexMotor::kGrabberStartPosition =
102 kReadyToLiftPosition - kGrabberLength;
Austin Schuh6328daf2013-03-05 00:53:15 -0800103/*static*/ const double IndexMotor::kGrabberMovementVelocity = 0.7;
Austin Schuhf8c52252013-03-03 02:25:49 -0800104/*static*/ const double IndexMotor::kLifterStopPosition =
105 kReadyToLiftPosition + 0.161925;
106/*static*/ const double IndexMotor::kLifterMovementVelocity = 1.0;
107/*static*/ const double IndexMotor::kEjectorStopPosition =
108 kLifterStopPosition + 0.01;
109/*static*/ const double IndexMotor::kEjectorMovementVelocity = 1.0;
Austin Schuhcc297022013-03-09 23:26:40 -0800110/*static*/ const double IndexMotor::kBottomDiscDetectStart = 0.00;
111/*static*/ const double IndexMotor::kBottomDiscDetectStop = 0.13;
112/*static*/ const double IndexMotor::kBottomDiscIndexDelay = 0.032;
Austin Schuhf8c52252013-03-03 02:25:49 -0800113
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800114// TODO(aschuh): Verify these with the sensor actually on.
Austin Schuh825bde92013-03-06 00:16:46 -0800115/*static*/ const double IndexMotor::kTopDiscDetectStart =
116 (IndexMotor::kLoaderFreeStopPosition -
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800117 IndexMotor::ConvertDiscAngleToDiscPosition(49 * M_PI / 180));
Austin Schuh825bde92013-03-06 00:16:46 -0800118/*static*/ const double IndexMotor::kTopDiscDetectStop =
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800119 (IndexMotor::kLoaderFreeStopPosition +
120 IndexMotor::ConvertDiscAngleToDiscPosition(19 * M_PI / 180));
121
122// I measured the angle between 2 discs. That then gives me the distance
123// between 2 posedges (or negedges). Then subtract off the width of the
124// positive pulse, and that gives the width of the negative pulse.
125/*static*/ const double IndexMotor::kTopDiscDetectMinSeperation =
126 (IndexMotor::ConvertDiscAngleToDiscPosition(120 * M_PI / 180) -
127 (IndexMotor::kTopDiscDetectStop - IndexMotor::kTopDiscDetectStart));
Austin Schuhf8c52252013-03-03 02:25:49 -0800128
Austin Schuhd78ab542013-03-01 22:22:19 -0800129const /*static*/ double IndexMotor::kDiscRadius = 10.875 * 0.0254 / 2;
130const /*static*/ double IndexMotor::kRollerRadius = 2.0 * 0.0254 / 2;
Austin Schuhf8c52252013-03-03 02:25:49 -0800131const /*static*/ double IndexMotor::kTransferRollerRadius = 1.25 * 0.0254 / 2;
Austin Schuhd78ab542013-03-01 22:22:19 -0800132
Austin Schuhf8c52252013-03-03 02:25:49 -0800133/*static*/ const int IndexMotor::kGrabbingDelay = 5;
134/*static*/ const int IndexMotor::kLiftingDelay = 20;
135/*static*/ const int IndexMotor::kShootingDelay = 5;
136/*static*/ const int IndexMotor::kLoweringDelay = 20;
Austin Schuhd78ab542013-03-01 22:22:19 -0800137
Austin Schuh93485832013-03-04 00:01:34 -0800138// TODO(aschuh): Tune these.
139/*static*/ const double
140 IndexMotor::IndexStateFeedbackLoop::kMinMotionVoltage = 5.0;
141/*static*/ const double
142 IndexMotor::IndexStateFeedbackLoop::kNoMotionCuttoffCount = 30;
143
Austin Schuhd78ab542013-03-01 22:22:19 -0800144// Distance to move the indexer when grabbing a disc.
145const double kNextPosition = 10.0;
146
147/*static*/ double IndexMotor::ConvertDiscAngleToIndex(const double angle) {
148 return (angle * (1 + (kDiscRadius * 2 + kRollerRadius) / kRollerRadius));
149}
150
Austin Schuhf8c52252013-03-03 02:25:49 -0800151/*static*/ double IndexMotor::ConvertDiscAngleToDiscPosition(
152 const double angle) {
Austin Schuhd78ab542013-03-01 22:22:19 -0800153 return angle * (kDiscRadius + kRollerRadius);
154}
155
Austin Schuhf8c52252013-03-03 02:25:49 -0800156/*static*/ double IndexMotor::ConvertDiscPositionToDiscAngle(
157 const double position) {
158 return position / (kDiscRadius + kRollerRadius);
159}
160
Austin Schuhd78ab542013-03-01 22:22:19 -0800161/*static*/ double IndexMotor::ConvertIndexToDiscAngle(const double angle) {
162 return (angle / (1 + (kDiscRadius * 2 + kRollerRadius) / kRollerRadius));
163}
164
165/*static*/ double IndexMotor::ConvertIndexToDiscPosition(const double angle) {
166 return IndexMotor::ConvertDiscAngleToDiscPosition(
167 ConvertIndexToDiscAngle(angle));
168}
169
Austin Schuhf8c52252013-03-03 02:25:49 -0800170/*static*/ double IndexMotor::ConvertTransferToDiscPosition(
171 const double angle) {
172 const double gear_ratio = (1 + (kDiscRadius * 2 + kTransferRollerRadius) /
173 kTransferRollerRadius);
174 return angle / gear_ratio * (kDiscRadius + kTransferRollerRadius);
175}
176
177/*static*/ double IndexMotor::ConvertDiscPositionToIndex(
178 const double position) {
179 return IndexMotor::ConvertDiscAngleToIndex(
180 ConvertDiscPositionToDiscAngle(position));
181}
182
Austin Schuh1b864a12013-03-07 00:46:50 -0800183bool IndexMotor::MinDiscPosition(double *disc_position, Frisbee **found_disc) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800184 bool found_start = false;
185 for (unsigned int i = 0; i < frisbees_.size(); ++i) {
Austin Schuh1b864a12013-03-07 00:46:50 -0800186 Frisbee &frisbee = frisbees_[i];
Austin Schuhf8c52252013-03-03 02:25:49 -0800187 if (!found_start) {
188 if (frisbee.has_position()) {
189 *disc_position = frisbee.position();
Austin Schuh1b864a12013-03-07 00:46:50 -0800190 if (found_disc) {
191 *found_disc = &frisbee;
192 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800193 found_start = true;
194 }
195 } else {
Austin Schuh1b864a12013-03-07 00:46:50 -0800196 if (frisbee.position() <= *disc_position) {
197 *disc_position = frisbee.position();
198 if (found_disc) {
199 *found_disc = &frisbee;
200 }
201 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800202 }
203 }
204 return found_start;
205}
206
Austin Schuh1b864a12013-03-07 00:46:50 -0800207bool IndexMotor::MaxDiscPosition(double *disc_position, Frisbee **found_disc) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800208 bool found_start = false;
209 for (unsigned int i = 0; i < frisbees_.size(); ++i) {
Austin Schuh1b864a12013-03-07 00:46:50 -0800210 Frisbee &frisbee = frisbees_[i];
Austin Schuhf8c52252013-03-03 02:25:49 -0800211 if (!found_start) {
212 if (frisbee.has_position()) {
213 *disc_position = frisbee.position();
Austin Schuh1b864a12013-03-07 00:46:50 -0800214 if (found_disc) {
215 *found_disc = &frisbee;
216 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800217 found_start = true;
218 }
219 } else {
Austin Schuh1b864a12013-03-07 00:46:50 -0800220 if (frisbee.position() > *disc_position) {
221 *disc_position = frisbee.position();
222 if (found_disc) {
223 *found_disc = &frisbee;
224 }
225 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800226 }
227 }
228 return found_start;
229}
230
Austin Schuh93485832013-03-04 00:01:34 -0800231void IndexMotor::IndexStateFeedbackLoop::CapU() {
232 // If the voltage has been low for a large number of cycles, cut the motor
233 // power. This is generally very bad controls practice since this isn't LTI,
234 // but we don't really care about tracking anything other than large step
235 // inputs, and the loader doesn't need to be that accurate.
236 if (::std::abs(U(0, 0)) < kMinMotionVoltage) {
237 ++low_voltage_count_;
238 if (low_voltage_count_ > kNoMotionCuttoffCount) {
239 printf("Limiting power from %f to 0\n", U(0, 0));
240 U(0, 0) = 0.0;
241 }
242 } else {
243 low_voltage_count_ = 0;
244 }
245
246 for (int i = 0; i < kNumOutputs; ++i) {
247 if (U[i] > plant.U_max[i]) {
248 U[i] = plant.U_max[i];
249 } else if (U[i] < plant.U_min[i]) {
250 U[i] = plant.U_min[i];
251 }
252 }
253}
254
255
Austin Schuhd78ab542013-03-01 22:22:19 -0800256// Positive angle is towards the shooter, and positive power is towards the
257// shooter.
258void IndexMotor::RunIteration(
259 const control_loops::IndexLoop::Goal *goal,
260 const control_loops::IndexLoop::Position *position,
261 control_loops::IndexLoop::Output *output,
262 control_loops::IndexLoop::Status *status) {
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800263 // Make goal easy to work with and sanity check it.
Austin Schuhd78ab542013-03-01 22:22:19 -0800264 Goal goal_enum = static_cast<Goal>(goal->goal_state);
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800265 if (goal->goal_state < 0 || goal->goal_state > 4) {
Brian Silverman94195052013-03-09 13:45:05 -0800266 LOG(ERROR, "Goal state is %"PRId32" which is out of range. Going to HOLD.\n",
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800267 goal->goal_state);
268 goal_enum = Goal::HOLD;
269 }
Austin Schuhd78ab542013-03-01 22:22:19 -0800270
271 // Disable the motors now so that all early returns will return with the
272 // motors disabled.
Austin Schuhb6d898b2013-03-03 15:34:35 -0800273 double intake_voltage = 0.0;
Austin Schuhf8c52252013-03-03 02:25:49 -0800274 double transfer_voltage = 0.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800275 if (output) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800276 output->intake_voltage = 0.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800277 output->transfer_voltage = 0.0;
278 output->index_voltage = 0.0;
279 }
280
281 status->ready_to_intake = false;
282
Austin Schuhf8c52252013-03-03 02:25:49 -0800283 // Compute a safe index position that we can use.
Austin Schuhd78ab542013-03-01 22:22:19 -0800284 if (position) {
285 wrist_loop_->Y << position->index_position;
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800286 // Set the goal to be the current position if this is the first time through
287 // so we don't always spin the indexer to the 0 position before starting.
288 if (no_prior_position_) {
289 wrist_loop_->R << wrist_loop_->Y(0, 0), 0.0;
Austin Schuhc5ef1bb2013-03-10 00:42:05 -0800290 wrist_loop_->X_hat(0, 0) = wrist_loop_->Y(0, 0);
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800291 no_prior_position_ = false;
Austin Schuh6328daf2013-03-05 00:53:15 -0800292 last_bottom_disc_posedge_count_ = position->bottom_disc_posedge_count;
293 last_bottom_disc_negedge_count_ = position->bottom_disc_negedge_count;
294 last_bottom_disc_negedge_wait_count_ =
295 position->bottom_disc_negedge_wait_count;
Austin Schuh825bde92013-03-06 00:16:46 -0800296 last_top_disc_posedge_count_ = position->top_disc_posedge_count;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800297 last_top_disc_negedge_count_ = position->top_disc_negedge_count;
298 // The open positions for the upper is right here and isn't a hard edge.
299 upper_open_index_position_ = wrist_loop_->Y(0, 0);
300 upper_open_index_position_was_negedge_ = false;
301 lower_open_index_position_ = wrist_loop_->Y(0, 0);
302 lower_open_index_position_was_negedge_ = false;
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800303 }
304
Austin Schuh1b864a12013-03-07 00:46:50 -0800305 // If the cRIO is gone for over 1/2 of a second, assume that it rebooted.
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800306 if (missing_position_count_ > 50) {
Austin Schuh6328daf2013-03-05 00:53:15 -0800307 last_bottom_disc_posedge_count_ = position->bottom_disc_posedge_count;
308 last_bottom_disc_negedge_count_ = position->bottom_disc_negedge_count;
309 last_bottom_disc_negedge_wait_count_ =
310 position->bottom_disc_negedge_wait_count;
Austin Schuh825bde92013-03-06 00:16:46 -0800311 last_top_disc_posedge_count_ = position->top_disc_posedge_count;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800312 last_top_disc_negedge_count_ = position->top_disc_negedge_count;
313 // We can't really trust the open range any more if the crio rebooted.
314 upper_open_index_position_ = wrist_loop_->Y(0, 0);
315 upper_open_index_position_was_negedge_ = false;
316 lower_open_index_position_ = wrist_loop_->Y(0, 0);
317 lower_open_index_position_was_negedge_ = false;
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800318 // Adjust the disc positions so that they don't have to move.
319 const double disc_offset =
320 position->index_position - wrist_loop_->X_hat(0, 0);
321 for (auto frisbee = frisbees_.begin();
322 frisbee != frisbees_.end(); ++frisbee) {
323 frisbee->OffsetDisc(disc_offset);
324 }
Austin Schuhc5ef1bb2013-03-10 00:42:05 -0800325 wrist_loop_->X_hat(0, 0) = wrist_loop_->Y(0, 0);
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800326 }
327 missing_position_count_ = 0;
328 } else {
329 ++missing_position_count_;
Austin Schuhd78ab542013-03-01 22:22:19 -0800330 }
331 const double index_position = wrist_loop_->X_hat(0, 0);
332
Austin Schuh825bde92013-03-06 00:16:46 -0800333 if (position) {
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800334 // Reset the open region if we saw a negedge.
335 if (position->top_disc_negedge_count != last_top_disc_negedge_count_) {
336 // Saw a negedge, must be a new region.
337 upper_open_index_position_ = position->top_disc_negedge_position;
338 lower_open_index_position_ = position->top_disc_negedge_position;
339 upper_open_index_position_was_negedge_ = true;
340 lower_open_index_position_was_negedge_ = true;
341 }
342
343 // No disc. Expand the open region.
344 if (!position->top_disc_detect) {
345 // If it is higher than it was before, the end of the region is no longer
346 // determined by the negedge.
347 if (index_position > upper_open_index_position_) {
348 upper_open_index_position_ = index_position;
349 upper_open_index_position_was_negedge_ = false;
350 }
351 // If it is lower than it was before, the end of the region is no longer
352 // determined by the negedge.
353 if (index_position < lower_open_index_position_) {
354 lower_open_index_position_ = index_position;
355 lower_open_index_position_was_negedge_ = false;
356 }
357 }
358
Austin Schuh825bde92013-03-06 00:16:46 -0800359 if (!position->top_disc_detect) {
360 // We don't see a disc. Verify that there are no discs that we should be
361 // seeing.
Austin Schuh1b864a12013-03-07 00:46:50 -0800362 // Assume that discs will move slow enough that we won't miss one as it
363 // goes by. They will either pile up above or below the sensor.
Austin Schuhdff24e22013-03-06 00:41:21 -0800364
365 double cumulative_offset = 0.0;
366 for (auto frisbee = frisbees_.rbegin(), rend = frisbees_.rend();
367 frisbee != rend; ++frisbee) {
368 frisbee->OffsetDisc(cumulative_offset);
369 double amount_moved = frisbee->ObserveNoTopDiscSensor(
Austin Schuh825bde92013-03-06 00:16:46 -0800370 wrist_loop_->X_hat(0, 0), wrist_loop_->X_hat(1, 0));
Austin Schuhdff24e22013-03-06 00:41:21 -0800371 cumulative_offset += amount_moved;
Austin Schuh825bde92013-03-06 00:16:46 -0800372 }
373 }
Austin Schuh1b864a12013-03-07 00:46:50 -0800374
Austin Schuh825bde92013-03-06 00:16:46 -0800375 if (position->top_disc_posedge_count != last_top_disc_posedge_count_) {
Austin Schuh1b864a12013-03-07 00:46:50 -0800376 const double index_position = wrist_loop_->X_hat(0, 0) -
377 position->index_position + position->top_disc_posedge_position;
Austin Schuh825bde92013-03-06 00:16:46 -0800378 // TODO(aschuh): Sanity check this number...
379 // Requires storing when the disc was last seen with the sensor off, and
380 // figuring out what to do if things go south.
381
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800382 // 1 if discs are going up, 0 if we have no clue, and -1 if they are going
383 // down.
384 int disc_direction = 0;
Austin Schuh825bde92013-03-06 00:16:46 -0800385 if (wrist_loop_->X_hat(1, 0) > 50.0) {
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800386 disc_direction = 1;
387 } else if (wrist_loop_->X_hat(1, 0) < -50.0) {
388 disc_direction = -1;
389 } else {
390 // Save the upper and lower positions that we last saw a disc at.
391 // If there is a big buffer above, must be a disc from below.
392 // If there is a big buffer below, must be a disc from above.
393 // This should work to replace the velocity threshold above.
394
395 const double open_width =
396 upper_open_index_position_ - lower_open_index_position_;
397 const double relative_upper_open_precentage =
398 (upper_open_index_position_ - index_position) / open_width;
399 const double relative_lower_open_precentage =
400 (index_position - lower_open_index_position_) / open_width;
401 printf("Width %f upper %f lower %f\n",
402 open_width, relative_upper_open_precentage,
403 relative_lower_open_precentage);
404
405 if (ConvertIndexToDiscPosition(open_width) <
406 kTopDiscDetectMinSeperation * 0.9) {
407 LOG(ERROR, "Discs are way too close to each other. Doing nothing\n");
408 } else if (relative_upper_open_precentage > 0.75) {
409 // Looks like it is a disc going down from above since we are near
410 // the upper edge.
411 disc_direction = -1;
412 printf("Disc edge going down\n");
413 } else if (relative_lower_open_precentage > 0.75) {
414 // Looks like it is a disc going up from below since we are near
415 // the lower edge.
416 disc_direction = 1;
417 printf("Disc edge going up\n");
418 } else {
419 LOG(ERROR,
420 "Got an edge in the middle of what should be an open region.\n");
421 LOG(ERROR, "Open width: %f upper precentage %f %%\n",
422 open_width, relative_upper_open_precentage);
423 }
424 }
425
426 if (disc_direction > 0) {
Austin Schuh825bde92013-03-06 00:16:46 -0800427 // Moving up at a reasonable clip.
Austin Schuh1b864a12013-03-07 00:46:50 -0800428 // Find the highest disc that is below the top disc sensor.
429 // While we are at it, count the number above and log an error if there
430 // are too many.
431 if (frisbees_.size() == 0) {
432 Frisbee new_frisbee;
433 new_frisbee.has_been_indexed_ = true;
434 new_frisbee.index_start_position_ = index_position -
435 ConvertDiscPositionToIndex(kTopDiscDetectStart -
436 kIndexStartPosition);
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800437 ++hopper_disc_count_;
438 ++total_disc_count_;
Austin Schuh1b864a12013-03-07 00:46:50 -0800439 frisbees_.push_front(new_frisbee);
440 LOG(WARNING, "Added a disc to the hopper at the top sensor\n");
441 }
442
443 int above_disc_count = 0;
444 double highest_position = 0;
445 Frisbee *highest_frisbee_below_sensor = NULL;
446 for (auto frisbee = frisbees_.rbegin(), rend = frisbees_.rend();
447 frisbee != rend; ++frisbee) {
448 const double disc_position = frisbee->absolute_position(
449 index_position);
450 // It is save to use the top position for the cuttoff, since the
451 // sensor being low will result in discs being pushed off of it.
452 if (disc_position >= kTopDiscDetectStop) {
453 ++above_disc_count;
454 } else if (!highest_frisbee_below_sensor ||
455 disc_position > highest_position) {
456 highest_frisbee_below_sensor = &*frisbee;
457 highest_position = disc_position;
458 }
459 }
460 if (above_disc_count > 1) {
461 LOG(ERROR, "We have 2 discs above the top sensor.\n");
462 }
463
464 // We now have the disc. Shift all the ones below the sensor up by the
465 // computed delta.
466 const double disc_delta = IndexMotor::ConvertDiscPositionToIndex(
467 highest_position - kTopDiscDetectStart);
468 for (auto frisbee = frisbees_.rbegin(), rend = frisbees_.rend();
469 frisbee != rend; ++frisbee) {
470 const double disc_position = frisbee->absolute_position(
471 index_position);
472 if (disc_position < kTopDiscDetectStop) {
473 frisbee->OffsetDisc(disc_delta);
474 }
475 }
476 printf("Currently have %d discs, saw posedge moving up. "
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800477 "Moving down by %f to %f\n", frisbees_.size(),
Austin Schuh1b864a12013-03-07 00:46:50 -0800478 ConvertIndexToDiscPosition(disc_delta),
479 highest_frisbee_below_sensor->absolute_position(
480 wrist_loop_->X_hat(0, 0)));
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800481 } else if (disc_direction < 0) {
Austin Schuh825bde92013-03-06 00:16:46 -0800482 // Moving down at a reasonable clip.
Austin Schuh1b864a12013-03-07 00:46:50 -0800483 // There can only be 1 disc up top that would give us a posedge.
484 // Find it and place it at the one spot that it can be.
Brian Silverman94195052013-03-09 13:45:05 -0800485 double min_disc_position = 0;
Austin Schuh1b864a12013-03-07 00:46:50 -0800486 Frisbee *min_frisbee = NULL;
487 MinDiscPosition(&min_disc_position, &min_frisbee);
488 if (!min_frisbee) {
489 // Uh, oh, we see a disc but there isn't one...
490 LOG(ERROR, "Saw a disc up top but there isn't one in the hopper\n");
491 } else {
492 const double disc_position = min_frisbee->absolute_position(
493 index_position);
494
495 const double disc_delta_meters = disc_position - kTopDiscDetectStop;
496 const double disc_delta = IndexMotor::ConvertDiscPositionToIndex(
497 disc_delta_meters);
498 printf("Posedge going down. Moving top disc down by %f\n",
499 disc_delta_meters);
500 for (auto frisbee = frisbees_.begin(), end = frisbees_.end();
501 frisbee != end; ++frisbee) {
502 frisbee->OffsetDisc(disc_delta);
503 }
504 }
Austin Schuh825bde92013-03-06 00:16:46 -0800505 } else {
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800506 LOG(ERROR, "Not sure how to handle the upper posedge, doing nothing\n");
Austin Schuh825bde92013-03-06 00:16:46 -0800507 }
508 }
509 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800510
Austin Schuhf8c52252013-03-03 02:25:49 -0800511 // Bool to track if it is safe for the goal to change yet.
Austin Schuhd78ab542013-03-01 22:22:19 -0800512 bool safe_to_change_state_ = true;
513 switch (safe_goal_) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800514 case Goal::HOLD:
Austin Schuhd78ab542013-03-01 22:22:19 -0800515 // The goal should already be good, so sit tight with everything the same
516 // as it was.
Austin Schuhd78ab542013-03-01 22:22:19 -0800517 break;
Austin Schuhf8c52252013-03-03 02:25:49 -0800518 case Goal::READY_LOWER:
519 case Goal::INTAKE:
Austin Schuhd78ab542013-03-01 22:22:19 -0800520 {
521 Time now = Time::Now();
Austin Schuhd78ab542013-03-01 22:22:19 -0800522 if (position) {
Austin Schuh6328daf2013-03-05 00:53:15 -0800523 // Posedge of the disc entering the beam break.
524 if (position->bottom_disc_posedge_count !=
525 last_bottom_disc_posedge_count_) {
Austin Schuhd78ab542013-03-01 22:22:19 -0800526 transfer_frisbee_.Reset();
527 transfer_frisbee_.bottom_posedge_time_ = now;
528 printf("Posedge of bottom disc %f\n",
529 transfer_frisbee_.bottom_posedge_time_.ToSeconds());
530 ++hopper_disc_count_;
Austin Schuhf8c52252013-03-03 02:25:49 -0800531 ++total_disc_count_;
Austin Schuhd78ab542013-03-01 22:22:19 -0800532 }
533
534 // Disc exited the beam break now.
Austin Schuh6328daf2013-03-05 00:53:15 -0800535 if (position->bottom_disc_negedge_count !=
536 last_bottom_disc_negedge_count_) {
Austin Schuhd78ab542013-03-01 22:22:19 -0800537 transfer_frisbee_.bottom_negedge_time_ = now;
538 printf("Negedge of bottom disc %f\n",
539 transfer_frisbee_.bottom_negedge_time_.ToSeconds());
540 frisbees_.push_front(transfer_frisbee_);
541 }
542
543 if (position->bottom_disc_detect) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800544 intake_voltage = transfer_voltage = 12.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800545 // Must wait until the disc gets out before we can change state.
546 safe_to_change_state_ = false;
547
Austin Schuhf8c52252013-03-03 02:25:49 -0800548 // TODO(aschuh): A disc on the way through needs to start moving
549 // the indexer if it isn't already moving. Maybe?
Austin Schuhd78ab542013-03-01 22:22:19 -0800550
551 Time elapsed_posedge_time = now -
552 transfer_frisbee_.bottom_posedge_time_;
553 if (elapsed_posedge_time >= Time::InSeconds(0.3)) {
554 // It has been too long. The disc must be jammed.
555 LOG(ERROR, "Been way too long. Jammed disc?\n");
556 printf("Been way too long. Jammed disc?\n");
557 }
558 }
559
Austin Schuhf8c52252013-03-03 02:25:49 -0800560 // Check all non-indexed discs and see if they should be indexed.
Austin Schuhb6d898b2013-03-03 15:34:35 -0800561 for (auto frisbee = frisbees_.begin();
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800562 frisbee != frisbees_.end(); ++frisbee) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800563 if (!frisbee->has_been_indexed_) {
564 intake_voltage = transfer_voltage = 12.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800565
Austin Schuh6328daf2013-03-05 00:53:15 -0800566 if (last_bottom_disc_negedge_wait_count_ !=
567 position->bottom_disc_negedge_wait_count) {
568 // We have an index difference.
569 // Save the indexer position, and the time.
570 if (last_bottom_disc_negedge_wait_count_ + 1 !=
571 position->bottom_disc_negedge_wait_count) {
572 LOG(ERROR, "Funny, we got 2 edges since we last checked.\n");
573 }
574
575 // Save the captured position as the position at which the disc
576 // touched the indexer.
Austin Schuhd78ab542013-03-01 22:22:19 -0800577 LOG(INFO, "Grabbed on the index now at %f\n", index_position);
578 printf("Grabbed on the index now at %f\n", index_position);
Austin Schuhb6d898b2013-03-03 15:34:35 -0800579 frisbee->has_been_indexed_ = true;
Austin Schuh6328daf2013-03-05 00:53:15 -0800580 frisbee->index_start_position_ =
581 position->bottom_disc_negedge_wait_position;
Austin Schuhd78ab542013-03-01 22:22:19 -0800582 }
583 }
Austin Schuhb6d898b2013-03-03 15:34:35 -0800584 if (!frisbee->has_been_indexed_) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800585 // All discs must be indexed before it is safe to stop indexing.
Austin Schuhd78ab542013-03-01 22:22:19 -0800586 safe_to_change_state_ = false;
587 }
588 }
589
Austin Schuhf8c52252013-03-03 02:25:49 -0800590 // Figure out where the indexer should be to move the discs down to
591 // the right position.
Brian Silverman94195052013-03-09 13:45:05 -0800592 double max_disc_position = 0;
Austin Schuh1b864a12013-03-07 00:46:50 -0800593 if (MaxDiscPosition(&max_disc_position, NULL)) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800594 printf("There is a disc down here!\n");
595 // TODO(aschuh): Figure out what to do if grabbing the next one
596 // would cause things to jam into the loader.
597 // Say we aren't ready any more. Undefined behavior will result if
598 // that isn't observed.
599 double bottom_disc_position =
600 max_disc_position + ConvertDiscAngleToIndex(M_PI);
601 wrist_loop_->R << bottom_disc_position, 0.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800602
Austin Schuhf8c52252013-03-03 02:25:49 -0800603 // Verify that we are close enough to the goal so that we should be
604 // fine accepting the next disc.
605 double disc_error_meters = ConvertIndexToDiscPosition(
606 wrist_loop_->X_hat(0, 0) - bottom_disc_position);
607 // We are ready for the next disc if the first one is in the first
608 // half circle of the indexer. It will take time for the disc to
609 // come into the indexer, so we will be able to move it out of the
610 // way in time.
611 // This choice also makes sure that we don't claim that we aren't
612 // ready between full speed intaking.
613 if (-ConvertDiscAngleToIndex(M_PI) < disc_error_meters &&
614 disc_error_meters < 0.04) {
615 // We are only ready if we aren't being asked to change state or
616 // are full.
617 status->ready_to_intake =
618 (safe_goal_ == goal_enum) && hopper_disc_count_ < 4;
619 } else {
620 status->ready_to_intake = false;
Austin Schuhd78ab542013-03-01 22:22:19 -0800621 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800622 } else {
623 // No discs! We are always ready for more if we aren't being
624 // asked to change state.
625 status->ready_to_intake = (safe_goal_ == goal_enum);
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800626 printf("Ready to intake, zero discs. %d %d %d\n",
627 status->ready_to_intake, hopper_disc_count_, safe_goal_);
Austin Schuhd78ab542013-03-01 22:22:19 -0800628 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800629
630 // Turn on the transfer roller if we are ready.
631 if (status->ready_to_intake && hopper_disc_count_ < 4 &&
632 safe_goal_ == Goal::INTAKE) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800633 intake_voltage = transfer_voltage = 12.0;
Austin Schuhf8c52252013-03-03 02:25:49 -0800634 }
Austin Schuhd78ab542013-03-01 22:22:19 -0800635 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800636 printf("INTAKE\n");
Austin Schuhd78ab542013-03-01 22:22:19 -0800637 }
638 break;
Austin Schuhf8c52252013-03-03 02:25:49 -0800639 case Goal::READY_SHOOTER:
640 case Goal::SHOOT:
641 // Check if we have any discs to shoot or load and handle them.
Brian Silverman94195052013-03-09 13:45:05 -0800642 double min_disc_position = 0;
Austin Schuh1b864a12013-03-07 00:46:50 -0800643 if (MinDiscPosition(&min_disc_position, NULL)) {
644 const double ready_disc_position = min_disc_position +
645 ConvertDiscPositionToIndex(kReadyToPreload - kIndexStartPosition);
Austin Schuhf8c52252013-03-03 02:25:49 -0800646
647 const double grabbed_disc_position =
648 min_disc_position +
649 ConvertDiscPositionToIndex(kReadyToLiftPosition -
650 kIndexStartPosition + 0.03);
651
652 // Check the state of the loader FSM.
653 // If it is ready to load discs, position the disc so that it is ready
654 // to be grabbed.
655 // If it isn't ready, there is a disc in there. It needs to finish it's
656 // cycle first.
657 if (loader_state_ != LoaderState::READY) {
658 // We already have a disc in the loader.
659 // Stage the discs back a bit.
660 wrist_loop_->R << ready_disc_position, 0.0;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800661 printf("Loader not ready but asked to shoot\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800662
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800663 // Shoot if we are grabbed and being asked to shoot.
664 if (loader_state_ == LoaderState::GRABBED &&
665 safe_goal_ == Goal::SHOOT) {
666 loader_goal_ = LoaderGoal::SHOOT_AND_RESET;
667 }
668
Austin Schuhf8c52252013-03-03 02:25:49 -0800669 // Must wait until it has been grabbed to continue.
670 if (loader_state_ == LoaderState::GRABBING) {
671 safe_to_change_state_ = false;
672 }
673 } else {
674 // No disc up top right now.
675 wrist_loop_->R << grabbed_disc_position, 0.0;
676
677 // See if the disc has gotten pretty far up yet.
678 if (wrist_loop_->X_hat(0, 0) > ready_disc_position) {
679 // Point of no return. We are committing to grabbing it now.
680 safe_to_change_state_ = false;
681 const double robust_grabbed_disc_position =
682 (grabbed_disc_position -
683 ConvertDiscPositionToIndex(kGrabberLength));
684
685 // If close, start grabbing and/or shooting.
686 if (wrist_loop_->X_hat(0, 0) > robust_grabbed_disc_position) {
687 // Start the state machine.
688 if (safe_goal_ == Goal::SHOOT) {
689 loader_goal_ = LoaderGoal::SHOOT_AND_RESET;
690 } else {
691 loader_goal_ = LoaderGoal::GRAB;
692 }
693 // This frisbee is now gone. Take it out of the queue.
694 frisbees_.pop_back();
Austin Schuhf8c52252013-03-03 02:25:49 -0800695 }
696 }
697 }
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800698 } else {
699 if (loader_state_ != LoaderState::READY) {
700 // Shoot if we are grabbed and being asked to shoot.
701 if (loader_state_ == LoaderState::GRABBED &&
702 safe_goal_ == Goal::SHOOT) {
703 loader_goal_ = LoaderGoal::SHOOT_AND_RESET;
704 }
705 } else {
706 // Ok, no discs in sight. Spin the hopper up by 150% of it's full
707 // range and verify that we don't see anything.
708 printf("Moving the indexer to verify that it is clear\n");
709 const double hopper_clear_verification_position =
710 lower_open_index_position_ +
711 ConvertDiscPositionToIndex(kIndexFreeLength) * 1.5;
712
713 wrist_loop_->R << hopper_clear_verification_position, 0.0;
714 if (::std::abs(wrist_loop_->X_hat(0, 0) -
715 hopper_clear_verification_position) <
716 ConvertDiscPositionToIndex(0.05)) {
717 printf("Should be empty\n");
718 // We are at the end of the range. There are no more discs here.
719 while (frisbees_.size() > 0) {
720 LOG(ERROR, "Dropping an extra disc since it can't exist\n");
721 frisbees_.pop_back();
722 --hopper_disc_count_;
723 --total_disc_count_;
724 }
725 if (hopper_disc_count_ != 0) {
726 LOG(ERROR,
727 "Emptied the hopper out but there are still discs there\n");
728 }
729 }
730 }
731 }
732
733 {
734 const double hopper_clear_verification_position =
735 lower_open_index_position_ +
736 ConvertDiscPositionToIndex(kIndexFreeLength) * 1.5;
737
738 if (wrist_loop_->X_hat(0, 0) >
739 hopper_clear_verification_position +
740 ConvertDiscPositionToIndex(0.05)) {
741 // We are at the end of the range. There are no more discs here.
742 while (frisbees_.size() > 0) {
743 LOG(ERROR, "Dropping an extra disc since it can't exist\n");
744 frisbees_.pop_back();
745 --hopper_disc_count_;
746 --total_disc_count_;
747 }
748 if (hopper_disc_count_ != 0) {
749 LOG(ERROR,
750 "Emptied the hopper out but there are still discs there\n");
751 }
752 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800753 }
754
755 printf("READY_SHOOTER or SHOOT\n");
Austin Schuhd78ab542013-03-01 22:22:19 -0800756 break;
Austin Schuhf8c52252013-03-03 02:25:49 -0800757 }
758
759 // The only way out of the loader is to shoot the disc. The FSM can only go
760 // forwards.
761 switch (loader_state_) {
762 case LoaderState::READY:
763 printf("Loader READY\n");
764 // Open and down, ready to accept a disc.
765 loader_up_ = false;
766 disc_clamped_ = false;
767 disc_ejected_ = false;
768 if (loader_goal_ == LoaderGoal::GRAB ||
769 loader_goal_ == LoaderGoal::SHOOT_AND_RESET) {
770 if (loader_goal_ == LoaderGoal::GRAB) {
771 printf("Told to GRAB, moving on\n");
772 } else {
773 printf("Told to SHOOT_AND_RESET, moving on\n");
774 }
775 loader_state_ = LoaderState::GRABBING;
776 loader_countdown_ = kGrabbingDelay;
777 } else {
778 break;
779 }
780 case LoaderState::GRABBING:
781 printf("Loader GRABBING %d\n", loader_countdown_);
782 // Closing the grabber.
783 loader_up_ = false;
784 disc_clamped_ = true;
785 disc_ejected_ = false;
786 if (loader_countdown_ > 0) {
787 --loader_countdown_;
788 break;
789 } else {
790 loader_state_ = LoaderState::GRABBED;
791 }
792 case LoaderState::GRABBED:
793 printf("Loader GRABBED\n");
794 // Grabber closed.
795 loader_up_ = false;
796 disc_clamped_ = true;
797 disc_ejected_ = false;
798 if (loader_goal_ == LoaderGoal::SHOOT_AND_RESET) {
799 // TODO(aschuh): Only shoot if the shooter is up to speed.
800 // Seems like that would have us shooting a bit later than we could be,
801 // but it also probably spins back up real fast.
802 loader_state_ = LoaderState::LIFTING;
803 loader_countdown_ = kLiftingDelay;
804 printf("Told to SHOOT_AND_RESET, moving on\n");
805 } else if (loader_goal_ == LoaderGoal::READY) {
806 LOG(ERROR, "Can't go to ready when we have something grabbed.\n");
807 printf("Can't go to ready when we have something grabbed.\n");
808 break;
809 } else {
810 break;
811 }
812 case LoaderState::LIFTING:
813 printf("Loader LIFTING %d\n", loader_countdown_);
814 // Lifting the disc.
815 loader_up_ = true;
816 disc_clamped_ = true;
817 disc_ejected_ = false;
818 if (loader_countdown_ > 0) {
819 --loader_countdown_;
820 break;
821 } else {
822 loader_state_ = LoaderState::LIFTED;
823 }
824 case LoaderState::LIFTED:
825 printf("Loader LIFTED\n");
826 // Disc lifted. Time to eject it out.
827 loader_up_ = true;
828 disc_clamped_ = true;
829 disc_ejected_ = false;
830 loader_state_ = LoaderState::SHOOTING;
831 loader_countdown_ = kShootingDelay;
832 case LoaderState::SHOOTING:
833 printf("Loader SHOOTING %d\n", loader_countdown_);
834 // Ejecting the disc into the shooter.
835 loader_up_ = true;
836 disc_clamped_ = false;
837 disc_ejected_ = true;
838 if (loader_countdown_ > 0) {
839 --loader_countdown_;
840 break;
841 } else {
842 loader_state_ = LoaderState::SHOOT;
843 }
844 case LoaderState::SHOOT:
845 printf("Loader SHOOT\n");
846 // The disc has been shot.
847 loader_up_ = true;
848 disc_clamped_ = false;
849 disc_ejected_ = true;
850 loader_state_ = LoaderState::LOWERING;
851 loader_countdown_ = kLoweringDelay;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800852 --hopper_disc_count_;
Austin Schuhf8c52252013-03-03 02:25:49 -0800853 case LoaderState::LOWERING:
854 printf("Loader LOWERING %d\n", loader_countdown_);
855 // Lowering the loader back down.
856 loader_up_ = false;
857 disc_clamped_ = false;
858 disc_ejected_ = true;
859 if (loader_countdown_ > 0) {
860 --loader_countdown_;
861 break;
862 } else {
863 loader_state_ = LoaderState::LOWERED;
864 }
865 case LoaderState::LOWERED:
866 printf("Loader LOWERED\n");
867 // The indexer is lowered.
868 loader_up_ = false;
869 disc_clamped_ = false;
870 disc_ejected_ = false;
871 loader_state_ = LoaderState::READY;
872 // Once we have shot, we need to hang out in READY until otherwise
873 // notified.
874 loader_goal_ = LoaderGoal::READY;
Austin Schuhd78ab542013-03-01 22:22:19 -0800875 break;
876 }
877
878 // Update the observer.
879 wrist_loop_->Update(position != NULL, output == NULL);
880
881 if (position) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800882 LOG(DEBUG, "pos=%f\n", position->index_position);
Austin Schuhd78ab542013-03-01 22:22:19 -0800883 last_bottom_disc_detect_ = position->bottom_disc_detect;
Austin Schuh825bde92013-03-06 00:16:46 -0800884 last_top_disc_detect_ = position->top_disc_detect;
Austin Schuh6328daf2013-03-05 00:53:15 -0800885 last_bottom_disc_posedge_count_ = position->bottom_disc_posedge_count;
886 last_bottom_disc_negedge_count_ = position->bottom_disc_negedge_count;
887 last_bottom_disc_negedge_wait_count_ =
888 position->bottom_disc_negedge_wait_count;
Austin Schuh825bde92013-03-06 00:16:46 -0800889 last_top_disc_posedge_count_ = position->top_disc_posedge_count;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800890 last_top_disc_negedge_count_ = position->top_disc_negedge_count;
Austin Schuhd78ab542013-03-01 22:22:19 -0800891 }
892
893 status->hopper_disc_count = hopper_disc_count_;
894 status->total_disc_count = total_disc_count_;
Austin Schuhf8c52252013-03-03 02:25:49 -0800895 status->preloaded = (loader_state_ != LoaderState::READY);
Austin Schuhd78ab542013-03-01 22:22:19 -0800896
897 if (output) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800898 output->intake_voltage = intake_voltage;
Austin Schuhf8c52252013-03-03 02:25:49 -0800899 output->transfer_voltage = transfer_voltage;
Austin Schuhd78ab542013-03-01 22:22:19 -0800900 output->index_voltage = wrist_loop_->U(0, 0);
Austin Schuhf8c52252013-03-03 02:25:49 -0800901 output->loader_up = loader_up_;
902 output->disc_clamped = disc_clamped_;
903 output->disc_ejected = disc_ejected_;
Austin Schuhd78ab542013-03-01 22:22:19 -0800904 }
905
906 if (safe_to_change_state_) {
907 safe_goal_ = goal_enum;
908 }
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800909 if (hopper_disc_count_ < 0) {
910 LOG(ERROR, "NEGATIVE DISCS. VERY VERY BAD\n");
911 }
Austin Schuhd78ab542013-03-01 22:22:19 -0800912}
913
914} // namespace control_loops
915} // namespace frc971