blob: 2a3576458bdfdb945c3a428c2677fa60379e71b6 [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;
Tyler Chatowf8f03112017-02-05 14:31:34 -080019using constants::PotAndIndexPulseZeroingConstants;
Austin Schuh5f01f152017-02-11 21:34:08 -080020using constants::PotAndAbsoluteEncoderZeroingConstants;
Isaac Wilcove0851ffd2017-02-16 04:13:14 +000021using constants::EncoderPlusIndexZeroingConstants;
Adam Snaiderc4b3c192015-02-01 01:30:39 +000022
Adam Snaiderb4119252015-02-15 01:30:57 +000023static const size_t kSampleSize = 30;
24static const double kAcceptableUnzeroedError = 0.2;
Adam Snaider3cd11c52015-02-16 02:16:09 +000025static const double kIndexErrorFraction = 0.3;
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -080026static const size_t kMovingBufferSize = 3;
Adam Snaiderc4b3c192015-02-01 01:30:39 +000027
Adam Snaiderb4119252015-02-15 01:30:57 +000028class ZeroingTest : public ::testing::Test {
Adam Snaiderc4b3c192015-02-01 01:30:39 +000029 protected:
30 void SetUp() override { aos::SetDieTestMode(true); }
31
Tyler Chatowf8f03112017-02-05 14:31:34 -080032 void MoveTo(PositionSensorSimulator *simulator,
33 PotAndIndexPulseZeroingEstimator *estimator,
Adam Snaiderb4119252015-02-15 01:30:57 +000034 double new_position) {
Brian Silvermandc4eb102017-02-05 17:34:41 -080035 PotAndIndexPosition sensor_values;
Adam Snaiderb4119252015-02-15 01:30:57 +000036 simulator->MoveTo(new_position);
Brian Silvermandc4eb102017-02-05 17:34:41 -080037 simulator->GetSensorValues(&sensor_values);
38 estimator->UpdateEstimate(sensor_values);
Adam Snaiderb4119252015-02-15 01:30:57 +000039 }
Adam Snaiderc4b3c192015-02-01 01:30:39 +000040
Austin Schuh5f01f152017-02-11 21:34:08 -080041 void MoveTo(PositionSensorSimulator *simulator,
Austin Schuh72db9a12019-01-21 18:02:51 -080042 PotAndAbsoluteEncoderZeroingEstimator *estimator,
Neil Balch16275e32017-02-18 16:38:45 -080043 double new_position) {
Austin Schuh5f01f152017-02-11 21:34:08 -080044 PotAndAbsolutePosition sensor_values_;
45 simulator->MoveTo(new_position);
46 simulator->GetSensorValues(&sensor_values_);
47 estimator->UpdateEstimate(sensor_values_);
48 }
Isaac Wilcove0851ffd2017-02-16 04:13:14 +000049
50 void MoveTo(PositionSensorSimulator *simulator,
51 PulseIndexZeroingEstimator *estimator, double new_position) {
52 IndexPosition sensor_values_;
53 simulator->MoveTo(new_position);
54 simulator->GetSensorValues(&sensor_values_);
55 estimator->UpdateEstimate(sensor_values_);
56 }
Austin Schuh5f01f152017-02-11 21:34:08 -080057
Austin Schuh55934032017-03-11 12:45:27 -080058 void MoveTo(PositionSensorSimulator *simulator,
59 HallEffectAndPositionZeroingEstimator *estimator,
60 double new_position) {
61 HallEffectAndPosition sensor_values_;
62 simulator->MoveTo(new_position);
63 simulator->GetSensorValues(&sensor_values_);
64 estimator->UpdateEstimate(sensor_values_);
65 }
66
Brian Silvermanf5f8d8e2015-12-06 18:39:12 -050067 ::aos::testing::TestSharedMemory my_shm_;
Adam Snaiderc4b3c192015-02-01 01:30:39 +000068};
69
Adam Snaiderb4119252015-02-15 01:30:57 +000070TEST_F(ZeroingTest, TestMovingAverageFilter) {
71 const double index_diff = 1.0;
72 PositionSensorSimulator sim(index_diff);
73 sim.Initialize(3.6 * index_diff, index_diff / 3.0);
Tyler Chatowf8f03112017-02-05 14:31:34 -080074 PotAndIndexPulseZeroingEstimator estimator(PotAndIndexPulseZeroingConstants{
Adam Snaider3cd11c52015-02-16 02:16:09 +000075 kSampleSize, index_diff, 0.0, kIndexErrorFraction});
Adam Snaiderc4b3c192015-02-01 01:30:39 +000076
77 // The zeroing code is supposed to perform some filtering on the difference
78 // between the potentiometer value and the encoder value. We assume that 300
79 // samples are sufficient to have updated the filter.
80 for (int i = 0; i < 300; i++) {
Adam Snaiderb4119252015-02-15 01:30:57 +000081 MoveTo(&sim, &estimator, 3.3 * index_diff);
Adam Snaiderc4b3c192015-02-01 01:30:39 +000082 }
Brian Silvermanf37839c2017-02-19 18:07:15 -080083 ASSERT_NEAR(3.3 * index_diff, estimator.GetEstimatorState().position,
Adam Snaiderb4119252015-02-15 01:30:57 +000084 kAcceptableUnzeroedError * index_diff);
Adam Snaiderc4b3c192015-02-01 01:30:39 +000085
86 for (int i = 0; i < 300; i++) {
Adam Snaiderb4119252015-02-15 01:30:57 +000087 MoveTo(&sim, &estimator, 3.9 * index_diff);
Adam Snaiderc4b3c192015-02-01 01:30:39 +000088 }
Brian Silvermanf37839c2017-02-19 18:07:15 -080089 ASSERT_NEAR(3.9 * index_diff, estimator.GetEstimatorState().position,
Adam Snaiderb4119252015-02-15 01:30:57 +000090 kAcceptableUnzeroedError * index_diff);
Adam Snaiderc4b3c192015-02-01 01:30:39 +000091}
92
Adam Snaiderb4119252015-02-15 01:30:57 +000093TEST_F(ZeroingTest, NotZeroedBeforeEnoughSamplesCollected) {
94 double index_diff = 0.5;
95 double position = 3.6 * index_diff;
96 PositionSensorSimulator sim(index_diff);
97 sim.Initialize(position, index_diff / 3.0);
Tyler Chatowf8f03112017-02-05 14:31:34 -080098 PotAndIndexPulseZeroingEstimator estimator(PotAndIndexPulseZeroingConstants{
Adam Snaider3cd11c52015-02-16 02:16:09 +000099 kSampleSize, index_diff, 0.0, kIndexErrorFraction});
Adam Snaiderb4119252015-02-15 01:30:57 +0000100
101 // Make sure that the zeroing code does not consider itself zeroed until we
102 // collect a good amount of samples. In this case we're waiting until the
103 // moving average filter is full.
104 for (unsigned int i = 0; i < kSampleSize - 1; i++) {
105 MoveTo(&sim, &estimator, position += index_diff);
106 ASSERT_FALSE(estimator.zeroed());
107 }
108
109 MoveTo(&sim, &estimator, position);
110 ASSERT_TRUE(estimator.zeroed());
111}
112
113TEST_F(ZeroingTest, TestLotsOfMovement) {
114 double index_diff = 1.0;
115 PositionSensorSimulator sim(index_diff);
116 sim.Initialize(3.6, index_diff / 3.0);
Tyler Chatowf8f03112017-02-05 14:31:34 -0800117 PotAndIndexPulseZeroingEstimator estimator(PotAndIndexPulseZeroingConstants{
Adam Snaider3cd11c52015-02-16 02:16:09 +0000118 kSampleSize, index_diff, 0.0, kIndexErrorFraction});
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000119
120 // The zeroing code is supposed to perform some filtering on the difference
121 // between the potentiometer value and the encoder value. We assume that 300
122 // samples are sufficient to have updated the filter.
123 for (int i = 0; i < 300; i++) {
Adam Snaiderb4119252015-02-15 01:30:57 +0000124 MoveTo(&sim, &estimator, 3.6);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000125 }
Brian Silvermanf37839c2017-02-19 18:07:15 -0800126 ASSERT_NEAR(3.6, estimator.GetEstimatorState().position,
127 kAcceptableUnzeroedError * index_diff);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000128
129 // With a single index pulse the zeroing estimator should be able to lock
130 // onto the true value of the position.
Adam Snaiderb4119252015-02-15 01:30:57 +0000131 MoveTo(&sim, &estimator, 4.01);
Brian Silvermanf37839c2017-02-19 18:07:15 -0800132 ASSERT_NEAR(4.01, estimator.GetEstimatorState().position, 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000133
Adam Snaiderb4119252015-02-15 01:30:57 +0000134 MoveTo(&sim, &estimator, 4.99);
Brian Silvermanf37839c2017-02-19 18:07:15 -0800135 ASSERT_NEAR(4.99, estimator.GetEstimatorState().position, 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000136
Adam Snaiderb4119252015-02-15 01:30:57 +0000137 MoveTo(&sim, &estimator, 3.99);
Brian Silvermanf37839c2017-02-19 18:07:15 -0800138 ASSERT_NEAR(3.99, estimator.GetEstimatorState().position, 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000139
Adam Snaiderb4119252015-02-15 01:30:57 +0000140 MoveTo(&sim, &estimator, 3.01);
Brian Silvermanf37839c2017-02-19 18:07:15 -0800141 ASSERT_NEAR(3.01, estimator.GetEstimatorState().position, 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000142
Adam Snaiderb4119252015-02-15 01:30:57 +0000143 MoveTo(&sim, &estimator, 13.55);
Brian Silvermanf37839c2017-02-19 18:07:15 -0800144 ASSERT_NEAR(13.55, estimator.GetEstimatorState().position, 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000145}
146
Adam Snaiderb4119252015-02-15 01:30:57 +0000147TEST_F(ZeroingTest, TestDifferentIndexDiffs) {
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000148 double index_diff = 0.89;
Adam Snaiderb4119252015-02-15 01:30:57 +0000149 PositionSensorSimulator sim(index_diff);
150 sim.Initialize(3.5 * index_diff, index_diff / 3.0);
Tyler Chatowf8f03112017-02-05 14:31:34 -0800151 PotAndIndexPulseZeroingEstimator estimator(PotAndIndexPulseZeroingConstants{
Adam Snaider3cd11c52015-02-16 02:16:09 +0000152 kSampleSize, index_diff, 0.0, kIndexErrorFraction});
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000153
154 // The zeroing code is supposed to perform some filtering on the difference
155 // between the potentiometer value and the encoder value. We assume that 300
156 // samples are sufficient to have updated the filter.
157 for (int i = 0; i < 300; i++) {
Adam Snaiderb4119252015-02-15 01:30:57 +0000158 MoveTo(&sim, &estimator, 3.5 * index_diff);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000159 }
Brian Silvermanf37839c2017-02-19 18:07:15 -0800160 ASSERT_NEAR(3.5 * index_diff, estimator.GetEstimatorState().position,
Adam Snaiderb4119252015-02-15 01:30:57 +0000161 kAcceptableUnzeroedError * index_diff);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000162
163 // With a single index pulse the zeroing estimator should be able to lock
164 // onto the true value of the position.
Adam Snaiderb4119252015-02-15 01:30:57 +0000165 MoveTo(&sim, &estimator, 4.01);
Brian Silvermanf37839c2017-02-19 18:07:15 -0800166 ASSERT_NEAR(4.01, estimator.GetEstimatorState().position, 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000167
Adam Snaiderb4119252015-02-15 01:30:57 +0000168 MoveTo(&sim, &estimator, 4.99);
Brian Silvermanf37839c2017-02-19 18:07:15 -0800169 ASSERT_NEAR(4.99, estimator.GetEstimatorState().position, 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000170
Adam Snaiderb4119252015-02-15 01:30:57 +0000171 MoveTo(&sim, &estimator, 3.99);
Brian Silvermanf37839c2017-02-19 18:07:15 -0800172 ASSERT_NEAR(3.99, estimator.GetEstimatorState().position, 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000173
Adam Snaiderb4119252015-02-15 01:30:57 +0000174 MoveTo(&sim, &estimator, 3.01);
Brian Silvermanf37839c2017-02-19 18:07:15 -0800175 ASSERT_NEAR(3.01, estimator.GetEstimatorState().position, 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000176
Adam Snaiderb4119252015-02-15 01:30:57 +0000177 MoveTo(&sim, &estimator, 13.55);
Brian Silvermanf37839c2017-02-19 18:07:15 -0800178 ASSERT_NEAR(13.55, estimator.GetEstimatorState().position, 0.001);
Adam Snaiderb4119252015-02-15 01:30:57 +0000179}
180
181TEST_F(ZeroingTest, TestPercentage) {
182 double index_diff = 0.89;
183 PositionSensorSimulator sim(index_diff);
184 sim.Initialize(3.5 * index_diff, index_diff / 3.0);
Tyler Chatowf8f03112017-02-05 14:31:34 -0800185 PotAndIndexPulseZeroingEstimator estimator(PotAndIndexPulseZeroingConstants{
Adam Snaider3cd11c52015-02-16 02:16:09 +0000186 kSampleSize, index_diff, 0.0, kIndexErrorFraction});
Adam Snaiderb4119252015-02-15 01:30:57 +0000187
188 for (unsigned int i = 0; i < kSampleSize / 2; i++) {
189 MoveTo(&sim, &estimator, 3.5 * index_diff);
190 }
191 ASSERT_NEAR(0.5, estimator.offset_ratio_ready(), 0.001);
Austin Schuh7485dbb2016-02-08 00:21:58 -0800192 ASSERT_FALSE(estimator.offset_ready());
193
194 for (unsigned int i = 0; i < kSampleSize / 2; i++) {
195 MoveTo(&sim, &estimator, 3.5 * index_diff);
196 }
197 ASSERT_NEAR(1.0, estimator.offset_ratio_ready(), 0.001);
198 ASSERT_TRUE(estimator.offset_ready());
Adam Snaiderb4119252015-02-15 01:30:57 +0000199}
200
201TEST_F(ZeroingTest, TestOffset) {
202 double index_diff = 0.89;
203 PositionSensorSimulator sim(index_diff);
204 sim.Initialize(3.1 * index_diff, index_diff / 3.0);
Tyler Chatowf8f03112017-02-05 14:31:34 -0800205 PotAndIndexPulseZeroingEstimator estimator(PotAndIndexPulseZeroingConstants{
Adam Snaider3cd11c52015-02-16 02:16:09 +0000206 kSampleSize, index_diff, 0.0, kIndexErrorFraction});
Adam Snaiderb4119252015-02-15 01:30:57 +0000207
Philipp Schrader41d82912015-02-15 03:44:23 +0000208 MoveTo(&sim, &estimator, 3.1 * index_diff);
209
Adam Snaiderb4119252015-02-15 01:30:57 +0000210 for (unsigned int i = 0; i < kSampleSize; i++) {
211 MoveTo(&sim, &estimator, 5.0 * index_diff);
212 }
Philipp Schrader41d82912015-02-15 03:44:23 +0000213
Adam Snaiderb4119252015-02-15 01:30:57 +0000214 ASSERT_NEAR(3.1 * index_diff, estimator.offset(), 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000215}
216
Philipp Schrader41d82912015-02-15 03:44:23 +0000217TEST_F(ZeroingTest, WaitForIndexPulseAfterReset) {
218 double index_diff = 0.6;
219 PositionSensorSimulator sim(index_diff);
220 sim.Initialize(3.1 * index_diff, index_diff / 3.0);
Tyler Chatowf8f03112017-02-05 14:31:34 -0800221 PotAndIndexPulseZeroingEstimator estimator(PotAndIndexPulseZeroingConstants{
Adam Snaider3cd11c52015-02-16 02:16:09 +0000222 kSampleSize, index_diff, 0.0, kIndexErrorFraction});
Philipp Schrader41d82912015-02-15 03:44:23 +0000223
224 // Make sure to fill up the averaging filter with samples.
225 for (unsigned int i = 0; i < kSampleSize; i++) {
226 MoveTo(&sim, &estimator, 3.1 * index_diff);
227 }
228
229 // Make sure we're not zeroed until we hit an index pulse.
230 ASSERT_FALSE(estimator.zeroed());
231
232 // Trigger an index pulse; we should now be zeroed.
233 MoveTo(&sim, &estimator, 4.5 * index_diff);
234 ASSERT_TRUE(estimator.zeroed());
235
236 // Reset the zeroing logic and supply a bunch of samples within the current
237 // index segment.
238 estimator.Reset();
239 for (unsigned int i = 0; i < kSampleSize; i++) {
240 MoveTo(&sim, &estimator, 4.2 * index_diff);
241 }
242
243 // Make sure we're not zeroed until we hit an index pulse.
244 ASSERT_FALSE(estimator.zeroed());
245
246 // Trigger another index pulse; we should be zeroed again.
247 MoveTo(&sim, &estimator, 3.1 * index_diff);
248 ASSERT_TRUE(estimator.zeroed());
249}
250
Philipp Schrader030ad182015-02-15 05:40:58 +0000251TEST_F(ZeroingTest, TestNonZeroIndexPulseOffsets) {
252 const double index_diff = 0.9;
253 const double known_index_pos = 3.5 * index_diff;
254 PositionSensorSimulator sim(index_diff);
255 sim.Initialize(3.3 * index_diff, index_diff / 3.0, known_index_pos);
Tyler Chatowf8f03112017-02-05 14:31:34 -0800256 PotAndIndexPulseZeroingEstimator estimator(PotAndIndexPulseZeroingConstants{
Adam Snaider3cd11c52015-02-16 02:16:09 +0000257 kSampleSize, index_diff, known_index_pos, kIndexErrorFraction});
Philipp Schrader030ad182015-02-15 05:40:58 +0000258
259 // Make sure to fill up the averaging filter with samples.
260 for (unsigned int i = 0; i < kSampleSize; i++) {
261 MoveTo(&sim, &estimator, 3.3 * index_diff);
262 }
263
264 // Make sure we're not zeroed until we hit an index pulse.
265 ASSERT_FALSE(estimator.zeroed());
266
267 // Trigger an index pulse; we should now be zeroed.
268 MoveTo(&sim, &estimator, 3.7 * index_diff);
269 ASSERT_TRUE(estimator.zeroed());
270 ASSERT_DOUBLE_EQ(3.3 * index_diff, estimator.offset());
Brian Silvermanf37839c2017-02-19 18:07:15 -0800271 ASSERT_DOUBLE_EQ(3.7 * index_diff, estimator.GetEstimatorState().position);
Philipp Schrader030ad182015-02-15 05:40:58 +0000272
273 // Trigger one more index pulse and check the offset.
274 MoveTo(&sim, &estimator, 4.7 * index_diff);
275 ASSERT_DOUBLE_EQ(3.3 * index_diff, estimator.offset());
Brian Silvermanf37839c2017-02-19 18:07:15 -0800276 ASSERT_DOUBLE_EQ(4.7 * index_diff, estimator.GetEstimatorState().position);
Philipp Schrader030ad182015-02-15 05:40:58 +0000277}
278
Philipp Schrader53f4b6d2015-02-15 22:32:08 +0000279TEST_F(ZeroingTest, BasicErrorAPITest) {
280 const double index_diff = 1.0;
Tyler Chatowf8f03112017-02-05 14:31:34 -0800281 PotAndIndexPulseZeroingEstimator estimator(PotAndIndexPulseZeroingConstants{
Adam Snaider3cd11c52015-02-16 02:16:09 +0000282 kSampleSize, index_diff, 0.0, kIndexErrorFraction});
Philipp Schrader53f4b6d2015-02-15 22:32:08 +0000283 PositionSensorSimulator sim(index_diff);
284 sim.Initialize(1.5 * index_diff, index_diff / 3.0, 0.0);
285
286 // Perform a simple move and make sure that no error occured.
287 MoveTo(&sim, &estimator, 3.5 * index_diff);
288 ASSERT_FALSE(estimator.error());
289
290 // Trigger an error and make sure it's reported.
291 estimator.TriggerError();
292 ASSERT_TRUE(estimator.error());
293
294 // Make sure that it can recover after a reset.
295 estimator.Reset();
296 ASSERT_FALSE(estimator.error());
297 MoveTo(&sim, &estimator, 4.5 * index_diff);
298 MoveTo(&sim, &estimator, 5.5 * index_diff);
299 ASSERT_FALSE(estimator.error());
300}
301
Brian Silvermana10d20a2017-02-19 14:28:53 -0800302// Tests that an error is detected when the starting position changes too much.
303TEST_F(ZeroingTest, TestIndexOffsetError) {
Adam Snaider3cd11c52015-02-16 02:16:09 +0000304 const double index_diff = 0.8;
305 const double known_index_pos = 2 * index_diff;
Austin Schuh5f01f152017-02-11 21:34:08 -0800306 const size_t sample_size = 30;
Adam Snaider3cd11c52015-02-16 02:16:09 +0000307 PositionSensorSimulator sim(index_diff);
308 sim.Initialize(10 * index_diff, index_diff / 3.0, known_index_pos);
Tyler Chatowf8f03112017-02-05 14:31:34 -0800309 PotAndIndexPulseZeroingEstimator estimator(PotAndIndexPulseZeroingConstants{
Adam Snaider3cd11c52015-02-16 02:16:09 +0000310 sample_size, index_diff, known_index_pos, kIndexErrorFraction});
311
Austin Schuh5f01f152017-02-11 21:34:08 -0800312 for (size_t i = 0; i < sample_size; i++) {
Adam Snaider3cd11c52015-02-16 02:16:09 +0000313 MoveTo(&sim, &estimator, 13 * index_diff);
314 }
315 MoveTo(&sim, &estimator, 8 * index_diff);
316
317 ASSERT_TRUE(estimator.zeroed());
318 ASSERT_FALSE(estimator.error());
319 sim.Initialize(9.0 * index_diff + 0.31 * index_diff, index_diff / 3.0,
320 known_index_pos);
321 MoveTo(&sim, &estimator, 9 * index_diff);
322 ASSERT_TRUE(estimator.zeroed());
323 ASSERT_TRUE(estimator.error());
324}
325
Austin Schuh5f01f152017-02-11 21:34:08 -0800326// Makes sure that using an absolute encoder lets us zero without moving.
327TEST_F(ZeroingTest, TestAbsoluteEncoderZeroingWithoutMovement) {
328 const double index_diff = 1.0;
329 PositionSensorSimulator sim(index_diff);
330
331 const double start_pos = 2.1;
332 double measured_absolute_position = 0.3 * index_diff;
333
Brian Silvermana10d20a2017-02-19 14:28:53 -0800334 PotAndAbsoluteEncoderZeroingConstants constants{
335 kSampleSize, index_diff, measured_absolute_position,
336 0.1, kMovingBufferSize, kIndexErrorFraction};
Austin Schuh5f01f152017-02-11 21:34:08 -0800337
338 sim.Initialize(start_pos, index_diff / 3.0, 0.0,
339 constants.measured_absolute_position);
340
Austin Schuh72db9a12019-01-21 18:02:51 -0800341 PotAndAbsoluteEncoderZeroingEstimator estimator(constants);
Austin Schuh5f01f152017-02-11 21:34:08 -0800342
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800343 for (size_t i = 0; i < kSampleSize + kMovingBufferSize - 1; ++i) {
Austin Schuh5f01f152017-02-11 21:34:08 -0800344 MoveTo(&sim, &estimator, start_pos);
345 ASSERT_FALSE(estimator.zeroed());
346 }
347
348 MoveTo(&sim, &estimator, start_pos);
349 ASSERT_TRUE(estimator.zeroed());
350 EXPECT_DOUBLE_EQ(start_pos, estimator.offset());
351}
352
Austin Schuhddd08f82018-03-02 20:05:29 -0800353// Makes sure that we ignore a NAN if we get it, but will correctly zero
354// afterwords.
355TEST_F(ZeroingTest, TestAbsoluteEncoderZeroingIgnoresNAN) {
356 const double index_diff = 1.0;
357 PositionSensorSimulator sim(index_diff);
358
359 const double start_pos = 2.1;
360 double measured_absolute_position = 0.3 * index_diff;
361
362 PotAndAbsoluteEncoderZeroingConstants constants{
363 kSampleSize, index_diff, measured_absolute_position,
364 0.1, kMovingBufferSize, kIndexErrorFraction};
365
366 sim.Initialize(start_pos, index_diff / 3.0, 0.0,
367 constants.measured_absolute_position);
368
Austin Schuh72db9a12019-01-21 18:02:51 -0800369 PotAndAbsoluteEncoderZeroingEstimator estimator(constants);
Austin Schuhddd08f82018-03-02 20:05:29 -0800370
371 // We tolerate a couple NANs before we start.
372 PotAndAbsolutePosition sensor_values;
373 sensor_values.absolute_encoder = ::std::numeric_limits<double>::quiet_NaN();
374 sensor_values.encoder = 0.0;
375 sensor_values.pot = 0.0;
376 for (size_t i = 0; i < kSampleSize - 1; ++i) {
377 estimator.UpdateEstimate(sensor_values);
378 }
379
380 for (size_t i = 0; i < kSampleSize + kMovingBufferSize - 1; ++i) {
381 MoveTo(&sim, &estimator, start_pos);
382 ASSERT_FALSE(estimator.zeroed());
383 }
384
385 MoveTo(&sim, &estimator, start_pos);
386 ASSERT_TRUE(estimator.zeroed());
387 EXPECT_DOUBLE_EQ(start_pos, estimator.offset());
388}
389
Austin Schuh5f01f152017-02-11 21:34:08 -0800390// Makes sure that using an absolute encoder doesn't let us zero while moving.
391TEST_F(ZeroingTest, TestAbsoluteEncoderZeroingWithMovement) {
392 const double index_diff = 1.0;
393 PositionSensorSimulator sim(index_diff);
394
395 const double start_pos = 10 * index_diff;
396 double measured_absolute_position = 0.3 * index_diff;
397
Brian Silvermana10d20a2017-02-19 14:28:53 -0800398 PotAndAbsoluteEncoderZeroingConstants constants{
399 kSampleSize, index_diff, measured_absolute_position,
400 0.1, kMovingBufferSize, kIndexErrorFraction};
Austin Schuh5f01f152017-02-11 21:34:08 -0800401
402 sim.Initialize(start_pos, index_diff / 3.0, 0.0,
403 constants.measured_absolute_position);
404
Austin Schuh72db9a12019-01-21 18:02:51 -0800405 PotAndAbsoluteEncoderZeroingEstimator estimator(constants);
Austin Schuh5f01f152017-02-11 21:34:08 -0800406
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800407 for (size_t i = 0; i < kSampleSize + kMovingBufferSize - 1; ++i) {
Austin Schuh5f01f152017-02-11 21:34:08 -0800408 MoveTo(&sim, &estimator, start_pos + i * index_diff);
409 ASSERT_FALSE(estimator.zeroed());
410 }
411 MoveTo(&sim, &estimator, start_pos + 10 * index_diff);
412
413 MoveTo(&sim, &estimator, start_pos);
414 ASSERT_FALSE(estimator.zeroed());
415}
Neil Balch16275e32017-02-18 16:38:45 -0800416
417// Makes sure we detect an error if the ZeroingEstimator gets sent a NaN.
418TEST_F(ZeroingTest, TestAbsoluteEncoderZeroingWithNaN) {
419 PotAndAbsoluteEncoderZeroingConstants constants{
Brian Silvermana10d20a2017-02-19 14:28:53 -0800420 kSampleSize, 1, 0.3, 0.1, kMovingBufferSize, kIndexErrorFraction};
Neil Balch16275e32017-02-18 16:38:45 -0800421
Austin Schuh72db9a12019-01-21 18:02:51 -0800422 PotAndAbsoluteEncoderZeroingEstimator estimator(constants);
Neil Balch16275e32017-02-18 16:38:45 -0800423
Austin Schuhddd08f82018-03-02 20:05:29 -0800424 PotAndAbsolutePosition sensor_values;
425 sensor_values.absolute_encoder = ::std::numeric_limits<double>::quiet_NaN();
426 sensor_values.encoder = 0.0;
427 sensor_values.pot = 0.0;
428 // We tolerate a couple NANs before we start.
429 for (size_t i = 0; i < kSampleSize - 1; ++i) {
430 estimator.UpdateEstimate(sensor_values);
431 }
432 ASSERT_FALSE(estimator.error());
Neil Balch16275e32017-02-18 16:38:45 -0800433
Austin Schuhddd08f82018-03-02 20:05:29 -0800434 estimator.UpdateEstimate(sensor_values);
Neil Balch16275e32017-02-18 16:38:45 -0800435 ASSERT_TRUE(estimator.error());
436}
437
Brian Silvermana10d20a2017-02-19 14:28:53 -0800438// Tests that an error is detected when the starting position changes too much.
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000439TEST_F(ZeroingTest, TestRelativeEncoderZeroing) {
440 EncoderPlusIndexZeroingConstants constants;
441 constants.index_pulse_count = 3;
442 constants.index_difference = 10.0;
443 constants.measured_index_position = 20.0;
444 constants.known_index_pulse = 1;
Philipp Schrader3f5b6182017-03-25 22:36:37 +0000445 constants.allowable_encoder_error = 0.01;
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000446
447 PositionSensorSimulator sim(constants.index_difference);
448
449 const double start_pos = 2.5 * constants.index_difference;
450
451 sim.Initialize(start_pos, constants.index_difference / 3.0,
452 constants.measured_index_position);
453
454 PulseIndexZeroingEstimator estimator(constants);
455
456 // Should not be zeroed when we stand still.
457 for (int i = 0; i < 300; ++i) {
458 MoveTo(&sim, &estimator, start_pos);
459 ASSERT_FALSE(estimator.zeroed());
460 }
461
462 // Move to 1.5 constants.index_difference and we should still not be zeroed.
463 MoveTo(&sim, &estimator, 1.5 * constants.index_difference);
464 ASSERT_FALSE(estimator.zeroed());
465
466 // Move to 0.5 constants.index_difference and we should still not be zeroed.
467 MoveTo(&sim, &estimator, 0.5 * constants.index_difference);
468 ASSERT_FALSE(estimator.zeroed());
469
470 // Move back to 1.5 constants.index_difference and we should still not be
471 // zeroed.
472 MoveTo(&sim, &estimator, 1.5 * constants.index_difference);
473 ASSERT_FALSE(estimator.zeroed());
474
475 // Move back to 2.5 constants.index_difference and we should still not be
476 // zeroed.
477 MoveTo(&sim, &estimator, 2.5 * constants.index_difference);
478 ASSERT_FALSE(estimator.zeroed());
479
480 // Move back to 3.5 constants.index_difference and we should now be zeroed.
481 MoveTo(&sim, &estimator, 3.5 * constants.index_difference);
482 ASSERT_TRUE(estimator.zeroed());
483
484 ASSERT_DOUBLE_EQ(start_pos, estimator.offset());
Brian Silvermanf37839c2017-02-19 18:07:15 -0800485 ASSERT_DOUBLE_EQ(3.5 * constants.index_difference,
486 estimator.GetEstimatorState().position);
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000487
488 MoveTo(&sim, &estimator, 0.5 * constants.index_difference);
Brian Silvermanf37839c2017-02-19 18:07:15 -0800489 ASSERT_DOUBLE_EQ(0.5 * constants.index_difference,
490 estimator.GetEstimatorState().position);
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000491}
492
Philipp Schrader3f5b6182017-03-25 22:36:37 +0000493// Tests that we can detect when an index pulse occurs where we didn't expect
494// it to for the PulseIndexZeroingEstimator.
495TEST_F(ZeroingTest, TestRelativeEncoderSlipping) {
496 EncoderPlusIndexZeroingConstants constants;
497 constants.index_pulse_count = 3;
498 constants.index_difference = 10.0;
499 constants.measured_index_position = 20.0;
500 constants.known_index_pulse = 1;
501 constants.allowable_encoder_error = 0.05;
502
503 PositionSensorSimulator sim(constants.index_difference);
504
505 const double start_pos =
506 constants.measured_index_position + 0.5 * constants.index_difference;
507
508 for (double direction : {1.0, -1.0}) {
509 sim.Initialize(start_pos, constants.index_difference / 3.0,
510 constants.measured_index_position);
511
512 PulseIndexZeroingEstimator estimator(constants);
513
514 // Zero the estimator.
515 MoveTo(&sim, &estimator, start_pos - 1 * constants.index_difference);
516 MoveTo(
517 &sim, &estimator,
518 start_pos - constants.index_pulse_count * constants.index_difference);
519 ASSERT_TRUE(estimator.zeroed());
520 ASSERT_FALSE(estimator.error());
521
522 // We have a 5% allowable error so we slip a little bit each time and make
523 // sure that the index pulses are still accepted.
524 for (double error = 0.00;
525 ::std::abs(error) < constants.allowable_encoder_error;
526 error += 0.01 * direction) {
527 sim.Initialize(start_pos, constants.index_difference / 3.0,
528 constants.measured_index_position +
529 error * constants.index_difference);
530 MoveTo(&sim, &estimator, start_pos - constants.index_difference);
531 EXPECT_FALSE(estimator.error());
532 }
533
534 // As soon as we hit cross the error margin, we should trigger an error.
535 sim.Initialize(start_pos, constants.index_difference / 3.0,
536 constants.measured_index_position +
537 constants.allowable_encoder_error * 1.1 *
538 constants.index_difference * direction);
539 MoveTo(&sim, &estimator, start_pos - constants.index_difference);
540 ASSERT_TRUE(estimator.error());
541 }
542}
543
Austin Schuh1fe4fb42017-10-16 22:35:54 -0700544// Test fixture for HallEffectAndPositionZeroingEstimator.
545class HallEffectAndPositionZeroingEstimatorTest : public ZeroingTest {
546 public:
547 // The starting position of the system.
548 static constexpr double kStartPosition = 2.0;
549
550 // Returns a reasonable set of test constants.
551 static constants::HallEffectZeroingConstants MakeConstants() {
552 constants::HallEffectZeroingConstants constants;
553 constants.lower_hall_position = 0.25;
554 constants.upper_hall_position = 0.75;
555 constants.index_difference = 1.0;
556 constants.hall_trigger_zeroing_length = 2;
557 constants.zeroing_move_direction = false;
558 return constants;
559 }
560
561 HallEffectAndPositionZeroingEstimatorTest()
562 : constants_(MakeConstants()), sim_(constants_.index_difference) {
563 // Start the system out at the starting position.
564 sim_.InitializeHallEffectAndPosition(kStartPosition,
565 constants_.lower_hall_position,
566 constants_.upper_hall_position);
567 }
568
569 protected:
570 // Constants, and the simulation using them.
571 const constants::HallEffectZeroingConstants constants_;
572 PositionSensorSimulator sim_;
573};
574
Austin Schuh55934032017-03-11 12:45:27 -0800575// Tests that an error is detected when the starting position changes too much.
Austin Schuh1fe4fb42017-10-16 22:35:54 -0700576TEST_F(HallEffectAndPositionZeroingEstimatorTest, TestHallEffectZeroing) {
577 HallEffectAndPositionZeroingEstimator estimator(constants_);
Austin Schuh55934032017-03-11 12:45:27 -0800578
579 // Should not be zeroed when we stand still.
580 for (int i = 0; i < 300; ++i) {
Austin Schuh1fe4fb42017-10-16 22:35:54 -0700581 MoveTo(&sim_, &estimator, kStartPosition);
Austin Schuh55934032017-03-11 12:45:27 -0800582 ASSERT_FALSE(estimator.zeroed());
583 }
584
Austin Schuh1fe4fb42017-10-16 22:35:54 -0700585 MoveTo(&sim_, &estimator, 1.9);
Austin Schuh55934032017-03-11 12:45:27 -0800586 ASSERT_FALSE(estimator.zeroed());
587
588 // Move to where the hall effect is triggered and make sure it becomes zeroed.
Austin Schuh1fe4fb42017-10-16 22:35:54 -0700589 MoveTo(&sim_, &estimator, 1.5);
Austin Schuh55934032017-03-11 12:45:27 -0800590 EXPECT_FALSE(estimator.zeroed());
Austin Schuh1fe4fb42017-10-16 22:35:54 -0700591 MoveTo(&sim_, &estimator, 1.5);
Austin Schuh55934032017-03-11 12:45:27 -0800592 ASSERT_TRUE(estimator.zeroed());
593
Austin Schuh1fe4fb42017-10-16 22:35:54 -0700594 // Check that the offset is calculated correctly. We should expect to read
595 // 0.5. Since the encoder is reading -0.5 right now, the offset needs to be
596 // 1.
597 EXPECT_DOUBLE_EQ(1.0, estimator.offset());
Austin Schuh55934032017-03-11 12:45:27 -0800598
599 // Make sure triggering errors works.
600 estimator.TriggerError();
601 ASSERT_TRUE(estimator.error());
602
603 // Ensure resetting resets the state of the estimator.
604 estimator.Reset();
605 ASSERT_FALSE(estimator.zeroed());
606 ASSERT_FALSE(estimator.error());
Austin Schuh1fe4fb42017-10-16 22:35:54 -0700607}
Austin Schuh55934032017-03-11 12:45:27 -0800608
Austin Schuh1fe4fb42017-10-16 22:35:54 -0700609// Tests that we don't zero on a too short pulse.
610TEST_F(HallEffectAndPositionZeroingEstimatorTest, TestTooShortPulse) {
611 HallEffectAndPositionZeroingEstimator estimator(constants_);
Austin Schuh55934032017-03-11 12:45:27 -0800612
Austin Schuh1fe4fb42017-10-16 22:35:54 -0700613 // Trigger for 1 cycle.
614 MoveTo(&sim_, &estimator, 0.9);
Austin Schuh55934032017-03-11 12:45:27 -0800615 EXPECT_FALSE(estimator.zeroed());
Austin Schuh1fe4fb42017-10-16 22:35:54 -0700616 MoveTo(&sim_, &estimator, 0.5);
Austin Schuh55934032017-03-11 12:45:27 -0800617 EXPECT_FALSE(estimator.zeroed());
Austin Schuh1fe4fb42017-10-16 22:35:54 -0700618 MoveTo(&sim_, &estimator, 0.9);
Austin Schuh55934032017-03-11 12:45:27 -0800619 EXPECT_FALSE(estimator.zeroed());
620}
621
Austin Schuh1fe4fb42017-10-16 22:35:54 -0700622// Tests that we don't zero when we go the wrong direction.
623TEST_F(HallEffectAndPositionZeroingEstimatorTest, TestWrongDirectionNoZero) {
624 HallEffectAndPositionZeroingEstimator estimator(constants_);
625
626 // Pass through the sensor, lingering long enough that we should zero.
627 MoveTo(&sim_, &estimator, 0.0);
628 ASSERT_FALSE(estimator.zeroed());
629 MoveTo(&sim_, &estimator, 0.4);
630 EXPECT_FALSE(estimator.zeroed());
631 MoveTo(&sim_, &estimator, 0.6);
632 EXPECT_FALSE(estimator.zeroed());
633 MoveTo(&sim_, &estimator, 0.7);
634 EXPECT_FALSE(estimator.zeroed());
635 MoveTo(&sim_, &estimator, 0.9);
636 EXPECT_FALSE(estimator.zeroed());
637}
638
639// Make sure we don't zero if we start in the hall effect's range.
640TEST_F(HallEffectAndPositionZeroingEstimatorTest, TestStartingOnNoZero) {
641 HallEffectAndPositionZeroingEstimator estimator(constants_);
642 MoveTo(&sim_, &estimator, 0.5);
643 estimator.Reset();
644
645 // Stay on the hall effect. We shouldn't zero.
646 EXPECT_FALSE(estimator.zeroed());
647 MoveTo(&sim_, &estimator, 0.5);
648 EXPECT_FALSE(estimator.zeroed());
649 MoveTo(&sim_, &estimator, 0.5);
650 EXPECT_FALSE(estimator.zeroed());
651
652 // Verify moving off the hall still doesn't zero us.
653 MoveTo(&sim_, &estimator, 0.0);
654 EXPECT_FALSE(estimator.zeroed());
655 MoveTo(&sim_, &estimator, 0.0);
656 EXPECT_FALSE(estimator.zeroed());
657}
Austin Schuh55934032017-03-11 12:45:27 -0800658
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000659} // namespace zeroing
660} // namespace frc971