blob: 404b0e0d6b1d79f7eb22f4cfa1d304764c843807 [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"
Adam Snaiderc4b3c192015-02-01 01:30:39 +000010#include "aos/common/queue_testutils.h"
11#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;
19using constants::Values;
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
Adam Snaiderb4119252015-02-15 01:30:57 +000037 aos::common::testing::GlobalCoreInstance my_core;
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);
Adam Snaider3cd11c52015-02-16 02:16:09 +000044 ZeroingEstimator estimator(Values::ZeroingConstants{
45 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);
Adam Snaider3cd11c52015-02-16 02:16:09 +000068 ZeroingEstimator estimator(Values::ZeroingConstants{
69 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);
Adam Snaider3cd11c52015-02-16 02:16:09 +000087 ZeroingEstimator estimator(Values::ZeroingConstants{
88 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);
Adam Snaider3cd11c52015-02-16 02:16:09 +0000120 ZeroingEstimator estimator(Values::ZeroingConstants{
121 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);
Adam Snaider3cd11c52015-02-16 02:16:09 +0000154 ZeroingEstimator estimator(Values::ZeroingConstants{
155 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);
161}
162
163TEST_F(ZeroingTest, TestOffset) {
164 double index_diff = 0.89;
165 PositionSensorSimulator sim(index_diff);
166 sim.Initialize(3.1 * index_diff, index_diff / 3.0);
Adam Snaider3cd11c52015-02-16 02:16:09 +0000167 ZeroingEstimator estimator(Values::ZeroingConstants{
168 kSampleSize, index_diff, 0.0, kIndexErrorFraction});
Adam Snaiderb4119252015-02-15 01:30:57 +0000169
Philipp Schrader41d82912015-02-15 03:44:23 +0000170 MoveTo(&sim, &estimator, 3.1 * index_diff);
171
Adam Snaiderb4119252015-02-15 01:30:57 +0000172 for (unsigned int i = 0; i < kSampleSize; i++) {
173 MoveTo(&sim, &estimator, 5.0 * index_diff);
174 }
Philipp Schrader41d82912015-02-15 03:44:23 +0000175
Adam Snaiderb4119252015-02-15 01:30:57 +0000176 ASSERT_NEAR(3.1 * index_diff, estimator.offset(), 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000177}
178
Philipp Schrader41d82912015-02-15 03:44:23 +0000179TEST_F(ZeroingTest, WaitForIndexPulseAfterReset) {
180 double index_diff = 0.6;
181 PositionSensorSimulator sim(index_diff);
182 sim.Initialize(3.1 * index_diff, index_diff / 3.0);
Adam Snaider3cd11c52015-02-16 02:16:09 +0000183 ZeroingEstimator estimator(Values::ZeroingConstants{
184 kSampleSize, index_diff, 0.0, kIndexErrorFraction});
Philipp Schrader41d82912015-02-15 03:44:23 +0000185
186 // Make sure to fill up the averaging filter with samples.
187 for (unsigned int i = 0; i < kSampleSize; i++) {
188 MoveTo(&sim, &estimator, 3.1 * index_diff);
189 }
190
191 // Make sure we're not zeroed until we hit an index pulse.
192 ASSERT_FALSE(estimator.zeroed());
193
194 // Trigger an index pulse; we should now be zeroed.
195 MoveTo(&sim, &estimator, 4.5 * index_diff);
196 ASSERT_TRUE(estimator.zeroed());
197
198 // Reset the zeroing logic and supply a bunch of samples within the current
199 // index segment.
200 estimator.Reset();
201 for (unsigned int i = 0; i < kSampleSize; i++) {
202 MoveTo(&sim, &estimator, 4.2 * index_diff);
203 }
204
205 // Make sure we're not zeroed until we hit an index pulse.
206 ASSERT_FALSE(estimator.zeroed());
207
208 // Trigger another index pulse; we should be zeroed again.
209 MoveTo(&sim, &estimator, 3.1 * index_diff);
210 ASSERT_TRUE(estimator.zeroed());
211}
212
Philipp Schrader030ad182015-02-15 05:40:58 +0000213TEST_F(ZeroingTest, TestNonZeroIndexPulseOffsets) {
214 const double index_diff = 0.9;
215 const double known_index_pos = 3.5 * index_diff;
216 PositionSensorSimulator sim(index_diff);
217 sim.Initialize(3.3 * index_diff, index_diff / 3.0, known_index_pos);
Adam Snaider3cd11c52015-02-16 02:16:09 +0000218 ZeroingEstimator estimator(Values::ZeroingConstants{
219 kSampleSize, index_diff, known_index_pos, kIndexErrorFraction});
Philipp Schrader030ad182015-02-15 05:40:58 +0000220
221 // Make sure to fill up the averaging filter with samples.
222 for (unsigned int i = 0; i < kSampleSize; i++) {
223 MoveTo(&sim, &estimator, 3.3 * index_diff);
224 }
225
226 // Make sure we're not zeroed until we hit an index pulse.
227 ASSERT_FALSE(estimator.zeroed());
228
229 // Trigger an index pulse; we should now be zeroed.
230 MoveTo(&sim, &estimator, 3.7 * index_diff);
231 ASSERT_TRUE(estimator.zeroed());
232 ASSERT_DOUBLE_EQ(3.3 * index_diff, estimator.offset());
233 ASSERT_DOUBLE_EQ(3.7 * index_diff, estimator.position());
234
235 // Trigger one more index pulse and check the offset.
236 MoveTo(&sim, &estimator, 4.7 * index_diff);
237 ASSERT_DOUBLE_EQ(3.3 * index_diff, estimator.offset());
238 ASSERT_DOUBLE_EQ(4.7 * index_diff, estimator.position());
239}
240
Philipp Schrader53f4b6d2015-02-15 22:32:08 +0000241TEST_F(ZeroingTest, BasicErrorAPITest) {
242 const double index_diff = 1.0;
Adam Snaider3cd11c52015-02-16 02:16:09 +0000243 ZeroingEstimator estimator(Values::ZeroingConstants{
244 kSampleSize, index_diff, 0.0, kIndexErrorFraction});
Philipp Schrader53f4b6d2015-02-15 22:32:08 +0000245 PositionSensorSimulator sim(index_diff);
246 sim.Initialize(1.5 * index_diff, index_diff / 3.0, 0.0);
247
248 // Perform a simple move and make sure that no error occured.
249 MoveTo(&sim, &estimator, 3.5 * index_diff);
250 ASSERT_FALSE(estimator.error());
251
252 // Trigger an error and make sure it's reported.
253 estimator.TriggerError();
254 ASSERT_TRUE(estimator.error());
255
256 // Make sure that it can recover after a reset.
257 estimator.Reset();
258 ASSERT_FALSE(estimator.error());
259 MoveTo(&sim, &estimator, 4.5 * index_diff);
260 MoveTo(&sim, &estimator, 5.5 * index_diff);
261 ASSERT_FALSE(estimator.error());
262}
263
Adam Snaider3cd11c52015-02-16 02:16:09 +0000264// I want to test that the the zeroing class can
265// detect an error when the starting position
266// changes too much. I do so by creating the
267// simulator at an 'X' positon, making sure
268// that the estimator is zeroed, and then
269// initializing the simulator at another
270// position. After making sure it's zeroed,
271// if the error() function returns true,
272// then, it works.
273TEST_F(ZeroingTest, TestOffsetError) {
274 const double index_diff = 0.8;
275 const double known_index_pos = 2 * index_diff;
276 int sample_size = 30;
277 PositionSensorSimulator sim(index_diff);
278 sim.Initialize(10 * index_diff, index_diff / 3.0, known_index_pos);
279 ZeroingEstimator estimator(Values::ZeroingConstants{
280 sample_size, index_diff, known_index_pos, kIndexErrorFraction});
281
282 for (int i = 0; i < sample_size; i++) {
283 MoveTo(&sim, &estimator, 13 * index_diff);
284 }
285 MoveTo(&sim, &estimator, 8 * index_diff);
286
287 ASSERT_TRUE(estimator.zeroed());
288 ASSERT_FALSE(estimator.error());
289 sim.Initialize(9.0 * index_diff + 0.31 * index_diff, index_diff / 3.0,
290 known_index_pos);
291 MoveTo(&sim, &estimator, 9 * index_diff);
292 ASSERT_TRUE(estimator.zeroed());
293 ASSERT_TRUE(estimator.error());
294}
295
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000296} // namespace zeroing
297} // namespace frc971