blob: d66e9500bbb27affb333ea705a4f15d4d4bce98a [file] [log] [blame]
Austin Schuh4fae0fc2018-03-27 23:51:42 -07001#include "motors/seems_reasonable/spring.h"
2
3#include "frc971/zeroing/wrap.h"
4
5#include <cmath>
6
7namespace motors {
8namespace seems_reasonable {
9namespace {
10
11constexpr float kTwoPi = 2.0 * M_PI;
12
13} // namespace
14
15float NextGoal(float current_goal, float goal) {
16 float remainder = remainderf(current_goal - goal, kTwoPi);
17 if (remainder >= 0.0f) {
18 remainder -= kTwoPi;
19 }
20 return -remainder + current_goal;
21}
22
23float PreviousGoal(float current_goal, float goal) {
24 float remainder = remainderf(current_goal - goal, kTwoPi);
25 if (remainder <= 0.0f) {
26 remainder += kTwoPi;
27 }
28 return -remainder + current_goal;
29}
30
31void Spring::Iterate(bool unload, bool prime, bool fire, bool force_reset,
32 bool encoder_valid, float angle) {
33 // Angle is +- M_PI. So, we need to find the nearest angle to the previous
34 // one, and that's our new angle.
35 angle_ = ::frc971::zeroing::Wrap(angle_, angle, kTwoPi);
36
37 switch (state_) {
38 case State::UNINITIALIZED:
39 // Go to the previous unload from where we are.
40 goal_ = angle_;
41 goal_ = PreviousGoal(kUnloadGoal);
42 if (prime && fire) {
43 Unload();
44 }
45 break;
46 case State::UNLOAD:
47 if (!encoder_valid) {
48 state_ = State::STUCK_UNLOAD;
49 } else if (!unload && prime && fire) {
50 // Go to the next goal from the current location. This handles if we
51 // fired or didn't on the previous cycle.
52 goal_ = angle_;
53 goal_ = NextGoal(kLoadGoal);
54 Load();
55 }
56 case State::STUCK_UNLOAD:
57 if (force_reset && encoder_valid && state_ == State::STUCK_UNLOAD) {
58 state_ = State::UNINITIALIZED;
59 } else if (timeout_ > 0) {
60 --timeout_;
61 }
62 break;
63 case State::LOAD:
64 if (!encoder_valid) {
65 goal_ = PreviousGoal(kUnloadGoal);
66 StuckUnload();
67 } else if (unload) {
68 goal_ = PreviousGoal(kUnloadGoal);
69 Unload();
70 } else if (!Near()) {
71 if (timeout_ > 0) {
72 --timeout_;
73 } else {
74 StuckUnload();
75 }
76 } else if (prime) {
77 goal_ = NextGoal(kPrimeGoal);
78 Prime();
79 }
80 break;
81 case State::PRIME:
82 if (!encoder_valid) {
83 goal_ = PreviousGoal(kUnloadGoal);
84 StuckUnload();
85 } else if (unload) {
86 goal_ = PreviousGoal(kUnloadGoal);
87 Unload();
88 } else if (!prime) {
89 goal_ = PreviousGoal(kLoadGoal);
90 Load();
91 } else if (!Near()) {
92 if (timeout_ > 0) {
93 --timeout_;
94 } else {
95 StuckUnload();
96 }
97 } else if (fire) {
98 goal_ = NextGoal(kFireGoal);
99 Fire();
100 }
101 break;
102
103 case State::FIRE:
104 if (!encoder_valid) {
105 goal_ = PreviousGoal(kUnloadGoal);
106 StuckUnload();
107 } else if (!Near()) {
108 if (timeout_ > 0) {
109 --timeout_;
110 } else {
111 StuckUnload();
112 }
113 } else {
114 // TODO(austin): Maybe have a different timeout for success.
115 if (timeout_ > 0) {
116 timeout_--;
117 } else {
118 Load();
119 goal_ = NextGoal(kLoadGoal);
120 }
121 }
122 break;
123 }
124 const float error = goal_ - angle_;
125 const float derror = (error - last_error_) * 200.0f;
126
127 switch (state_) {
128 case State::UNINITIALIZED:
129 output_ = 0.0f;
130 break;
131 case State::STUCK_UNLOAD:
132 case State::UNLOAD:
133 if (timeout_ > 0) {
134 output_ = -0.1f;
135 } else {
136 output_ = 0.0f;
137 }
138 break;
139
140 case State::LOAD:
141 case State::PRIME:
142 case State::FIRE: {
143 constexpr float kP = 3.00f;
144 constexpr float kD = 0.00f;
145 output_ = kP * error + kD * derror;
146 } break;
147 }
148 last_error_ = error;
149}
150
151} // namespace seems_reasonable
152} // namespace motors