blob: 1134259996b66aef4aabf1c8af57dc8591391f68 [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;
Brian Silvermanb691f5e2015-08-02 11:37:55 -070019using constants::ZeroingConstants;
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
Adam Snaiderb4119252015-02-15 01:30:57 +000029 void MoveTo(PositionSensorSimulator* simulator, ZeroingEstimator* estimator,
30 double new_position) {
31 PotAndIndexPosition sensor_values_;
32 simulator->MoveTo(new_position);
33 simulator->GetSensorValues(&sensor_values_);
34 estimator->UpdateEstimate(sensor_values_);
35 }
Adam Snaiderc4b3c192015-02-01 01:30:39 +000036
Brian Silvermanf5f8d8e2015-12-06 18:39:12 -050037 ::aos::testing::TestSharedMemory my_shm_;
Adam Snaiderc4b3c192015-02-01 01:30:39 +000038};
39
Adam Snaiderb4119252015-02-15 01:30:57 +000040TEST_F(ZeroingTest, TestMovingAverageFilter) {
41 const double index_diff = 1.0;
42 PositionSensorSimulator sim(index_diff);
43 sim.Initialize(3.6 * index_diff, index_diff / 3.0);
Brian Silvermanb691f5e2015-08-02 11:37:55 -070044 ZeroingEstimator estimator(ZeroingConstants{
Adam Snaider3cd11c52015-02-16 02:16:09 +000045 kSampleSize, index_diff, 0.0, kIndexErrorFraction});
Adam Snaiderc4b3c192015-02-01 01:30:39 +000046
47 // The zeroing code is supposed to perform some filtering on the difference
48 // between the potentiometer value and the encoder value. We assume that 300
49 // samples are sufficient to have updated the filter.
50 for (int i = 0; i < 300; i++) {
Adam Snaiderb4119252015-02-15 01:30:57 +000051 MoveTo(&sim, &estimator, 3.3 * index_diff);
Adam Snaiderc4b3c192015-02-01 01:30:39 +000052 }
Adam Snaiderb4119252015-02-15 01:30:57 +000053 ASSERT_NEAR(3.3 * index_diff, estimator.position(),
54 kAcceptableUnzeroedError * index_diff);
Adam Snaiderc4b3c192015-02-01 01:30:39 +000055
56 for (int i = 0; i < 300; i++) {
Adam Snaiderb4119252015-02-15 01:30:57 +000057 MoveTo(&sim, &estimator, 3.9 * index_diff);
Adam Snaiderc4b3c192015-02-01 01:30:39 +000058 }
Adam Snaiderb4119252015-02-15 01:30:57 +000059 ASSERT_NEAR(3.9 * index_diff, estimator.position(),
60 kAcceptableUnzeroedError * index_diff);
Adam Snaiderc4b3c192015-02-01 01:30:39 +000061}
62
Adam Snaiderb4119252015-02-15 01:30:57 +000063TEST_F(ZeroingTest, NotZeroedBeforeEnoughSamplesCollected) {
64 double index_diff = 0.5;
65 double position = 3.6 * index_diff;
66 PositionSensorSimulator sim(index_diff);
67 sim.Initialize(position, index_diff / 3.0);
Brian Silvermanb691f5e2015-08-02 11:37:55 -070068 ZeroingEstimator estimator(ZeroingConstants{
Adam Snaider3cd11c52015-02-16 02:16:09 +000069 kSampleSize, index_diff, 0.0, kIndexErrorFraction});
Adam Snaiderb4119252015-02-15 01:30:57 +000070
71 // Make sure that the zeroing code does not consider itself zeroed until we
72 // collect a good amount of samples. In this case we're waiting until the
73 // moving average filter is full.
74 for (unsigned int i = 0; i < kSampleSize - 1; i++) {
75 MoveTo(&sim, &estimator, position += index_diff);
76 ASSERT_FALSE(estimator.zeroed());
77 }
78
79 MoveTo(&sim, &estimator, position);
80 ASSERT_TRUE(estimator.zeroed());
81}
82
83TEST_F(ZeroingTest, TestLotsOfMovement) {
84 double index_diff = 1.0;
85 PositionSensorSimulator sim(index_diff);
86 sim.Initialize(3.6, index_diff / 3.0);
Brian Silvermanb691f5e2015-08-02 11:37:55 -070087 ZeroingEstimator estimator(ZeroingConstants{
Adam Snaider3cd11c52015-02-16 02:16:09 +000088 kSampleSize, index_diff, 0.0, kIndexErrorFraction});
Adam Snaiderc4b3c192015-02-01 01:30:39 +000089
90 // The zeroing code is supposed to perform some filtering on the difference
91 // between the potentiometer value and the encoder value. We assume that 300
92 // samples are sufficient to have updated the filter.
93 for (int i = 0; i < 300; i++) {
Adam Snaiderb4119252015-02-15 01:30:57 +000094 MoveTo(&sim, &estimator, 3.6);
Adam Snaiderc4b3c192015-02-01 01:30:39 +000095 }
Adam Snaiderb4119252015-02-15 01:30:57 +000096 ASSERT_NEAR(3.6, estimator.position(), kAcceptableUnzeroedError * index_diff);
Adam Snaiderc4b3c192015-02-01 01:30:39 +000097
98 // With a single index pulse the zeroing estimator should be able to lock
99 // onto the true value of the position.
Adam Snaiderb4119252015-02-15 01:30:57 +0000100 MoveTo(&sim, &estimator, 4.01);
101 ASSERT_NEAR(4.01, estimator.position(), 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000102
Adam Snaiderb4119252015-02-15 01:30:57 +0000103 MoveTo(&sim, &estimator, 4.99);
104 ASSERT_NEAR(4.99, estimator.position(), 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000105
Adam Snaiderb4119252015-02-15 01:30:57 +0000106 MoveTo(&sim, &estimator, 3.99);
107 ASSERT_NEAR(3.99, estimator.position(), 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000108
Adam Snaiderb4119252015-02-15 01:30:57 +0000109 MoveTo(&sim, &estimator, 3.01);
110 ASSERT_NEAR(3.01, estimator.position(), 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000111
Adam Snaiderb4119252015-02-15 01:30:57 +0000112 MoveTo(&sim, &estimator, 13.55);
113 ASSERT_NEAR(13.55, estimator.position(), 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000114}
115
Adam Snaiderb4119252015-02-15 01:30:57 +0000116TEST_F(ZeroingTest, TestDifferentIndexDiffs) {
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000117 double index_diff = 0.89;
Adam Snaiderb4119252015-02-15 01:30:57 +0000118 PositionSensorSimulator sim(index_diff);
119 sim.Initialize(3.5 * index_diff, index_diff / 3.0);
Brian Silvermanb691f5e2015-08-02 11:37:55 -0700120 ZeroingEstimator estimator(ZeroingConstants{
Adam Snaider3cd11c52015-02-16 02:16:09 +0000121 kSampleSize, index_diff, 0.0, kIndexErrorFraction});
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000122
123 // The zeroing code is supposed to perform some filtering on the difference
124 // between the potentiometer value and the encoder value. We assume that 300
125 // samples are sufficient to have updated the filter.
126 for (int i = 0; i < 300; i++) {
Adam Snaiderb4119252015-02-15 01:30:57 +0000127 MoveTo(&sim, &estimator, 3.5 * index_diff);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000128 }
Adam Snaiderb4119252015-02-15 01:30:57 +0000129 ASSERT_NEAR(3.5 * index_diff, estimator.position(),
130 kAcceptableUnzeroedError * index_diff);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000131
132 // With a single index pulse the zeroing estimator should be able to lock
133 // onto the true value of the position.
Adam Snaiderb4119252015-02-15 01:30:57 +0000134 MoveTo(&sim, &estimator, 4.01);
135 ASSERT_NEAR(4.01, estimator.position(), 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000136
Adam Snaiderb4119252015-02-15 01:30:57 +0000137 MoveTo(&sim, &estimator, 4.99);
138 ASSERT_NEAR(4.99, estimator.position(), 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000139
Adam Snaiderb4119252015-02-15 01:30:57 +0000140 MoveTo(&sim, &estimator, 3.99);
141 ASSERT_NEAR(3.99, estimator.position(), 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000142
Adam Snaiderb4119252015-02-15 01:30:57 +0000143 MoveTo(&sim, &estimator, 3.01);
144 ASSERT_NEAR(3.01, estimator.position(), 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000145
Adam Snaiderb4119252015-02-15 01:30:57 +0000146 MoveTo(&sim, &estimator, 13.55);
147 ASSERT_NEAR(13.55, estimator.position(), 0.001);
148}
149
150TEST_F(ZeroingTest, TestPercentage) {
151 double index_diff = 0.89;
152 PositionSensorSimulator sim(index_diff);
153 sim.Initialize(3.5 * index_diff, index_diff / 3.0);
Brian Silvermanb691f5e2015-08-02 11:37:55 -0700154 ZeroingEstimator estimator(ZeroingConstants{
Adam Snaider3cd11c52015-02-16 02:16:09 +0000155 kSampleSize, index_diff, 0.0, kIndexErrorFraction});
Adam Snaiderb4119252015-02-15 01:30:57 +0000156
157 for (unsigned int i = 0; i < kSampleSize / 2; i++) {
158 MoveTo(&sim, &estimator, 3.5 * index_diff);
159 }
160 ASSERT_NEAR(0.5, estimator.offset_ratio_ready(), 0.001);
Austin Schuh7485dbb2016-02-08 00:21:58 -0800161 ASSERT_FALSE(estimator.offset_ready());
162
163 for (unsigned int i = 0; i < kSampleSize / 2; i++) {
164 MoveTo(&sim, &estimator, 3.5 * index_diff);
165 }
166 ASSERT_NEAR(1.0, estimator.offset_ratio_ready(), 0.001);
167 ASSERT_TRUE(estimator.offset_ready());
Adam Snaiderb4119252015-02-15 01:30:57 +0000168}
169
170TEST_F(ZeroingTest, TestOffset) {
171 double index_diff = 0.89;
172 PositionSensorSimulator sim(index_diff);
173 sim.Initialize(3.1 * index_diff, index_diff / 3.0);
Brian Silvermanb691f5e2015-08-02 11:37:55 -0700174 ZeroingEstimator estimator(ZeroingConstants{
Adam Snaider3cd11c52015-02-16 02:16:09 +0000175 kSampleSize, index_diff, 0.0, kIndexErrorFraction});
Adam Snaiderb4119252015-02-15 01:30:57 +0000176
Philipp Schrader41d82912015-02-15 03:44:23 +0000177 MoveTo(&sim, &estimator, 3.1 * index_diff);
178
Adam Snaiderb4119252015-02-15 01:30:57 +0000179 for (unsigned int i = 0; i < kSampleSize; i++) {
180 MoveTo(&sim, &estimator, 5.0 * index_diff);
181 }
Philipp Schrader41d82912015-02-15 03:44:23 +0000182
Adam Snaiderb4119252015-02-15 01:30:57 +0000183 ASSERT_NEAR(3.1 * index_diff, estimator.offset(), 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000184}
185
Philipp Schrader41d82912015-02-15 03:44:23 +0000186TEST_F(ZeroingTest, WaitForIndexPulseAfterReset) {
187 double index_diff = 0.6;
188 PositionSensorSimulator sim(index_diff);
189 sim.Initialize(3.1 * index_diff, index_diff / 3.0);
Brian Silvermanb691f5e2015-08-02 11:37:55 -0700190 ZeroingEstimator estimator(ZeroingConstants{
Adam Snaider3cd11c52015-02-16 02:16:09 +0000191 kSampleSize, index_diff, 0.0, kIndexErrorFraction});
Philipp Schrader41d82912015-02-15 03:44:23 +0000192
193 // Make sure to fill up the averaging filter with samples.
194 for (unsigned int i = 0; i < kSampleSize; i++) {
195 MoveTo(&sim, &estimator, 3.1 * index_diff);
196 }
197
198 // Make sure we're not zeroed until we hit an index pulse.
199 ASSERT_FALSE(estimator.zeroed());
200
201 // Trigger an index pulse; we should now be zeroed.
202 MoveTo(&sim, &estimator, 4.5 * index_diff);
203 ASSERT_TRUE(estimator.zeroed());
204
205 // Reset the zeroing logic and supply a bunch of samples within the current
206 // index segment.
207 estimator.Reset();
208 for (unsigned int i = 0; i < kSampleSize; i++) {
209 MoveTo(&sim, &estimator, 4.2 * index_diff);
210 }
211
212 // Make sure we're not zeroed until we hit an index pulse.
213 ASSERT_FALSE(estimator.zeroed());
214
215 // Trigger another index pulse; we should be zeroed again.
216 MoveTo(&sim, &estimator, 3.1 * index_diff);
217 ASSERT_TRUE(estimator.zeroed());
218}
219
Philipp Schrader030ad182015-02-15 05:40:58 +0000220TEST_F(ZeroingTest, TestNonZeroIndexPulseOffsets) {
221 const double index_diff = 0.9;
222 const double known_index_pos = 3.5 * index_diff;
223 PositionSensorSimulator sim(index_diff);
224 sim.Initialize(3.3 * index_diff, index_diff / 3.0, known_index_pos);
Brian Silvermanb691f5e2015-08-02 11:37:55 -0700225 ZeroingEstimator estimator(ZeroingConstants{
Adam Snaider3cd11c52015-02-16 02:16:09 +0000226 kSampleSize, index_diff, known_index_pos, kIndexErrorFraction});
Philipp Schrader030ad182015-02-15 05:40:58 +0000227
228 // Make sure to fill up the averaging filter with samples.
229 for (unsigned int i = 0; i < kSampleSize; i++) {
230 MoveTo(&sim, &estimator, 3.3 * index_diff);
231 }
232
233 // Make sure we're not zeroed until we hit an index pulse.
234 ASSERT_FALSE(estimator.zeroed());
235
236 // Trigger an index pulse; we should now be zeroed.
237 MoveTo(&sim, &estimator, 3.7 * index_diff);
238 ASSERT_TRUE(estimator.zeroed());
239 ASSERT_DOUBLE_EQ(3.3 * index_diff, estimator.offset());
240 ASSERT_DOUBLE_EQ(3.7 * index_diff, estimator.position());
241
242 // Trigger one more index pulse and check the offset.
243 MoveTo(&sim, &estimator, 4.7 * index_diff);
244 ASSERT_DOUBLE_EQ(3.3 * index_diff, estimator.offset());
245 ASSERT_DOUBLE_EQ(4.7 * index_diff, estimator.position());
246}
247
Philipp Schrader53f4b6d2015-02-15 22:32:08 +0000248TEST_F(ZeroingTest, BasicErrorAPITest) {
249 const double index_diff = 1.0;
Brian Silvermanb691f5e2015-08-02 11:37:55 -0700250 ZeroingEstimator estimator(ZeroingConstants{
Adam Snaider3cd11c52015-02-16 02:16:09 +0000251 kSampleSize, index_diff, 0.0, kIndexErrorFraction});
Philipp Schrader53f4b6d2015-02-15 22:32:08 +0000252 PositionSensorSimulator sim(index_diff);
253 sim.Initialize(1.5 * index_diff, index_diff / 3.0, 0.0);
254
255 // Perform a simple move and make sure that no error occured.
256 MoveTo(&sim, &estimator, 3.5 * index_diff);
257 ASSERT_FALSE(estimator.error());
258
259 // Trigger an error and make sure it's reported.
260 estimator.TriggerError();
261 ASSERT_TRUE(estimator.error());
262
263 // Make sure that it can recover after a reset.
264 estimator.Reset();
265 ASSERT_FALSE(estimator.error());
266 MoveTo(&sim, &estimator, 4.5 * index_diff);
267 MoveTo(&sim, &estimator, 5.5 * index_diff);
268 ASSERT_FALSE(estimator.error());
269}
270
Adam Snaider3cd11c52015-02-16 02:16:09 +0000271// I want to test that the the zeroing class can
272// detect an error when the starting position
273// changes too much. I do so by creating the
274// simulator at an 'X' positon, making sure
275// that the estimator is zeroed, and then
276// initializing the simulator at another
277// position. After making sure it's zeroed,
278// if the error() function returns true,
279// then, it works.
280TEST_F(ZeroingTest, TestOffsetError) {
281 const double index_diff = 0.8;
282 const double known_index_pos = 2 * index_diff;
283 int sample_size = 30;
284 PositionSensorSimulator sim(index_diff);
285 sim.Initialize(10 * index_diff, index_diff / 3.0, known_index_pos);
Brian Silvermanb691f5e2015-08-02 11:37:55 -0700286 ZeroingEstimator estimator(ZeroingConstants{
Adam Snaider3cd11c52015-02-16 02:16:09 +0000287 sample_size, index_diff, known_index_pos, kIndexErrorFraction});
288
289 for (int i = 0; i < sample_size; i++) {
290 MoveTo(&sim, &estimator, 13 * index_diff);
291 }
292 MoveTo(&sim, &estimator, 8 * index_diff);
293
294 ASSERT_TRUE(estimator.zeroed());
295 ASSERT_FALSE(estimator.error());
296 sim.Initialize(9.0 * index_diff + 0.31 * index_diff, index_diff / 3.0,
297 known_index_pos);
298 MoveTo(&sim, &estimator, 9 * index_diff);
299 ASSERT_TRUE(estimator.zeroed());
300 ASSERT_TRUE(estimator.error());
301}
302
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000303} // namespace zeroing
304} // namespace frc971