blob: 1a19cbc05ed8318e979c19866f90e03cf338e8bc [file] [log] [blame]
James Kuszmaulcdd033e2013-03-02 15:10:43 -08001#include <unistd.h>
2
3#include <memory>
4
5#include "gtest/gtest.h"
6#include "aos/common/queue.h"
7#include "aos/common/queue_testutils.h"
8#include "frc971/control_loops/shooter_motor.q.h"
9#include "frc971/control_loops/shooter.h"
10#include "frc971/constants.h"
11
12using ::aos::time::Time;
13
14namespace frc971 {
15namespace control_loops {
16namespace testing {
17
Austin Schuh0e38a5d2013-03-03 03:53:35 -080018// Class which simulates the shooter and sends out queue messages with the
James Kuszmaulcdd033e2013-03-02 15:10:43 -080019// position.
20class ShooterMotorSimulation {
21 public:
22 // Constructs a shooter simulation. I'm not sure how the construction of
23 // the queue (my_shooter_loop_) actually works (same format as wrist).
24 ShooterMotorSimulation()
25 : shooter_plant_(new StateFeedbackPlant<2, 1, 1>(MakeShooterPlant())),
26 my_shooter_loop_(".frc971.control_loops.shooter",
27 0x78d8e372, ".frc971.control_loops.shooter.goal",
28 ".frc971.control_loops.shooter.position",
29 ".frc971.control_loops.shooter.output",
30 ".frc971.control_loops.shooter.status") {
James Kuszmaulcdd033e2013-03-02 15:10:43 -080031 }
32
33 // Sends a queue message with the position of the shooter.
34 void SendPositionMessage() {
Austin Schuh0e38a5d2013-03-03 03:53:35 -080035 ::aos::ScopedMessagePtr<control_loops::ShooterLoop::Position> position =
James Kuszmaulcdd033e2013-03-02 15:10:43 -080036 my_shooter_loop_.position.MakeMessage();
37 position->position = shooter_plant_->Y(0, 0);
38 position.Send();
39 }
40
41 // Simulates shooter for a single timestep.
42 void Simulate() {
43 EXPECT_TRUE(my_shooter_loop_.output.FetchLatest());
Austin Schuh0e38a5d2013-03-03 03:53:35 -080044 shooter_plant_->U << my_shooter_loop_.output->voltage;
James Kuszmaulcdd033e2013-03-02 15:10:43 -080045 shooter_plant_->Update();
46 }
47
48 ::std::unique_ptr<StateFeedbackPlant<2, 1, 1>> shooter_plant_;
Austin Schuh0e38a5d2013-03-03 03:53:35 -080049
James Kuszmaulcdd033e2013-03-02 15:10:43 -080050 private:
51 ShooterLoop my_shooter_loop_;
52};
53
54class ShooterTest : public ::testing::Test {
55 protected:
James Kuszmaulcdd033e2013-03-02 15:10:43 -080056 ShooterTest() : my_shooter_loop_(".frc971.control_loops.shooter",
Austin Schuh0e38a5d2013-03-03 03:53:35 -080057 0x78d8e372,
James Kuszmaulcdd033e2013-03-02 15:10:43 -080058 ".frc971.control_loops.shooter.goal",
59 ".frc971.control_loops.shooter.position",
60 ".frc971.control_loops.shooter.output",
Austin Schuh0e38a5d2013-03-03 03:53:35 -080061 ".frc971.control_loops.shooter.status"),
James Kuszmaulcdd033e2013-03-02 15:10:43 -080062 shooter_motor_(&my_shooter_loop_),
63 shooter_motor_plant_() {
64 // Flush the robot state queue so we can use clean shared memory for this
65 // test.
66 ::aos::robot_state.Clear();
67 SendDSPacket(true);
68 }
69
Austin Schuh0e38a5d2013-03-03 03:53:35 -080070 virtual ~ShooterTest() {
71 ::aos::robot_state.Clear();
72 }
73
James Kuszmaulcdd033e2013-03-02 15:10:43 -080074 // Update the robot state. Without this, the Iteration of the control loop
75 // will stop all the motors and the shooter won't go anywhere.
76 void SendDSPacket(bool enabled) {
77 ::aos::robot_state.MakeWithBuilder().enabled(enabled)
78 .autonomous(false)
79 .team_id(971).Send();
80 ::aos::robot_state.FetchLatest();
81 }
82
83 void VerifyNearGoal() {
84 my_shooter_loop_.goal.FetchLatest();
85 my_shooter_loop_.status.FetchLatest();
86 EXPECT_NEAR(my_shooter_loop_.goal->velocity,
87 my_shooter_loop_.status->average_velocity,
88 10.0);
89 }
90
Austin Schuh0e38a5d2013-03-03 03:53:35 -080091 // Bring up and down Core.
92 ::aos::common::testing::GlobalCoreInstance my_core;
93
94 // Create a new instance of the test queue so that it invalidates the queue
95 // that it points to. Otherwise, we will have a pointed to
96 // shared memory that is no longer valid.
97 ShooterLoop my_shooter_loop_;
98
99 // Create a control loop and simulation.
100 ShooterMotor shooter_motor_;
101 ShooterMotorSimulation shooter_motor_plant_;
James Kuszmaulcdd033e2013-03-02 15:10:43 -0800102};
103
104// Tests that the shooter does nothing when the goal is zero.
105TEST_F(ShooterTest, DoesNothing) {
106 my_shooter_loop_.goal.MakeWithBuilder().velocity(0.0).Send();
107 SendDSPacket(true);
108 shooter_motor_plant_.SendPositionMessage();
109 shooter_motor_.Iterate();
110 shooter_motor_plant_.Simulate();
111 VerifyNearGoal();
Austin Schuh0e38a5d2013-03-03 03:53:35 -0800112 my_shooter_loop_.output.FetchLatest();
113 EXPECT_EQ(my_shooter_loop_.output->voltage, 0.0);
James Kuszmaulcdd033e2013-03-02 15:10:43 -0800114}
115
116// Tests that the shooter spins up to speed and that it then spins down
117// without applying any power.
118TEST_F(ShooterTest, SpinUpAndDown) {
119 my_shooter_loop_.goal.MakeWithBuilder().velocity(450.0).Send();
120 bool is_done = false;
121 while (!is_done) {
122 shooter_motor_plant_.SendPositionMessage();
123 shooter_motor_.Iterate();
124 shooter_motor_plant_.Simulate();
125 SendDSPacket(true);
126 EXPECT_TRUE(my_shooter_loop_.status.FetchLatest());
127 is_done = my_shooter_loop_.status->ready;
128 }
129 VerifyNearGoal();
Austin Schuh0e38a5d2013-03-03 03:53:35 -0800130
James Kuszmaulcdd033e2013-03-02 15:10:43 -0800131 my_shooter_loop_.goal.MakeWithBuilder().velocity(0.0).Send();
Austin Schuh0e38a5d2013-03-03 03:53:35 -0800132 for (int i = 0; i < 100; ++i) {
133 shooter_motor_plant_.SendPositionMessage();
134 shooter_motor_.Iterate();
135 shooter_motor_plant_.Simulate();
136 SendDSPacket(true);
137 EXPECT_TRUE(my_shooter_loop_.output.FetchLatest());
138 EXPECT_EQ(0.0, my_shooter_loop_.output->voltage);
139 }
James Kuszmaulcdd033e2013-03-02 15:10:43 -0800140}
141
142// Test to make sure that it does not exceed the encoder's rated speed.
143TEST_F(ShooterTest, RateLimitTest) {
144 my_shooter_loop_.goal.MakeWithBuilder().velocity(1000.0).Send();
145 bool is_done = false;
146 while (!is_done) {
147 shooter_motor_plant_.SendPositionMessage();
148 shooter_motor_.Iterate();
149 shooter_motor_plant_.Simulate();
150 SendDSPacket(true);
151 EXPECT_TRUE(my_shooter_loop_.status.FetchLatest());
152 is_done = my_shooter_loop_.status->ready;
153 }
Austin Schuh0e38a5d2013-03-03 03:53:35 -0800154
James Kuszmaulcdd033e2013-03-02 15:10:43 -0800155 my_shooter_loop_.goal.FetchLatest();
156 my_shooter_loop_.status.FetchLatest();
Austin Schuh0e38a5d2013-03-03 03:53:35 -0800157 EXPECT_LT(shooter_motor_.kMaxSpeed,
158 shooter_motor_plant_.shooter_plant_->X(1, 0));
159}
160
161// Tests that the shooter can spin up nicely while missing position packets.
162TEST_F(ShooterTest, MissingPositionMessages) {
163 my_shooter_loop_.goal.MakeWithBuilder().velocity(200.0).Send();
164 for (int i = 0; i < 100; ++i) {
165 if (i % 7) {
166 shooter_motor_plant_.SendPositionMessage();
167 }
168 shooter_motor_.Iterate();
169 shooter_motor_plant_.Simulate();
170 SendDSPacket(true);
171 }
172
173 VerifyNearGoal();
174}
175
176// Tests that the shooter can spin up nicely while disabled for a while.
177TEST_F(ShooterTest, Disabled) {
178 my_shooter_loop_.goal.MakeWithBuilder().velocity(200.0).Send();
179 for (int i = 0; i < 100; ++i) {
180 if (i % 7) {
181 shooter_motor_plant_.SendPositionMessage();
182 }
183 shooter_motor_.Iterate();
184 shooter_motor_plant_.Simulate();
185 SendDSPacket(i > 50);
186 }
187
188 VerifyNearGoal();
James Kuszmaulcdd033e2013-03-02 15:10:43 -0800189}
190
191} // namespace testing
192} // namespace control_loops
193} // namespace frc971