blob: 2f562aa8628e0ad9ce37cda4de119696f65a075f [file] [log] [blame]
Adam Snaiderc4b3c192015-02-01 01:30:39 +00001#include <unistd.h>
2
3#include <memory>
4
5#include <random>
6
7#include "gtest/gtest.h"
Adam Snaiderc4b3c192015-02-01 01:30:39 +00008#include "frc971/zeroing/zeroing.h"
Adam Snaiderb4119252015-02-15 01:30:57 +00009#include "frc971/control_loops/control_loops.q.h"
Brian Silvermanf5f8d8e2015-12-06 18:39:12 -050010#include "aos/testing/test_shm.h"
Adam Snaiderc4b3c192015-02-01 01:30:39 +000011#include "aos/common/util/thread.h"
12#include "aos/common/die.h"
Adam Snaiderb4119252015-02-15 01:30:57 +000013#include "frc971/control_loops/position_sensor_sim.h"
Adam Snaiderc4b3c192015-02-01 01:30:39 +000014
15namespace frc971 {
16namespace zeroing {
17
Adam Snaiderb4119252015-02-15 01:30:57 +000018using control_loops::PositionSensorSimulator;
Tyler Chatowf8f03112017-02-05 14:31:34 -080019using constants::PotAndIndexPulseZeroingConstants;
Adam Snaiderc4b3c192015-02-01 01:30:39 +000020
Adam Snaiderb4119252015-02-15 01:30:57 +000021static const size_t kSampleSize = 30;
22static const double kAcceptableUnzeroedError = 0.2;
Adam Snaider3cd11c52015-02-16 02:16:09 +000023static const double kIndexErrorFraction = 0.3;
Adam Snaiderc4b3c192015-02-01 01:30:39 +000024
Adam Snaiderb4119252015-02-15 01:30:57 +000025class ZeroingTest : public ::testing::Test {
Adam Snaiderc4b3c192015-02-01 01:30:39 +000026 protected:
27 void SetUp() override { aos::SetDieTestMode(true); }
28
Tyler Chatowf8f03112017-02-05 14:31:34 -080029 void MoveTo(PositionSensorSimulator *simulator,
30 PotAndIndexPulseZeroingEstimator *estimator,
Adam Snaiderb4119252015-02-15 01:30:57 +000031 double new_position) {
32 PotAndIndexPosition sensor_values_;
33 simulator->MoveTo(new_position);
34 simulator->GetSensorValues(&sensor_values_);
35 estimator->UpdateEstimate(sensor_values_);
36 }
Adam Snaiderc4b3c192015-02-01 01:30:39 +000037
Brian Silvermanf5f8d8e2015-12-06 18:39:12 -050038 ::aos::testing::TestSharedMemory my_shm_;
Adam Snaiderc4b3c192015-02-01 01:30:39 +000039};
40
Adam Snaiderb4119252015-02-15 01:30:57 +000041TEST_F(ZeroingTest, TestMovingAverageFilter) {
42 const double index_diff = 1.0;
43 PositionSensorSimulator sim(index_diff);
44 sim.Initialize(3.6 * index_diff, index_diff / 3.0);
Tyler Chatowf8f03112017-02-05 14:31:34 -080045 PotAndIndexPulseZeroingEstimator estimator(PotAndIndexPulseZeroingConstants{
Adam Snaider3cd11c52015-02-16 02:16:09 +000046 kSampleSize, index_diff, 0.0, kIndexErrorFraction});
Adam Snaiderc4b3c192015-02-01 01:30:39 +000047
48 // The zeroing code is supposed to perform some filtering on the difference
49 // between the potentiometer value and the encoder value. We assume that 300
50 // samples are sufficient to have updated the filter.
51 for (int i = 0; i < 300; i++) {
Adam Snaiderb4119252015-02-15 01:30:57 +000052 MoveTo(&sim, &estimator, 3.3 * index_diff);
Adam Snaiderc4b3c192015-02-01 01:30:39 +000053 }
Adam Snaiderb4119252015-02-15 01:30:57 +000054 ASSERT_NEAR(3.3 * index_diff, estimator.position(),
55 kAcceptableUnzeroedError * index_diff);
Adam Snaiderc4b3c192015-02-01 01:30:39 +000056
57 for (int i = 0; i < 300; i++) {
Adam Snaiderb4119252015-02-15 01:30:57 +000058 MoveTo(&sim, &estimator, 3.9 * index_diff);
Adam Snaiderc4b3c192015-02-01 01:30:39 +000059 }
Adam Snaiderb4119252015-02-15 01:30:57 +000060 ASSERT_NEAR(3.9 * index_diff, estimator.position(),
61 kAcceptableUnzeroedError * index_diff);
Adam Snaiderc4b3c192015-02-01 01:30:39 +000062}
63
Adam Snaiderb4119252015-02-15 01:30:57 +000064TEST_F(ZeroingTest, NotZeroedBeforeEnoughSamplesCollected) {
65 double index_diff = 0.5;
66 double position = 3.6 * index_diff;
67 PositionSensorSimulator sim(index_diff);
68 sim.Initialize(position, index_diff / 3.0);
Tyler Chatowf8f03112017-02-05 14:31:34 -080069 PotAndIndexPulseZeroingEstimator estimator(PotAndIndexPulseZeroingConstants{
Adam Snaider3cd11c52015-02-16 02:16:09 +000070 kSampleSize, index_diff, 0.0, kIndexErrorFraction});
Adam Snaiderb4119252015-02-15 01:30:57 +000071
72 // Make sure that the zeroing code does not consider itself zeroed until we
73 // collect a good amount of samples. In this case we're waiting until the
74 // moving average filter is full.
75 for (unsigned int i = 0; i < kSampleSize - 1; i++) {
76 MoveTo(&sim, &estimator, position += index_diff);
77 ASSERT_FALSE(estimator.zeroed());
78 }
79
80 MoveTo(&sim, &estimator, position);
81 ASSERT_TRUE(estimator.zeroed());
82}
83
84TEST_F(ZeroingTest, TestLotsOfMovement) {
85 double index_diff = 1.0;
86 PositionSensorSimulator sim(index_diff);
87 sim.Initialize(3.6, index_diff / 3.0);
Tyler Chatowf8f03112017-02-05 14:31:34 -080088 PotAndIndexPulseZeroingEstimator estimator(PotAndIndexPulseZeroingConstants{
Adam Snaider3cd11c52015-02-16 02:16:09 +000089 kSampleSize, index_diff, 0.0, kIndexErrorFraction});
Adam Snaiderc4b3c192015-02-01 01:30:39 +000090
91 // The zeroing code is supposed to perform some filtering on the difference
92 // between the potentiometer value and the encoder value. We assume that 300
93 // samples are sufficient to have updated the filter.
94 for (int i = 0; i < 300; i++) {
Adam Snaiderb4119252015-02-15 01:30:57 +000095 MoveTo(&sim, &estimator, 3.6);
Adam Snaiderc4b3c192015-02-01 01:30:39 +000096 }
Adam Snaiderb4119252015-02-15 01:30:57 +000097 ASSERT_NEAR(3.6, estimator.position(), kAcceptableUnzeroedError * index_diff);
Adam Snaiderc4b3c192015-02-01 01:30:39 +000098
99 // With a single index pulse the zeroing estimator should be able to lock
100 // onto the true value of the position.
Adam Snaiderb4119252015-02-15 01:30:57 +0000101 MoveTo(&sim, &estimator, 4.01);
102 ASSERT_NEAR(4.01, estimator.position(), 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000103
Adam Snaiderb4119252015-02-15 01:30:57 +0000104 MoveTo(&sim, &estimator, 4.99);
105 ASSERT_NEAR(4.99, estimator.position(), 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000106
Adam Snaiderb4119252015-02-15 01:30:57 +0000107 MoveTo(&sim, &estimator, 3.99);
108 ASSERT_NEAR(3.99, estimator.position(), 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000109
Adam Snaiderb4119252015-02-15 01:30:57 +0000110 MoveTo(&sim, &estimator, 3.01);
111 ASSERT_NEAR(3.01, estimator.position(), 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000112
Adam Snaiderb4119252015-02-15 01:30:57 +0000113 MoveTo(&sim, &estimator, 13.55);
114 ASSERT_NEAR(13.55, estimator.position(), 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000115}
116
Adam Snaiderb4119252015-02-15 01:30:57 +0000117TEST_F(ZeroingTest, TestDifferentIndexDiffs) {
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000118 double index_diff = 0.89;
Adam Snaiderb4119252015-02-15 01:30:57 +0000119 PositionSensorSimulator sim(index_diff);
120 sim.Initialize(3.5 * index_diff, index_diff / 3.0);
Tyler Chatowf8f03112017-02-05 14:31:34 -0800121 PotAndIndexPulseZeroingEstimator estimator(PotAndIndexPulseZeroingConstants{
Adam Snaider3cd11c52015-02-16 02:16:09 +0000122 kSampleSize, index_diff, 0.0, kIndexErrorFraction});
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000123
124 // The zeroing code is supposed to perform some filtering on the difference
125 // between the potentiometer value and the encoder value. We assume that 300
126 // samples are sufficient to have updated the filter.
127 for (int i = 0; i < 300; i++) {
Adam Snaiderb4119252015-02-15 01:30:57 +0000128 MoveTo(&sim, &estimator, 3.5 * index_diff);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000129 }
Adam Snaiderb4119252015-02-15 01:30:57 +0000130 ASSERT_NEAR(3.5 * index_diff, estimator.position(),
131 kAcceptableUnzeroedError * index_diff);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000132
133 // With a single index pulse the zeroing estimator should be able to lock
134 // onto the true value of the position.
Adam Snaiderb4119252015-02-15 01:30:57 +0000135 MoveTo(&sim, &estimator, 4.01);
136 ASSERT_NEAR(4.01, estimator.position(), 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000137
Adam Snaiderb4119252015-02-15 01:30:57 +0000138 MoveTo(&sim, &estimator, 4.99);
139 ASSERT_NEAR(4.99, estimator.position(), 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000140
Adam Snaiderb4119252015-02-15 01:30:57 +0000141 MoveTo(&sim, &estimator, 3.99);
142 ASSERT_NEAR(3.99, estimator.position(), 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000143
Adam Snaiderb4119252015-02-15 01:30:57 +0000144 MoveTo(&sim, &estimator, 3.01);
145 ASSERT_NEAR(3.01, estimator.position(), 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000146
Adam Snaiderb4119252015-02-15 01:30:57 +0000147 MoveTo(&sim, &estimator, 13.55);
148 ASSERT_NEAR(13.55, estimator.position(), 0.001);
149}
150
151TEST_F(ZeroingTest, TestPercentage) {
152 double index_diff = 0.89;
153 PositionSensorSimulator sim(index_diff);
154 sim.Initialize(3.5 * index_diff, index_diff / 3.0);
Tyler Chatowf8f03112017-02-05 14:31:34 -0800155 PotAndIndexPulseZeroingEstimator estimator(PotAndIndexPulseZeroingConstants{
Adam Snaider3cd11c52015-02-16 02:16:09 +0000156 kSampleSize, index_diff, 0.0, kIndexErrorFraction});
Adam Snaiderb4119252015-02-15 01:30:57 +0000157
158 for (unsigned int i = 0; i < kSampleSize / 2; i++) {
159 MoveTo(&sim, &estimator, 3.5 * index_diff);
160 }
161 ASSERT_NEAR(0.5, estimator.offset_ratio_ready(), 0.001);
Austin Schuh7485dbb2016-02-08 00:21:58 -0800162 ASSERT_FALSE(estimator.offset_ready());
163
164 for (unsigned int i = 0; i < kSampleSize / 2; i++) {
165 MoveTo(&sim, &estimator, 3.5 * index_diff);
166 }
167 ASSERT_NEAR(1.0, estimator.offset_ratio_ready(), 0.001);
168 ASSERT_TRUE(estimator.offset_ready());
Adam Snaiderb4119252015-02-15 01:30:57 +0000169}
170
171TEST_F(ZeroingTest, TestOffset) {
172 double index_diff = 0.89;
173 PositionSensorSimulator sim(index_diff);
174 sim.Initialize(3.1 * index_diff, index_diff / 3.0);
Tyler Chatowf8f03112017-02-05 14:31:34 -0800175 PotAndIndexPulseZeroingEstimator estimator(PotAndIndexPulseZeroingConstants{
Adam Snaider3cd11c52015-02-16 02:16:09 +0000176 kSampleSize, index_diff, 0.0, kIndexErrorFraction});
Adam Snaiderb4119252015-02-15 01:30:57 +0000177
Philipp Schrader41d82912015-02-15 03:44:23 +0000178 MoveTo(&sim, &estimator, 3.1 * index_diff);
179
Adam Snaiderb4119252015-02-15 01:30:57 +0000180 for (unsigned int i = 0; i < kSampleSize; i++) {
181 MoveTo(&sim, &estimator, 5.0 * index_diff);
182 }
Philipp Schrader41d82912015-02-15 03:44:23 +0000183
Adam Snaiderb4119252015-02-15 01:30:57 +0000184 ASSERT_NEAR(3.1 * index_diff, estimator.offset(), 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000185}
186
Philipp Schrader41d82912015-02-15 03:44:23 +0000187TEST_F(ZeroingTest, WaitForIndexPulseAfterReset) {
188 double index_diff = 0.6;
189 PositionSensorSimulator sim(index_diff);
190 sim.Initialize(3.1 * index_diff, index_diff / 3.0);
Tyler Chatowf8f03112017-02-05 14:31:34 -0800191 PotAndIndexPulseZeroingEstimator estimator(PotAndIndexPulseZeroingConstants{
Adam Snaider3cd11c52015-02-16 02:16:09 +0000192 kSampleSize, index_diff, 0.0, kIndexErrorFraction});
Philipp Schrader41d82912015-02-15 03:44:23 +0000193
194 // Make sure to fill up the averaging filter with samples.
195 for (unsigned int i = 0; i < kSampleSize; i++) {
196 MoveTo(&sim, &estimator, 3.1 * index_diff);
197 }
198
199 // Make sure we're not zeroed until we hit an index pulse.
200 ASSERT_FALSE(estimator.zeroed());
201
202 // Trigger an index pulse; we should now be zeroed.
203 MoveTo(&sim, &estimator, 4.5 * index_diff);
204 ASSERT_TRUE(estimator.zeroed());
205
206 // Reset the zeroing logic and supply a bunch of samples within the current
207 // index segment.
208 estimator.Reset();
209 for (unsigned int i = 0; i < kSampleSize; i++) {
210 MoveTo(&sim, &estimator, 4.2 * index_diff);
211 }
212
213 // Make sure we're not zeroed until we hit an index pulse.
214 ASSERT_FALSE(estimator.zeroed());
215
216 // Trigger another index pulse; we should be zeroed again.
217 MoveTo(&sim, &estimator, 3.1 * index_diff);
218 ASSERT_TRUE(estimator.zeroed());
219}
220
Philipp Schrader030ad182015-02-15 05:40:58 +0000221TEST_F(ZeroingTest, TestNonZeroIndexPulseOffsets) {
222 const double index_diff = 0.9;
223 const double known_index_pos = 3.5 * index_diff;
224 PositionSensorSimulator sim(index_diff);
225 sim.Initialize(3.3 * index_diff, index_diff / 3.0, known_index_pos);
Tyler Chatowf8f03112017-02-05 14:31:34 -0800226 PotAndIndexPulseZeroingEstimator estimator(PotAndIndexPulseZeroingConstants{
Adam Snaider3cd11c52015-02-16 02:16:09 +0000227 kSampleSize, index_diff, known_index_pos, kIndexErrorFraction});
Philipp Schrader030ad182015-02-15 05:40:58 +0000228
229 // Make sure to fill up the averaging filter with samples.
230 for (unsigned int i = 0; i < kSampleSize; i++) {
231 MoveTo(&sim, &estimator, 3.3 * index_diff);
232 }
233
234 // Make sure we're not zeroed until we hit an index pulse.
235 ASSERT_FALSE(estimator.zeroed());
236
237 // Trigger an index pulse; we should now be zeroed.
238 MoveTo(&sim, &estimator, 3.7 * index_diff);
239 ASSERT_TRUE(estimator.zeroed());
240 ASSERT_DOUBLE_EQ(3.3 * index_diff, estimator.offset());
241 ASSERT_DOUBLE_EQ(3.7 * index_diff, estimator.position());
242
243 // Trigger one more index pulse and check the offset.
244 MoveTo(&sim, &estimator, 4.7 * index_diff);
245 ASSERT_DOUBLE_EQ(3.3 * index_diff, estimator.offset());
246 ASSERT_DOUBLE_EQ(4.7 * index_diff, estimator.position());
247}
248
Philipp Schrader53f4b6d2015-02-15 22:32:08 +0000249TEST_F(ZeroingTest, BasicErrorAPITest) {
250 const double index_diff = 1.0;
Tyler Chatowf8f03112017-02-05 14:31:34 -0800251 PotAndIndexPulseZeroingEstimator estimator(PotAndIndexPulseZeroingConstants{
Adam Snaider3cd11c52015-02-16 02:16:09 +0000252 kSampleSize, index_diff, 0.0, kIndexErrorFraction});
Philipp Schrader53f4b6d2015-02-15 22:32:08 +0000253 PositionSensorSimulator sim(index_diff);
254 sim.Initialize(1.5 * index_diff, index_diff / 3.0, 0.0);
255
256 // Perform a simple move and make sure that no error occured.
257 MoveTo(&sim, &estimator, 3.5 * index_diff);
258 ASSERT_FALSE(estimator.error());
259
260 // Trigger an error and make sure it's reported.
261 estimator.TriggerError();
262 ASSERT_TRUE(estimator.error());
263
264 // Make sure that it can recover after a reset.
265 estimator.Reset();
266 ASSERT_FALSE(estimator.error());
267 MoveTo(&sim, &estimator, 4.5 * index_diff);
268 MoveTo(&sim, &estimator, 5.5 * index_diff);
269 ASSERT_FALSE(estimator.error());
270}
271
Adam Snaider3cd11c52015-02-16 02:16:09 +0000272// I want to test that the the zeroing class can
273// detect an error when the starting position
274// changes too much. I do so by creating the
275// simulator at an 'X' positon, making sure
276// that the estimator is zeroed, and then
277// initializing the simulator at another
278// position. After making sure it's zeroed,
279// if the error() function returns true,
280// then, it works.
281TEST_F(ZeroingTest, TestOffsetError) {
282 const double index_diff = 0.8;
283 const double known_index_pos = 2 * index_diff;
284 int sample_size = 30;
285 PositionSensorSimulator sim(index_diff);
286 sim.Initialize(10 * index_diff, index_diff / 3.0, known_index_pos);
Tyler Chatowf8f03112017-02-05 14:31:34 -0800287 PotAndIndexPulseZeroingEstimator estimator(PotAndIndexPulseZeroingConstants{
Adam Snaider3cd11c52015-02-16 02:16:09 +0000288 sample_size, index_diff, known_index_pos, kIndexErrorFraction});
289
290 for (int i = 0; i < sample_size; i++) {
291 MoveTo(&sim, &estimator, 13 * index_diff);
292 }
293 MoveTo(&sim, &estimator, 8 * index_diff);
294
295 ASSERT_TRUE(estimator.zeroed());
296 ASSERT_FALSE(estimator.error());
297 sim.Initialize(9.0 * index_diff + 0.31 * index_diff, index_diff / 3.0,
298 known_index_pos);
299 MoveTo(&sim, &estimator, 9 * index_diff);
300 ASSERT_TRUE(estimator.zeroed());
301 ASSERT_TRUE(estimator.error());
302}
303
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000304} // namespace zeroing
305} // namespace frc971