blob: cca05852c9d5c975c39e071e4b2884ceacc5e387 [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"
John Park33858a32018-09-28 23:05:48 -070011#include "aos/util/thread.h"
12#include "aos/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;
Austin Schuhd82068e2019-01-26 20:05:42 -080019using constants::AbsoluteEncoderZeroingConstants;
Isaac Wilcove0851ffd2017-02-16 04:13:14 +000020using constants::EncoderPlusIndexZeroingConstants;
Austin Schuhd82068e2019-01-26 20:05:42 -080021using constants::PotAndAbsoluteEncoderZeroingConstants;
22using constants::PotAndIndexPulseZeroingConstants;
Adam Snaiderc4b3c192015-02-01 01:30:39 +000023
Adam Snaiderb4119252015-02-15 01:30:57 +000024static const size_t kSampleSize = 30;
25static const double kAcceptableUnzeroedError = 0.2;
Adam Snaider3cd11c52015-02-16 02:16:09 +000026static const double kIndexErrorFraction = 0.3;
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -080027static const size_t kMovingBufferSize = 3;
Adam Snaiderc4b3c192015-02-01 01:30:39 +000028
Adam Snaiderb4119252015-02-15 01:30:57 +000029class ZeroingTest : public ::testing::Test {
Adam Snaiderc4b3c192015-02-01 01:30:39 +000030 protected:
31 void SetUp() override { aos::SetDieTestMode(true); }
32
Tyler Chatowf8f03112017-02-05 14:31:34 -080033 void MoveTo(PositionSensorSimulator *simulator,
34 PotAndIndexPulseZeroingEstimator *estimator,
Adam Snaiderb4119252015-02-15 01:30:57 +000035 double new_position) {
Brian Silvermandc4eb102017-02-05 17:34:41 -080036 PotAndIndexPosition sensor_values;
Adam Snaiderb4119252015-02-15 01:30:57 +000037 simulator->MoveTo(new_position);
Brian Silvermandc4eb102017-02-05 17:34:41 -080038 simulator->GetSensorValues(&sensor_values);
39 estimator->UpdateEstimate(sensor_values);
Adam Snaiderb4119252015-02-15 01:30:57 +000040 }
Adam Snaiderc4b3c192015-02-01 01:30:39 +000041
Austin Schuh5f01f152017-02-11 21:34:08 -080042 void MoveTo(PositionSensorSimulator *simulator,
Austin Schuhd82068e2019-01-26 20:05:42 -080043 AbsoluteEncoderZeroingEstimator *estimator,
44 double new_position) {
45 AbsolutePosition sensor_values_;
46 simulator->MoveTo(new_position);
47 simulator->GetSensorValues(&sensor_values_);
48 estimator->UpdateEstimate(sensor_values_);
49 }
50
51 void MoveTo(PositionSensorSimulator *simulator,
Austin Schuh72db9a12019-01-21 18:02:51 -080052 PotAndAbsoluteEncoderZeroingEstimator *estimator,
Neil Balch16275e32017-02-18 16:38:45 -080053 double new_position) {
Austin Schuh5f01f152017-02-11 21:34:08 -080054 PotAndAbsolutePosition sensor_values_;
55 simulator->MoveTo(new_position);
56 simulator->GetSensorValues(&sensor_values_);
57 estimator->UpdateEstimate(sensor_values_);
58 }
Isaac Wilcove0851ffd2017-02-16 04:13:14 +000059
60 void MoveTo(PositionSensorSimulator *simulator,
61 PulseIndexZeroingEstimator *estimator, double new_position) {
62 IndexPosition sensor_values_;
63 simulator->MoveTo(new_position);
64 simulator->GetSensorValues(&sensor_values_);
65 estimator->UpdateEstimate(sensor_values_);
66 }
Austin Schuh5f01f152017-02-11 21:34:08 -080067
Austin Schuh55934032017-03-11 12:45:27 -080068 void MoveTo(PositionSensorSimulator *simulator,
69 HallEffectAndPositionZeroingEstimator *estimator,
70 double new_position) {
71 HallEffectAndPosition sensor_values_;
72 simulator->MoveTo(new_position);
73 simulator->GetSensorValues(&sensor_values_);
74 estimator->UpdateEstimate(sensor_values_);
75 }
76
Brian Silvermanf5f8d8e2015-12-06 18:39:12 -050077 ::aos::testing::TestSharedMemory my_shm_;
Adam Snaiderc4b3c192015-02-01 01:30:39 +000078};
79
Adam Snaiderb4119252015-02-15 01:30:57 +000080TEST_F(ZeroingTest, TestMovingAverageFilter) {
81 const double index_diff = 1.0;
82 PositionSensorSimulator sim(index_diff);
83 sim.Initialize(3.6 * index_diff, index_diff / 3.0);
Tyler Chatowf8f03112017-02-05 14:31:34 -080084 PotAndIndexPulseZeroingEstimator estimator(PotAndIndexPulseZeroingConstants{
Adam Snaider3cd11c52015-02-16 02:16:09 +000085 kSampleSize, index_diff, 0.0, kIndexErrorFraction});
Adam Snaiderc4b3c192015-02-01 01:30:39 +000086
87 // The zeroing code is supposed to perform some filtering on the difference
88 // between the potentiometer value and the encoder value. We assume that 300
89 // samples are sufficient to have updated the filter.
90 for (int i = 0; i < 300; i++) {
Adam Snaiderb4119252015-02-15 01:30:57 +000091 MoveTo(&sim, &estimator, 3.3 * index_diff);
Adam Snaiderc4b3c192015-02-01 01:30:39 +000092 }
Brian Silvermanf37839c2017-02-19 18:07:15 -080093 ASSERT_NEAR(3.3 * index_diff, estimator.GetEstimatorState().position,
Adam Snaiderb4119252015-02-15 01:30:57 +000094 kAcceptableUnzeroedError * index_diff);
Adam Snaiderc4b3c192015-02-01 01:30:39 +000095
96 for (int i = 0; i < 300; i++) {
Adam Snaiderb4119252015-02-15 01:30:57 +000097 MoveTo(&sim, &estimator, 3.9 * index_diff);
Adam Snaiderc4b3c192015-02-01 01:30:39 +000098 }
Brian Silvermanf37839c2017-02-19 18:07:15 -080099 ASSERT_NEAR(3.9 * index_diff, estimator.GetEstimatorState().position,
Adam Snaiderb4119252015-02-15 01:30:57 +0000100 kAcceptableUnzeroedError * index_diff);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000101}
102
Adam Snaiderb4119252015-02-15 01:30:57 +0000103TEST_F(ZeroingTest, NotZeroedBeforeEnoughSamplesCollected) {
104 double index_diff = 0.5;
105 double position = 3.6 * index_diff;
106 PositionSensorSimulator sim(index_diff);
107 sim.Initialize(position, index_diff / 3.0);
Tyler Chatowf8f03112017-02-05 14:31:34 -0800108 PotAndIndexPulseZeroingEstimator estimator(PotAndIndexPulseZeroingConstants{
Adam Snaider3cd11c52015-02-16 02:16:09 +0000109 kSampleSize, index_diff, 0.0, kIndexErrorFraction});
Adam Snaiderb4119252015-02-15 01:30:57 +0000110
111 // Make sure that the zeroing code does not consider itself zeroed until we
112 // collect a good amount of samples. In this case we're waiting until the
113 // moving average filter is full.
114 for (unsigned int i = 0; i < kSampleSize - 1; i++) {
115 MoveTo(&sim, &estimator, position += index_diff);
116 ASSERT_FALSE(estimator.zeroed());
117 }
118
119 MoveTo(&sim, &estimator, position);
120 ASSERT_TRUE(estimator.zeroed());
121}
122
123TEST_F(ZeroingTest, TestLotsOfMovement) {
124 double index_diff = 1.0;
125 PositionSensorSimulator sim(index_diff);
126 sim.Initialize(3.6, index_diff / 3.0);
Tyler Chatowf8f03112017-02-05 14:31:34 -0800127 PotAndIndexPulseZeroingEstimator estimator(PotAndIndexPulseZeroingConstants{
Adam Snaider3cd11c52015-02-16 02:16:09 +0000128 kSampleSize, index_diff, 0.0, kIndexErrorFraction});
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000129
130 // The zeroing code is supposed to perform some filtering on the difference
131 // between the potentiometer value and the encoder value. We assume that 300
132 // samples are sufficient to have updated the filter.
133 for (int i = 0; i < 300; i++) {
Adam Snaiderb4119252015-02-15 01:30:57 +0000134 MoveTo(&sim, &estimator, 3.6);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000135 }
Brian Silvermanf37839c2017-02-19 18:07:15 -0800136 ASSERT_NEAR(3.6, estimator.GetEstimatorState().position,
137 kAcceptableUnzeroedError * index_diff);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000138
139 // With a single index pulse the zeroing estimator should be able to lock
140 // onto the true value of the position.
Adam Snaiderb4119252015-02-15 01:30:57 +0000141 MoveTo(&sim, &estimator, 4.01);
Brian Silvermanf37839c2017-02-19 18:07:15 -0800142 ASSERT_NEAR(4.01, estimator.GetEstimatorState().position, 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000143
Adam Snaiderb4119252015-02-15 01:30:57 +0000144 MoveTo(&sim, &estimator, 4.99);
Brian Silvermanf37839c2017-02-19 18:07:15 -0800145 ASSERT_NEAR(4.99, estimator.GetEstimatorState().position, 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000146
Adam Snaiderb4119252015-02-15 01:30:57 +0000147 MoveTo(&sim, &estimator, 3.99);
Brian Silvermanf37839c2017-02-19 18:07:15 -0800148 ASSERT_NEAR(3.99, estimator.GetEstimatorState().position, 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000149
Adam Snaiderb4119252015-02-15 01:30:57 +0000150 MoveTo(&sim, &estimator, 3.01);
Brian Silvermanf37839c2017-02-19 18:07:15 -0800151 ASSERT_NEAR(3.01, estimator.GetEstimatorState().position, 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000152
Adam Snaiderb4119252015-02-15 01:30:57 +0000153 MoveTo(&sim, &estimator, 13.55);
Brian Silvermanf37839c2017-02-19 18:07:15 -0800154 ASSERT_NEAR(13.55, estimator.GetEstimatorState().position, 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000155}
156
Adam Snaiderb4119252015-02-15 01:30:57 +0000157TEST_F(ZeroingTest, TestDifferentIndexDiffs) {
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000158 double index_diff = 0.89;
Adam Snaiderb4119252015-02-15 01:30:57 +0000159 PositionSensorSimulator sim(index_diff);
160 sim.Initialize(3.5 * index_diff, index_diff / 3.0);
Tyler Chatowf8f03112017-02-05 14:31:34 -0800161 PotAndIndexPulseZeroingEstimator estimator(PotAndIndexPulseZeroingConstants{
Adam Snaider3cd11c52015-02-16 02:16:09 +0000162 kSampleSize, index_diff, 0.0, kIndexErrorFraction});
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000163
164 // The zeroing code is supposed to perform some filtering on the difference
165 // between the potentiometer value and the encoder value. We assume that 300
166 // samples are sufficient to have updated the filter.
167 for (int i = 0; i < 300; i++) {
Adam Snaiderb4119252015-02-15 01:30:57 +0000168 MoveTo(&sim, &estimator, 3.5 * index_diff);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000169 }
Brian Silvermanf37839c2017-02-19 18:07:15 -0800170 ASSERT_NEAR(3.5 * index_diff, estimator.GetEstimatorState().position,
Adam Snaiderb4119252015-02-15 01:30:57 +0000171 kAcceptableUnzeroedError * index_diff);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000172
173 // With a single index pulse the zeroing estimator should be able to lock
174 // onto the true value of the position.
Adam Snaiderb4119252015-02-15 01:30:57 +0000175 MoveTo(&sim, &estimator, 4.01);
Brian Silvermanf37839c2017-02-19 18:07:15 -0800176 ASSERT_NEAR(4.01, estimator.GetEstimatorState().position, 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000177
Adam Snaiderb4119252015-02-15 01:30:57 +0000178 MoveTo(&sim, &estimator, 4.99);
Brian Silvermanf37839c2017-02-19 18:07:15 -0800179 ASSERT_NEAR(4.99, estimator.GetEstimatorState().position, 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000180
Adam Snaiderb4119252015-02-15 01:30:57 +0000181 MoveTo(&sim, &estimator, 3.99);
Brian Silvermanf37839c2017-02-19 18:07:15 -0800182 ASSERT_NEAR(3.99, estimator.GetEstimatorState().position, 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000183
Adam Snaiderb4119252015-02-15 01:30:57 +0000184 MoveTo(&sim, &estimator, 3.01);
Brian Silvermanf37839c2017-02-19 18:07:15 -0800185 ASSERT_NEAR(3.01, estimator.GetEstimatorState().position, 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000186
Adam Snaiderb4119252015-02-15 01:30:57 +0000187 MoveTo(&sim, &estimator, 13.55);
Brian Silvermanf37839c2017-02-19 18:07:15 -0800188 ASSERT_NEAR(13.55, estimator.GetEstimatorState().position, 0.001);
Adam Snaiderb4119252015-02-15 01:30:57 +0000189}
190
191TEST_F(ZeroingTest, TestPercentage) {
192 double index_diff = 0.89;
193 PositionSensorSimulator sim(index_diff);
194 sim.Initialize(3.5 * index_diff, index_diff / 3.0);
Tyler Chatowf8f03112017-02-05 14:31:34 -0800195 PotAndIndexPulseZeroingEstimator estimator(PotAndIndexPulseZeroingConstants{
Adam Snaider3cd11c52015-02-16 02:16:09 +0000196 kSampleSize, index_diff, 0.0, kIndexErrorFraction});
Adam Snaiderb4119252015-02-15 01:30:57 +0000197
198 for (unsigned int i = 0; i < kSampleSize / 2; i++) {
199 MoveTo(&sim, &estimator, 3.5 * index_diff);
200 }
201 ASSERT_NEAR(0.5, estimator.offset_ratio_ready(), 0.001);
Austin Schuh7485dbb2016-02-08 00:21:58 -0800202 ASSERT_FALSE(estimator.offset_ready());
203
204 for (unsigned int i = 0; i < kSampleSize / 2; i++) {
205 MoveTo(&sim, &estimator, 3.5 * index_diff);
206 }
207 ASSERT_NEAR(1.0, estimator.offset_ratio_ready(), 0.001);
208 ASSERT_TRUE(estimator.offset_ready());
Adam Snaiderb4119252015-02-15 01:30:57 +0000209}
210
211TEST_F(ZeroingTest, TestOffset) {
212 double index_diff = 0.89;
213 PositionSensorSimulator sim(index_diff);
214 sim.Initialize(3.1 * index_diff, index_diff / 3.0);
Tyler Chatowf8f03112017-02-05 14:31:34 -0800215 PotAndIndexPulseZeroingEstimator estimator(PotAndIndexPulseZeroingConstants{
Adam Snaider3cd11c52015-02-16 02:16:09 +0000216 kSampleSize, index_diff, 0.0, kIndexErrorFraction});
Adam Snaiderb4119252015-02-15 01:30:57 +0000217
Philipp Schrader41d82912015-02-15 03:44:23 +0000218 MoveTo(&sim, &estimator, 3.1 * index_diff);
219
Adam Snaiderb4119252015-02-15 01:30:57 +0000220 for (unsigned int i = 0; i < kSampleSize; i++) {
221 MoveTo(&sim, &estimator, 5.0 * index_diff);
222 }
Philipp Schrader41d82912015-02-15 03:44:23 +0000223
Adam Snaiderb4119252015-02-15 01:30:57 +0000224 ASSERT_NEAR(3.1 * index_diff, estimator.offset(), 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000225}
226
Philipp Schrader41d82912015-02-15 03:44:23 +0000227TEST_F(ZeroingTest, WaitForIndexPulseAfterReset) {
228 double index_diff = 0.6;
229 PositionSensorSimulator sim(index_diff);
230 sim.Initialize(3.1 * index_diff, index_diff / 3.0);
Tyler Chatowf8f03112017-02-05 14:31:34 -0800231 PotAndIndexPulseZeroingEstimator estimator(PotAndIndexPulseZeroingConstants{
Adam Snaider3cd11c52015-02-16 02:16:09 +0000232 kSampleSize, index_diff, 0.0, kIndexErrorFraction});
Philipp Schrader41d82912015-02-15 03:44:23 +0000233
234 // Make sure to fill up the averaging filter with samples.
235 for (unsigned int i = 0; i < kSampleSize; i++) {
236 MoveTo(&sim, &estimator, 3.1 * index_diff);
237 }
238
239 // Make sure we're not zeroed until we hit an index pulse.
240 ASSERT_FALSE(estimator.zeroed());
241
242 // Trigger an index pulse; we should now be zeroed.
243 MoveTo(&sim, &estimator, 4.5 * index_diff);
244 ASSERT_TRUE(estimator.zeroed());
245
246 // Reset the zeroing logic and supply a bunch of samples within the current
247 // index segment.
248 estimator.Reset();
249 for (unsigned int i = 0; i < kSampleSize; i++) {
250 MoveTo(&sim, &estimator, 4.2 * index_diff);
251 }
252
253 // Make sure we're not zeroed until we hit an index pulse.
254 ASSERT_FALSE(estimator.zeroed());
255
256 // Trigger another index pulse; we should be zeroed again.
257 MoveTo(&sim, &estimator, 3.1 * index_diff);
258 ASSERT_TRUE(estimator.zeroed());
259}
260
Philipp Schrader030ad182015-02-15 05:40:58 +0000261TEST_F(ZeroingTest, TestNonZeroIndexPulseOffsets) {
262 const double index_diff = 0.9;
263 const double known_index_pos = 3.5 * index_diff;
264 PositionSensorSimulator sim(index_diff);
265 sim.Initialize(3.3 * index_diff, index_diff / 3.0, known_index_pos);
Tyler Chatowf8f03112017-02-05 14:31:34 -0800266 PotAndIndexPulseZeroingEstimator estimator(PotAndIndexPulseZeroingConstants{
Adam Snaider3cd11c52015-02-16 02:16:09 +0000267 kSampleSize, index_diff, known_index_pos, kIndexErrorFraction});
Philipp Schrader030ad182015-02-15 05:40:58 +0000268
269 // Make sure to fill up the averaging filter with samples.
270 for (unsigned int i = 0; i < kSampleSize; i++) {
271 MoveTo(&sim, &estimator, 3.3 * index_diff);
272 }
273
274 // Make sure we're not zeroed until we hit an index pulse.
275 ASSERT_FALSE(estimator.zeroed());
276
277 // Trigger an index pulse; we should now be zeroed.
278 MoveTo(&sim, &estimator, 3.7 * index_diff);
279 ASSERT_TRUE(estimator.zeroed());
280 ASSERT_DOUBLE_EQ(3.3 * index_diff, estimator.offset());
Brian Silvermanf37839c2017-02-19 18:07:15 -0800281 ASSERT_DOUBLE_EQ(3.7 * index_diff, estimator.GetEstimatorState().position);
Philipp Schrader030ad182015-02-15 05:40:58 +0000282
283 // Trigger one more index pulse and check the offset.
284 MoveTo(&sim, &estimator, 4.7 * index_diff);
285 ASSERT_DOUBLE_EQ(3.3 * index_diff, estimator.offset());
Brian Silvermanf37839c2017-02-19 18:07:15 -0800286 ASSERT_DOUBLE_EQ(4.7 * index_diff, estimator.GetEstimatorState().position);
Philipp Schrader030ad182015-02-15 05:40:58 +0000287}
288
Philipp Schrader53f4b6d2015-02-15 22:32:08 +0000289TEST_F(ZeroingTest, BasicErrorAPITest) {
290 const double index_diff = 1.0;
Tyler Chatowf8f03112017-02-05 14:31:34 -0800291 PotAndIndexPulseZeroingEstimator estimator(PotAndIndexPulseZeroingConstants{
Adam Snaider3cd11c52015-02-16 02:16:09 +0000292 kSampleSize, index_diff, 0.0, kIndexErrorFraction});
Philipp Schrader53f4b6d2015-02-15 22:32:08 +0000293 PositionSensorSimulator sim(index_diff);
294 sim.Initialize(1.5 * index_diff, index_diff / 3.0, 0.0);
295
296 // Perform a simple move and make sure that no error occured.
297 MoveTo(&sim, &estimator, 3.5 * index_diff);
298 ASSERT_FALSE(estimator.error());
299
300 // Trigger an error and make sure it's reported.
301 estimator.TriggerError();
302 ASSERT_TRUE(estimator.error());
303
304 // Make sure that it can recover after a reset.
305 estimator.Reset();
306 ASSERT_FALSE(estimator.error());
307 MoveTo(&sim, &estimator, 4.5 * index_diff);
308 MoveTo(&sim, &estimator, 5.5 * index_diff);
309 ASSERT_FALSE(estimator.error());
310}
311
Brian Silvermana10d20a2017-02-19 14:28:53 -0800312// Tests that an error is detected when the starting position changes too much.
313TEST_F(ZeroingTest, TestIndexOffsetError) {
Adam Snaider3cd11c52015-02-16 02:16:09 +0000314 const double index_diff = 0.8;
315 const double known_index_pos = 2 * index_diff;
Austin Schuh5f01f152017-02-11 21:34:08 -0800316 const size_t sample_size = 30;
Adam Snaider3cd11c52015-02-16 02:16:09 +0000317 PositionSensorSimulator sim(index_diff);
318 sim.Initialize(10 * index_diff, index_diff / 3.0, known_index_pos);
Tyler Chatowf8f03112017-02-05 14:31:34 -0800319 PotAndIndexPulseZeroingEstimator estimator(PotAndIndexPulseZeroingConstants{
Adam Snaider3cd11c52015-02-16 02:16:09 +0000320 sample_size, index_diff, known_index_pos, kIndexErrorFraction});
321
Austin Schuh5f01f152017-02-11 21:34:08 -0800322 for (size_t i = 0; i < sample_size; i++) {
Adam Snaider3cd11c52015-02-16 02:16:09 +0000323 MoveTo(&sim, &estimator, 13 * index_diff);
324 }
325 MoveTo(&sim, &estimator, 8 * index_diff);
326
327 ASSERT_TRUE(estimator.zeroed());
328 ASSERT_FALSE(estimator.error());
329 sim.Initialize(9.0 * index_diff + 0.31 * index_diff, index_diff / 3.0,
330 known_index_pos);
331 MoveTo(&sim, &estimator, 9 * index_diff);
332 ASSERT_TRUE(estimator.zeroed());
333 ASSERT_TRUE(estimator.error());
334}
335
Austin Schuh5f01f152017-02-11 21:34:08 -0800336// Makes sure that using an absolute encoder lets us zero without moving.
Austin Schuhd82068e2019-01-26 20:05:42 -0800337TEST_F(ZeroingTest, TestPotAndAbsoluteEncoderZeroingWithoutMovement) {
Austin Schuh5f01f152017-02-11 21:34:08 -0800338 const double index_diff = 1.0;
339 PositionSensorSimulator sim(index_diff);
340
341 const double start_pos = 2.1;
342 double measured_absolute_position = 0.3 * index_diff;
343
Brian Silvermana10d20a2017-02-19 14:28:53 -0800344 PotAndAbsoluteEncoderZeroingConstants constants{
345 kSampleSize, index_diff, measured_absolute_position,
346 0.1, kMovingBufferSize, kIndexErrorFraction};
Austin Schuh5f01f152017-02-11 21:34:08 -0800347
348 sim.Initialize(start_pos, index_diff / 3.0, 0.0,
349 constants.measured_absolute_position);
350
Austin Schuh72db9a12019-01-21 18:02:51 -0800351 PotAndAbsoluteEncoderZeroingEstimator estimator(constants);
Austin Schuh5f01f152017-02-11 21:34:08 -0800352
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800353 for (size_t i = 0; i < kSampleSize + kMovingBufferSize - 1; ++i) {
Austin Schuh5f01f152017-02-11 21:34:08 -0800354 MoveTo(&sim, &estimator, start_pos);
355 ASSERT_FALSE(estimator.zeroed());
356 }
357
358 MoveTo(&sim, &estimator, start_pos);
359 ASSERT_TRUE(estimator.zeroed());
360 EXPECT_DOUBLE_EQ(start_pos, estimator.offset());
361}
362
Austin Schuhddd08f82018-03-02 20:05:29 -0800363// Makes sure that we ignore a NAN if we get it, but will correctly zero
Austin Schuhd82068e2019-01-26 20:05:42 -0800364// afterwards.
365TEST_F(ZeroingTest, TestPotAndAbsoluteEncoderZeroingIgnoresNAN) {
Austin Schuhddd08f82018-03-02 20:05:29 -0800366 const double index_diff = 1.0;
367 PositionSensorSimulator sim(index_diff);
368
369 const double start_pos = 2.1;
370 double measured_absolute_position = 0.3 * index_diff;
371
372 PotAndAbsoluteEncoderZeroingConstants constants{
373 kSampleSize, index_diff, measured_absolute_position,
374 0.1, kMovingBufferSize, kIndexErrorFraction};
375
376 sim.Initialize(start_pos, index_diff / 3.0, 0.0,
377 constants.measured_absolute_position);
378
Austin Schuh72db9a12019-01-21 18:02:51 -0800379 PotAndAbsoluteEncoderZeroingEstimator estimator(constants);
Austin Schuhddd08f82018-03-02 20:05:29 -0800380
381 // We tolerate a couple NANs before we start.
382 PotAndAbsolutePosition sensor_values;
383 sensor_values.absolute_encoder = ::std::numeric_limits<double>::quiet_NaN();
384 sensor_values.encoder = 0.0;
385 sensor_values.pot = 0.0;
386 for (size_t i = 0; i < kSampleSize - 1; ++i) {
387 estimator.UpdateEstimate(sensor_values);
388 }
389
390 for (size_t i = 0; i < kSampleSize + kMovingBufferSize - 1; ++i) {
391 MoveTo(&sim, &estimator, start_pos);
392 ASSERT_FALSE(estimator.zeroed());
393 }
394
395 MoveTo(&sim, &estimator, start_pos);
396 ASSERT_TRUE(estimator.zeroed());
397 EXPECT_DOUBLE_EQ(start_pos, estimator.offset());
398}
399
Austin Schuh5f01f152017-02-11 21:34:08 -0800400// Makes sure that using an absolute encoder doesn't let us zero while moving.
Austin Schuhd82068e2019-01-26 20:05:42 -0800401TEST_F(ZeroingTest, TestPotAndAbsoluteEncoderZeroingWithMovement) {
Austin Schuh5f01f152017-02-11 21:34:08 -0800402 const double index_diff = 1.0;
403 PositionSensorSimulator sim(index_diff);
404
405 const double start_pos = 10 * index_diff;
406 double measured_absolute_position = 0.3 * index_diff;
407
Brian Silvermana10d20a2017-02-19 14:28:53 -0800408 PotAndAbsoluteEncoderZeroingConstants constants{
409 kSampleSize, index_diff, measured_absolute_position,
410 0.1, kMovingBufferSize, kIndexErrorFraction};
Austin Schuh5f01f152017-02-11 21:34:08 -0800411
412 sim.Initialize(start_pos, index_diff / 3.0, 0.0,
413 constants.measured_absolute_position);
414
Austin Schuh72db9a12019-01-21 18:02:51 -0800415 PotAndAbsoluteEncoderZeroingEstimator estimator(constants);
Austin Schuh5f01f152017-02-11 21:34:08 -0800416
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800417 for (size_t i = 0; i < kSampleSize + kMovingBufferSize - 1; ++i) {
Austin Schuh5f01f152017-02-11 21:34:08 -0800418 MoveTo(&sim, &estimator, start_pos + i * index_diff);
419 ASSERT_FALSE(estimator.zeroed());
420 }
421 MoveTo(&sim, &estimator, start_pos + 10 * index_diff);
422
423 MoveTo(&sim, &estimator, start_pos);
424 ASSERT_FALSE(estimator.zeroed());
425}
Neil Balch16275e32017-02-18 16:38:45 -0800426
427// Makes sure we detect an error if the ZeroingEstimator gets sent a NaN.
Austin Schuhd82068e2019-01-26 20:05:42 -0800428TEST_F(ZeroingTest, TestPotAndAbsoluteEncoderZeroingWithNaN) {
Neil Balch16275e32017-02-18 16:38:45 -0800429 PotAndAbsoluteEncoderZeroingConstants constants{
Brian Silvermana10d20a2017-02-19 14:28:53 -0800430 kSampleSize, 1, 0.3, 0.1, kMovingBufferSize, kIndexErrorFraction};
Neil Balch16275e32017-02-18 16:38:45 -0800431
Austin Schuh72db9a12019-01-21 18:02:51 -0800432 PotAndAbsoluteEncoderZeroingEstimator estimator(constants);
Neil Balch16275e32017-02-18 16:38:45 -0800433
Austin Schuhddd08f82018-03-02 20:05:29 -0800434 PotAndAbsolutePosition sensor_values;
435 sensor_values.absolute_encoder = ::std::numeric_limits<double>::quiet_NaN();
436 sensor_values.encoder = 0.0;
437 sensor_values.pot = 0.0;
438 // We tolerate a couple NANs before we start.
439 for (size_t i = 0; i < kSampleSize - 1; ++i) {
440 estimator.UpdateEstimate(sensor_values);
441 }
442 ASSERT_FALSE(estimator.error());
Neil Balch16275e32017-02-18 16:38:45 -0800443
Austin Schuhddd08f82018-03-02 20:05:29 -0800444 estimator.UpdateEstimate(sensor_values);
Neil Balch16275e32017-02-18 16:38:45 -0800445 ASSERT_TRUE(estimator.error());
446}
447
Brian Silvermana10d20a2017-02-19 14:28:53 -0800448// Tests that an error is detected when the starting position changes too much.
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000449TEST_F(ZeroingTest, TestRelativeEncoderZeroing) {
450 EncoderPlusIndexZeroingConstants constants;
451 constants.index_pulse_count = 3;
452 constants.index_difference = 10.0;
453 constants.measured_index_position = 20.0;
454 constants.known_index_pulse = 1;
Philipp Schrader3f5b6182017-03-25 22:36:37 +0000455 constants.allowable_encoder_error = 0.01;
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000456
457 PositionSensorSimulator sim(constants.index_difference);
458
459 const double start_pos = 2.5 * constants.index_difference;
460
461 sim.Initialize(start_pos, constants.index_difference / 3.0,
462 constants.measured_index_position);
463
464 PulseIndexZeroingEstimator estimator(constants);
465
466 // Should not be zeroed when we stand still.
467 for (int i = 0; i < 300; ++i) {
468 MoveTo(&sim, &estimator, start_pos);
469 ASSERT_FALSE(estimator.zeroed());
470 }
471
472 // Move to 1.5 constants.index_difference and we should still not be zeroed.
473 MoveTo(&sim, &estimator, 1.5 * constants.index_difference);
474 ASSERT_FALSE(estimator.zeroed());
475
476 // Move to 0.5 constants.index_difference and we should still not be zeroed.
477 MoveTo(&sim, &estimator, 0.5 * constants.index_difference);
478 ASSERT_FALSE(estimator.zeroed());
479
480 // Move back to 1.5 constants.index_difference and we should still not be
481 // zeroed.
482 MoveTo(&sim, &estimator, 1.5 * constants.index_difference);
483 ASSERT_FALSE(estimator.zeroed());
484
485 // Move back to 2.5 constants.index_difference and we should still not be
486 // zeroed.
487 MoveTo(&sim, &estimator, 2.5 * constants.index_difference);
488 ASSERT_FALSE(estimator.zeroed());
489
490 // Move back to 3.5 constants.index_difference and we should now be zeroed.
491 MoveTo(&sim, &estimator, 3.5 * constants.index_difference);
492 ASSERT_TRUE(estimator.zeroed());
493
494 ASSERT_DOUBLE_EQ(start_pos, estimator.offset());
Brian Silvermanf37839c2017-02-19 18:07:15 -0800495 ASSERT_DOUBLE_EQ(3.5 * constants.index_difference,
496 estimator.GetEstimatorState().position);
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000497
498 MoveTo(&sim, &estimator, 0.5 * constants.index_difference);
Brian Silvermanf37839c2017-02-19 18:07:15 -0800499 ASSERT_DOUBLE_EQ(0.5 * constants.index_difference,
500 estimator.GetEstimatorState().position);
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000501}
502
Philipp Schrader3f5b6182017-03-25 22:36:37 +0000503// Tests that we can detect when an index pulse occurs where we didn't expect
504// it to for the PulseIndexZeroingEstimator.
505TEST_F(ZeroingTest, TestRelativeEncoderSlipping) {
506 EncoderPlusIndexZeroingConstants constants;
507 constants.index_pulse_count = 3;
508 constants.index_difference = 10.0;
509 constants.measured_index_position = 20.0;
510 constants.known_index_pulse = 1;
511 constants.allowable_encoder_error = 0.05;
512
513 PositionSensorSimulator sim(constants.index_difference);
514
515 const double start_pos =
516 constants.measured_index_position + 0.5 * constants.index_difference;
517
518 for (double direction : {1.0, -1.0}) {
519 sim.Initialize(start_pos, constants.index_difference / 3.0,
520 constants.measured_index_position);
521
522 PulseIndexZeroingEstimator estimator(constants);
523
524 // Zero the estimator.
525 MoveTo(&sim, &estimator, start_pos - 1 * constants.index_difference);
526 MoveTo(
527 &sim, &estimator,
528 start_pos - constants.index_pulse_count * constants.index_difference);
529 ASSERT_TRUE(estimator.zeroed());
530 ASSERT_FALSE(estimator.error());
531
532 // We have a 5% allowable error so we slip a little bit each time and make
533 // sure that the index pulses are still accepted.
534 for (double error = 0.00;
535 ::std::abs(error) < constants.allowable_encoder_error;
536 error += 0.01 * direction) {
537 sim.Initialize(start_pos, constants.index_difference / 3.0,
538 constants.measured_index_position +
539 error * constants.index_difference);
540 MoveTo(&sim, &estimator, start_pos - constants.index_difference);
541 EXPECT_FALSE(estimator.error());
542 }
543
544 // As soon as we hit cross the error margin, we should trigger an error.
545 sim.Initialize(start_pos, constants.index_difference / 3.0,
546 constants.measured_index_position +
547 constants.allowable_encoder_error * 1.1 *
548 constants.index_difference * direction);
549 MoveTo(&sim, &estimator, start_pos - constants.index_difference);
550 ASSERT_TRUE(estimator.error());
551 }
552}
553
Austin Schuh1fe4fb42017-10-16 22:35:54 -0700554// Test fixture for HallEffectAndPositionZeroingEstimator.
555class HallEffectAndPositionZeroingEstimatorTest : public ZeroingTest {
556 public:
557 // The starting position of the system.
558 static constexpr double kStartPosition = 2.0;
559
560 // Returns a reasonable set of test constants.
561 static constants::HallEffectZeroingConstants MakeConstants() {
562 constants::HallEffectZeroingConstants constants;
563 constants.lower_hall_position = 0.25;
564 constants.upper_hall_position = 0.75;
565 constants.index_difference = 1.0;
566 constants.hall_trigger_zeroing_length = 2;
567 constants.zeroing_move_direction = false;
568 return constants;
569 }
570
571 HallEffectAndPositionZeroingEstimatorTest()
572 : constants_(MakeConstants()), sim_(constants_.index_difference) {
573 // Start the system out at the starting position.
574 sim_.InitializeHallEffectAndPosition(kStartPosition,
575 constants_.lower_hall_position,
576 constants_.upper_hall_position);
577 }
578
579 protected:
580 // Constants, and the simulation using them.
581 const constants::HallEffectZeroingConstants constants_;
582 PositionSensorSimulator sim_;
583};
584
Austin Schuh55934032017-03-11 12:45:27 -0800585// Tests that an error is detected when the starting position changes too much.
Austin Schuh1fe4fb42017-10-16 22:35:54 -0700586TEST_F(HallEffectAndPositionZeroingEstimatorTest, TestHallEffectZeroing) {
587 HallEffectAndPositionZeroingEstimator estimator(constants_);
Austin Schuh55934032017-03-11 12:45:27 -0800588
589 // Should not be zeroed when we stand still.
590 for (int i = 0; i < 300; ++i) {
Austin Schuh1fe4fb42017-10-16 22:35:54 -0700591 MoveTo(&sim_, &estimator, kStartPosition);
Austin Schuh55934032017-03-11 12:45:27 -0800592 ASSERT_FALSE(estimator.zeroed());
593 }
594
Austin Schuh1fe4fb42017-10-16 22:35:54 -0700595 MoveTo(&sim_, &estimator, 1.9);
Austin Schuh55934032017-03-11 12:45:27 -0800596 ASSERT_FALSE(estimator.zeroed());
597
598 // Move to where the hall effect is triggered and make sure it becomes zeroed.
Austin Schuh1fe4fb42017-10-16 22:35:54 -0700599 MoveTo(&sim_, &estimator, 1.5);
Austin Schuh55934032017-03-11 12:45:27 -0800600 EXPECT_FALSE(estimator.zeroed());
Austin Schuh1fe4fb42017-10-16 22:35:54 -0700601 MoveTo(&sim_, &estimator, 1.5);
Austin Schuh55934032017-03-11 12:45:27 -0800602 ASSERT_TRUE(estimator.zeroed());
603
Austin Schuh1fe4fb42017-10-16 22:35:54 -0700604 // Check that the offset is calculated correctly. We should expect to read
605 // 0.5. Since the encoder is reading -0.5 right now, the offset needs to be
606 // 1.
607 EXPECT_DOUBLE_EQ(1.0, estimator.offset());
Austin Schuh55934032017-03-11 12:45:27 -0800608
609 // Make sure triggering errors works.
610 estimator.TriggerError();
611 ASSERT_TRUE(estimator.error());
612
613 // Ensure resetting resets the state of the estimator.
614 estimator.Reset();
615 ASSERT_FALSE(estimator.zeroed());
616 ASSERT_FALSE(estimator.error());
Austin Schuh1fe4fb42017-10-16 22:35:54 -0700617}
Austin Schuh55934032017-03-11 12:45:27 -0800618
Austin Schuh1fe4fb42017-10-16 22:35:54 -0700619// Tests that we don't zero on a too short pulse.
620TEST_F(HallEffectAndPositionZeroingEstimatorTest, TestTooShortPulse) {
621 HallEffectAndPositionZeroingEstimator estimator(constants_);
Austin Schuh55934032017-03-11 12:45:27 -0800622
Austin Schuh1fe4fb42017-10-16 22:35:54 -0700623 // Trigger for 1 cycle.
624 MoveTo(&sim_, &estimator, 0.9);
Austin Schuh55934032017-03-11 12:45:27 -0800625 EXPECT_FALSE(estimator.zeroed());
Austin Schuh1fe4fb42017-10-16 22:35:54 -0700626 MoveTo(&sim_, &estimator, 0.5);
Austin Schuh55934032017-03-11 12:45:27 -0800627 EXPECT_FALSE(estimator.zeroed());
Austin Schuh1fe4fb42017-10-16 22:35:54 -0700628 MoveTo(&sim_, &estimator, 0.9);
Austin Schuh55934032017-03-11 12:45:27 -0800629 EXPECT_FALSE(estimator.zeroed());
630}
631
Austin Schuh1fe4fb42017-10-16 22:35:54 -0700632// Tests that we don't zero when we go the wrong direction.
633TEST_F(HallEffectAndPositionZeroingEstimatorTest, TestWrongDirectionNoZero) {
634 HallEffectAndPositionZeroingEstimator estimator(constants_);
635
636 // Pass through the sensor, lingering long enough that we should zero.
637 MoveTo(&sim_, &estimator, 0.0);
638 ASSERT_FALSE(estimator.zeroed());
639 MoveTo(&sim_, &estimator, 0.4);
640 EXPECT_FALSE(estimator.zeroed());
641 MoveTo(&sim_, &estimator, 0.6);
642 EXPECT_FALSE(estimator.zeroed());
643 MoveTo(&sim_, &estimator, 0.7);
644 EXPECT_FALSE(estimator.zeroed());
645 MoveTo(&sim_, &estimator, 0.9);
646 EXPECT_FALSE(estimator.zeroed());
647}
648
649// Make sure we don't zero if we start in the hall effect's range.
650TEST_F(HallEffectAndPositionZeroingEstimatorTest, TestStartingOnNoZero) {
651 HallEffectAndPositionZeroingEstimator estimator(constants_);
652 MoveTo(&sim_, &estimator, 0.5);
653 estimator.Reset();
654
655 // Stay on the hall effect. We shouldn't zero.
656 EXPECT_FALSE(estimator.zeroed());
657 MoveTo(&sim_, &estimator, 0.5);
658 EXPECT_FALSE(estimator.zeroed());
659 MoveTo(&sim_, &estimator, 0.5);
660 EXPECT_FALSE(estimator.zeroed());
661
662 // Verify moving off the hall still doesn't zero us.
663 MoveTo(&sim_, &estimator, 0.0);
664 EXPECT_FALSE(estimator.zeroed());
665 MoveTo(&sim_, &estimator, 0.0);
666 EXPECT_FALSE(estimator.zeroed());
667}
Austin Schuh55934032017-03-11 12:45:27 -0800668
Austin Schuhd82068e2019-01-26 20:05:42 -0800669// Makes sure that using an absolute encoder lets us zero without moving.
670TEST_F(ZeroingTest, TestAbsoluteEncoderZeroingWithoutMovement) {
671 const double index_diff = 1.0;
672 PositionSensorSimulator sim(index_diff);
673
674 const double kMiddlePosition = 2.5;
675 const double start_pos = 2.1;
676 double measured_absolute_position = 0.3 * index_diff;
677
678 AbsoluteEncoderZeroingConstants constants{
679 kSampleSize, index_diff, measured_absolute_position,
680 kMiddlePosition, 0.1, kMovingBufferSize,
681 kIndexErrorFraction};
682
683 sim.Initialize(start_pos, index_diff / 3.0, 0.0,
684 constants.measured_absolute_position);
685
686 AbsoluteEncoderZeroingEstimator estimator(constants);
687
688 for (size_t i = 0; i < kSampleSize + kMovingBufferSize - 1; ++i) {
689 MoveTo(&sim, &estimator, start_pos);
690 ASSERT_FALSE(estimator.zeroed());
691 }
692
693 MoveTo(&sim, &estimator, start_pos);
694 ASSERT_TRUE(estimator.zeroed());
695 EXPECT_DOUBLE_EQ(start_pos, estimator.offset());
696}
697
698// Makes sure that we ignore a NAN if we get it, but will correctly zero
699// afterwards.
700TEST_F(ZeroingTest, TestAbsoluteEncoderZeroingIgnoresNAN) {
701 const double index_diff = 1.0;
702 PositionSensorSimulator sim(index_diff);
703
704 const double start_pos = 2.1;
705 double measured_absolute_position = 0.3 * index_diff;
706 const double kMiddlePosition = 2.5;
707
708 AbsoluteEncoderZeroingConstants constants{
709 kSampleSize, index_diff, measured_absolute_position,
710 kMiddlePosition, 0.1, kMovingBufferSize,
711 kIndexErrorFraction};
712
713 sim.Initialize(start_pos, index_diff / 3.0, 0.0,
714 constants.measured_absolute_position);
715
716 AbsoluteEncoderZeroingEstimator estimator(constants);
717
718 // We tolerate a couple NANs before we start.
719 AbsolutePosition sensor_values;
720 sensor_values.absolute_encoder = ::std::numeric_limits<double>::quiet_NaN();
721 sensor_values.encoder = 0.0;
722 for (size_t i = 0; i < kSampleSize - 1; ++i) {
723 estimator.UpdateEstimate(sensor_values);
724 }
725
726 for (size_t i = 0; i < kSampleSize + kMovingBufferSize - 1; ++i) {
727 MoveTo(&sim, &estimator, start_pos);
728 ASSERT_FALSE(estimator.zeroed());
729 }
730
731 MoveTo(&sim, &estimator, start_pos);
732 ASSERT_TRUE(estimator.zeroed());
733 EXPECT_DOUBLE_EQ(start_pos, estimator.offset());
734}
735
736// Makes sure that using an absolute encoder doesn't let us zero while moving.
737TEST_F(ZeroingTest, TestAbsoluteEncoderZeroingWithMovement) {
738 const double index_diff = 1.0;
739 PositionSensorSimulator sim(index_diff);
740
741 const double start_pos = 10 * index_diff;
742 double measured_absolute_position = 0.3 * index_diff;
743 const double kMiddlePosition = 2.5;
744
745 AbsoluteEncoderZeroingConstants constants{
746 kSampleSize, index_diff, measured_absolute_position,
747 kMiddlePosition, 0.1, kMovingBufferSize,
748 kIndexErrorFraction};
749
750 sim.Initialize(start_pos, index_diff / 3.0, 0.0,
751 constants.measured_absolute_position);
752
753 AbsoluteEncoderZeroingEstimator estimator(constants);
754
755 for (size_t i = 0; i < kSampleSize + kMovingBufferSize - 1; ++i) {
756 MoveTo(&sim, &estimator, start_pos + i * index_diff);
757 ASSERT_FALSE(estimator.zeroed());
758 }
759 MoveTo(&sim, &estimator, start_pos + 10 * index_diff);
760
761 MoveTo(&sim, &estimator, start_pos);
762 ASSERT_FALSE(estimator.zeroed());
763}
764
765// Makes sure we detect an error if the ZeroingEstimator gets sent a NaN.
766TEST_F(ZeroingTest, TestAbsoluteEncoderZeroingWithNaN) {
767 AbsoluteEncoderZeroingConstants constants{
768 kSampleSize, 1, 0.3, 1.0, 0.1, kMovingBufferSize, kIndexErrorFraction};
769
770 AbsoluteEncoderZeroingEstimator estimator(constants);
771
772 AbsolutePosition sensor_values;
773 sensor_values.absolute_encoder = ::std::numeric_limits<double>::quiet_NaN();
774 sensor_values.encoder = 0.0;
775 // We tolerate a couple NANs before we start.
776 for (size_t i = 0; i < kSampleSize - 1; ++i) {
777 estimator.UpdateEstimate(sensor_values);
778 }
779 ASSERT_FALSE(estimator.error());
780
781 estimator.UpdateEstimate(sensor_values);
782 ASSERT_TRUE(estimator.error());
783}
784
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000785} // namespace zeroing
786} // namespace frc971