blob: 63afb80eed07b56f7f987a10fcc939c2d78cf480 [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
18// Class which simulates the shooter and sends out queue messages with the
19// 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") {
31 Reinitialize();
32 }
33
34 // Resets the shooter simulation.
35 void Reinitialize() {
36 shooter_plant_->X(0, 0) = 0;
37 shooter_plant_->X(1, 0) = 0;
38 shooter_plant_->Y = shooter_plant_->C * shooter_plant_->X;
39 }
40
41 // Sends a queue message with the position of the shooter.
42 void SendPositionMessage() {
43 ::aos::ScopedMessagePtr<control_loops::ShooterLoop::Position> position =
44 my_shooter_loop_.position.MakeMessage();
45 position->position = shooter_plant_->Y(0, 0);
46 position.Send();
47 }
48
49 // Simulates shooter for a single timestep.
50 void Simulate() {
51 EXPECT_TRUE(my_shooter_loop_.output.FetchLatest());
52 shooter_plant_->U << my_shooter_loop_.output->voltage * 12;
53 shooter_plant_->Update();
54 }
55
56 ::std::unique_ptr<StateFeedbackPlant<2, 1, 1>> shooter_plant_;
57 private:
58 ShooterLoop my_shooter_loop_;
59};
60
61class ShooterTest : public ::testing::Test {
62 protected:
63 // I haven't the faintest idea exactly what this line does,
64 // but without it things break.
65 ::aos::common::testing::GlobalCoreInstance my_core;
66
67 // Create a new instance of the test queue "so that it invalidates the queue
68 // that it points to Otherwise, we will have a pointed to
69 // shared memory that is no longer valid."
70 ShooterLoop my_shooter_loop_;
71
72 // Create a control loop and simulation.
73 ShooterMotor shooter_motor_;
74 ShooterMotorSimulation shooter_motor_plant_;
75
76 ShooterTest() : my_shooter_loop_(".frc971.control_loops.shooter",
77 0x78d8e372,
78 ".frc971.control_loops.shooter.goal",
79 ".frc971.control_loops.shooter.position",
80 ".frc971.control_loops.shooter.output",
81 ".frc971.control_loops.shooter.status"),
82 shooter_motor_(&my_shooter_loop_),
83 shooter_motor_plant_() {
84 // Flush the robot state queue so we can use clean shared memory for this
85 // test.
86 ::aos::robot_state.Clear();
87 SendDSPacket(true);
88 }
89
90 // Update the robot state. Without this, the Iteration of the control loop
91 // will stop all the motors and the shooter won't go anywhere.
92 void SendDSPacket(bool enabled) {
93 ::aos::robot_state.MakeWithBuilder().enabled(enabled)
94 .autonomous(false)
95 .team_id(971).Send();
96 ::aos::robot_state.FetchLatest();
97 }
98
99 void VerifyNearGoal() {
100 my_shooter_loop_.goal.FetchLatest();
101 my_shooter_loop_.status.FetchLatest();
102 EXPECT_NEAR(my_shooter_loop_.goal->velocity,
103 my_shooter_loop_.status->average_velocity,
104 10.0);
105 }
106
107 virtual ~ShooterTest() {
108 ::aos::robot_state.Clear();
109 }
110};
111
112// Tests that the shooter does nothing when the goal is zero.
113TEST_F(ShooterTest, DoesNothing) {
114 my_shooter_loop_.goal.MakeWithBuilder().velocity(0.0).Send();
115 SendDSPacket(true);
116 shooter_motor_plant_.SendPositionMessage();
117 shooter_motor_.Iterate();
118 shooter_motor_plant_.Simulate();
119 VerifyNearGoal();
120}
121
122// Tests that the shooter spins up to speed and that it then spins down
123// without applying any power.
124TEST_F(ShooterTest, SpinUpAndDown) {
125 my_shooter_loop_.goal.MakeWithBuilder().velocity(450.0).Send();
126 bool is_done = false;
127 while (!is_done) {
128 shooter_motor_plant_.SendPositionMessage();
129 shooter_motor_.Iterate();
130 shooter_motor_plant_.Simulate();
131 SendDSPacket(true);
132 EXPECT_TRUE(my_shooter_loop_.status.FetchLatest());
133 is_done = my_shooter_loop_.status->ready;
134 }
135 VerifyNearGoal();
136 my_shooter_loop_.goal.MakeWithBuilder().velocity(0.0).Send();
137 shooter_motor_plant_.SendPositionMessage();
138 shooter_motor_.Iterate();
139 shooter_motor_plant_.Simulate();
140 SendDSPacket(true);
141 EXPECT_TRUE(my_shooter_loop_.output.FetchLatest());
142 EXPECT_EQ(0.0, my_shooter_loop_.output->voltage);
143}
144
145// Test to make sure that it does not exceed the encoder's rated speed.
146TEST_F(ShooterTest, RateLimitTest) {
147 my_shooter_loop_.goal.MakeWithBuilder().velocity(1000.0).Send();
148 bool is_done = false;
149 while (!is_done) {
150 shooter_motor_plant_.SendPositionMessage();
151 shooter_motor_.Iterate();
152 shooter_motor_plant_.Simulate();
153 SendDSPacket(true);
154 EXPECT_TRUE(my_shooter_loop_.status.FetchLatest());
155 is_done = my_shooter_loop_.status->ready;
156 }
157 my_shooter_loop_.goal.FetchLatest();
158 my_shooter_loop_.status.FetchLatest();
159 EXPECT_FALSE(std::abs(my_shooter_loop_.goal->velocity -
160 my_shooter_loop_.status->average_velocity) < 10.0);
161}
162
163} // namespace testing
164} // namespace control_loops
165} // namespace frc971