blob: 67981238e07c6ae6c606d59f7f105c72ee644f6d [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 Snaiderc4b3c192015-02-01 01:30:39 +000023
Adam Snaiderb4119252015-02-15 01:30:57 +000024class ZeroingTest : public ::testing::Test {
Adam Snaiderc4b3c192015-02-01 01:30:39 +000025 protected:
26 void SetUp() override { aos::SetDieTestMode(true); }
27
Adam Snaiderb4119252015-02-15 01:30:57 +000028 void MoveTo(PositionSensorSimulator* simulator, ZeroingEstimator* estimator,
29 double new_position) {
30 PotAndIndexPosition sensor_values_;
31 simulator->MoveTo(new_position);
32 simulator->GetSensorValues(&sensor_values_);
33 estimator->UpdateEstimate(sensor_values_);
34 }
Adam Snaiderc4b3c192015-02-01 01:30:39 +000035
Adam Snaiderb4119252015-02-15 01:30:57 +000036 aos::common::testing::GlobalCoreInstance my_core;
Adam Snaiderc4b3c192015-02-01 01:30:39 +000037};
38
Adam Snaiderb4119252015-02-15 01:30:57 +000039TEST_F(ZeroingTest, TestMovingAverageFilter) {
40 const double index_diff = 1.0;
41 PositionSensorSimulator sim(index_diff);
42 sim.Initialize(3.6 * index_diff, index_diff / 3.0);
43 ZeroingEstimator estimator(
44 Values::ZeroingConstants{kSampleSize, index_diff, 0.0});
Adam Snaiderc4b3c192015-02-01 01:30:39 +000045
46 // The zeroing code is supposed to perform some filtering on the difference
47 // between the potentiometer value and the encoder value. We assume that 300
48 // samples are sufficient to have updated the filter.
49 for (int i = 0; i < 300; i++) {
Adam Snaiderb4119252015-02-15 01:30:57 +000050 MoveTo(&sim, &estimator, 3.3 * index_diff);
Adam Snaiderc4b3c192015-02-01 01:30:39 +000051 }
Adam Snaiderb4119252015-02-15 01:30:57 +000052 ASSERT_NEAR(3.3 * index_diff, estimator.position(),
53 kAcceptableUnzeroedError * index_diff);
Adam Snaiderc4b3c192015-02-01 01:30:39 +000054
55 for (int i = 0; i < 300; i++) {
Adam Snaiderb4119252015-02-15 01:30:57 +000056 MoveTo(&sim, &estimator, 3.9 * index_diff);
Adam Snaiderc4b3c192015-02-01 01:30:39 +000057 }
Adam Snaiderb4119252015-02-15 01:30:57 +000058 ASSERT_NEAR(3.9 * index_diff, estimator.position(),
59 kAcceptableUnzeroedError * index_diff);
Adam Snaiderc4b3c192015-02-01 01:30:39 +000060}
61
Adam Snaiderb4119252015-02-15 01:30:57 +000062TEST_F(ZeroingTest, NotZeroedBeforeEnoughSamplesCollected) {
63 double index_diff = 0.5;
64 double position = 3.6 * index_diff;
65 PositionSensorSimulator sim(index_diff);
66 sim.Initialize(position, index_diff / 3.0);
67 ZeroingEstimator estimator(
68 Values::ZeroingConstants{kSampleSize, index_diff, 0.0});
69
70 // Make sure that the zeroing code does not consider itself zeroed until we
71 // collect a good amount of samples. In this case we're waiting until the
72 // moving average filter is full.
73 for (unsigned int i = 0; i < kSampleSize - 1; i++) {
74 MoveTo(&sim, &estimator, position += index_diff);
75 ASSERT_FALSE(estimator.zeroed());
76 }
77
78 MoveTo(&sim, &estimator, position);
79 ASSERT_TRUE(estimator.zeroed());
80}
81
82TEST_F(ZeroingTest, TestLotsOfMovement) {
83 double index_diff = 1.0;
84 PositionSensorSimulator sim(index_diff);
85 sim.Initialize(3.6, index_diff / 3.0);
86 ZeroingEstimator estimator(
87 Values::ZeroingConstants{kSampleSize, index_diff, 0.0});
Adam Snaiderc4b3c192015-02-01 01:30:39 +000088
89 // The zeroing code is supposed to perform some filtering on the difference
90 // between the potentiometer value and the encoder value. We assume that 300
91 // samples are sufficient to have updated the filter.
92 for (int i = 0; i < 300; i++) {
Adam Snaiderb4119252015-02-15 01:30:57 +000093 MoveTo(&sim, &estimator, 3.6);
Adam Snaiderc4b3c192015-02-01 01:30:39 +000094 }
Adam Snaiderb4119252015-02-15 01:30:57 +000095 ASSERT_NEAR(3.6, estimator.position(), kAcceptableUnzeroedError * index_diff);
Adam Snaiderc4b3c192015-02-01 01:30:39 +000096
97 // With a single index pulse the zeroing estimator should be able to lock
98 // onto the true value of the position.
Adam Snaiderb4119252015-02-15 01:30:57 +000099 MoveTo(&sim, &estimator, 4.01);
100 ASSERT_NEAR(4.01, estimator.position(), 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000101
Adam Snaiderb4119252015-02-15 01:30:57 +0000102 MoveTo(&sim, &estimator, 4.99);
103 ASSERT_NEAR(4.99, estimator.position(), 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000104
Adam Snaiderb4119252015-02-15 01:30:57 +0000105 MoveTo(&sim, &estimator, 3.99);
106 ASSERT_NEAR(3.99, estimator.position(), 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000107
Adam Snaiderb4119252015-02-15 01:30:57 +0000108 MoveTo(&sim, &estimator, 3.01);
109 ASSERT_NEAR(3.01, estimator.position(), 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000110
Adam Snaiderb4119252015-02-15 01:30:57 +0000111 MoveTo(&sim, &estimator, 13.55);
112 ASSERT_NEAR(13.55, estimator.position(), 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000113}
114
Adam Snaiderb4119252015-02-15 01:30:57 +0000115TEST_F(ZeroingTest, TestDifferentIndexDiffs) {
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000116 double index_diff = 0.89;
Adam Snaiderb4119252015-02-15 01:30:57 +0000117 PositionSensorSimulator sim(index_diff);
118 sim.Initialize(3.5 * index_diff, index_diff / 3.0);
119 ZeroingEstimator estimator(
120 Values::ZeroingConstants{kSampleSize, index_diff, 0.0});
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000121
122 // The zeroing code is supposed to perform some filtering on the difference
123 // between the potentiometer value and the encoder value. We assume that 300
124 // samples are sufficient to have updated the filter.
125 for (int i = 0; i < 300; i++) {
Adam Snaiderb4119252015-02-15 01:30:57 +0000126 MoveTo(&sim, &estimator, 3.5 * index_diff);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000127 }
Adam Snaiderb4119252015-02-15 01:30:57 +0000128 ASSERT_NEAR(3.5 * index_diff, estimator.position(),
129 kAcceptableUnzeroedError * index_diff);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000130
131 // With a single index pulse the zeroing estimator should be able to lock
132 // onto the true value of the position.
Adam Snaiderb4119252015-02-15 01:30:57 +0000133 MoveTo(&sim, &estimator, 4.01);
134 ASSERT_NEAR(4.01, estimator.position(), 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000135
Adam Snaiderb4119252015-02-15 01:30:57 +0000136 MoveTo(&sim, &estimator, 4.99);
137 ASSERT_NEAR(4.99, estimator.position(), 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000138
Adam Snaiderb4119252015-02-15 01:30:57 +0000139 MoveTo(&sim, &estimator, 3.99);
140 ASSERT_NEAR(3.99, estimator.position(), 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000141
Adam Snaiderb4119252015-02-15 01:30:57 +0000142 MoveTo(&sim, &estimator, 3.01);
143 ASSERT_NEAR(3.01, estimator.position(), 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000144
Adam Snaiderb4119252015-02-15 01:30:57 +0000145 MoveTo(&sim, &estimator, 13.55);
146 ASSERT_NEAR(13.55, estimator.position(), 0.001);
147}
148
149TEST_F(ZeroingTest, TestPercentage) {
150 double index_diff = 0.89;
151 PositionSensorSimulator sim(index_diff);
152 sim.Initialize(3.5 * index_diff, index_diff / 3.0);
153 ZeroingEstimator estimator(
154 Values::ZeroingConstants{kSampleSize, index_diff, 0.0});
155
156 for (unsigned int i = 0; i < kSampleSize / 2; i++) {
157 MoveTo(&sim, &estimator, 3.5 * index_diff);
158 }
159 ASSERT_NEAR(0.5, estimator.offset_ratio_ready(), 0.001);
160}
161
162TEST_F(ZeroingTest, TestOffset) {
163 double index_diff = 0.89;
164 PositionSensorSimulator sim(index_diff);
165 sim.Initialize(3.1 * index_diff, index_diff / 3.0);
166 ZeroingEstimator estimator(
167 Values::ZeroingConstants{kSampleSize, index_diff, 0.0});
168
Philipp Schrader41d82912015-02-15 03:44:23 +0000169 MoveTo(&sim, &estimator, 3.1 * index_diff);
170
Adam Snaiderb4119252015-02-15 01:30:57 +0000171 for (unsigned int i = 0; i < kSampleSize; i++) {
172 MoveTo(&sim, &estimator, 5.0 * index_diff);
173 }
Philipp Schrader41d82912015-02-15 03:44:23 +0000174
Adam Snaiderb4119252015-02-15 01:30:57 +0000175 ASSERT_NEAR(3.1 * index_diff, estimator.offset(), 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000176}
177
Philipp Schrader41d82912015-02-15 03:44:23 +0000178TEST_F(ZeroingTest, WaitForIndexPulseAfterReset) {
179 double index_diff = 0.6;
180 PositionSensorSimulator sim(index_diff);
181 sim.Initialize(3.1 * index_diff, index_diff / 3.0);
182 ZeroingEstimator estimator(
183 Values::ZeroingConstants{kSampleSize, index_diff, 0.0});
184
185 // Make sure to fill up the averaging filter with samples.
186 for (unsigned int i = 0; i < kSampleSize; i++) {
187 MoveTo(&sim, &estimator, 3.1 * index_diff);
188 }
189
190 // Make sure we're not zeroed until we hit an index pulse.
191 ASSERT_FALSE(estimator.zeroed());
192
193 // Trigger an index pulse; we should now be zeroed.
194 MoveTo(&sim, &estimator, 4.5 * index_diff);
195 ASSERT_TRUE(estimator.zeroed());
196
197 // Reset the zeroing logic and supply a bunch of samples within the current
198 // index segment.
199 estimator.Reset();
200 for (unsigned int i = 0; i < kSampleSize; i++) {
201 MoveTo(&sim, &estimator, 4.2 * index_diff);
202 }
203
204 // Make sure we're not zeroed until we hit an index pulse.
205 ASSERT_FALSE(estimator.zeroed());
206
207 // Trigger another index pulse; we should be zeroed again.
208 MoveTo(&sim, &estimator, 3.1 * index_diff);
209 ASSERT_TRUE(estimator.zeroed());
210}
211
Philipp Schrader030ad182015-02-15 05:40:58 +0000212TEST_F(ZeroingTest, TestNonZeroIndexPulseOffsets) {
213 const double index_diff = 0.9;
214 const double known_index_pos = 3.5 * index_diff;
215 PositionSensorSimulator sim(index_diff);
216 sim.Initialize(3.3 * index_diff, index_diff / 3.0, known_index_pos);
217 ZeroingEstimator estimator(
218 Values::ZeroingConstants{kSampleSize, index_diff, known_index_pos});
219
220 // Make sure to fill up the averaging filter with samples.
221 for (unsigned int i = 0; i < kSampleSize; i++) {
222 MoveTo(&sim, &estimator, 3.3 * index_diff);
223 }
224
225 // Make sure we're not zeroed until we hit an index pulse.
226 ASSERT_FALSE(estimator.zeroed());
227
228 // Trigger an index pulse; we should now be zeroed.
229 MoveTo(&sim, &estimator, 3.7 * index_diff);
230 ASSERT_TRUE(estimator.zeroed());
231 ASSERT_DOUBLE_EQ(3.3 * index_diff, estimator.offset());
232 ASSERT_DOUBLE_EQ(3.7 * index_diff, estimator.position());
233
234 // Trigger one more index pulse and check the offset.
235 MoveTo(&sim, &estimator, 4.7 * index_diff);
236 ASSERT_DOUBLE_EQ(3.3 * index_diff, estimator.offset());
237 ASSERT_DOUBLE_EQ(4.7 * index_diff, estimator.position());
238}
239
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000240} // namespace zeroing
241} // namespace frc971