blob: 3869393a19a80f612c5e0ca4b46252b6cc85e21b [file] [log] [blame]
James Kuszmaulbdc6a792023-08-12 16:29:38 -07001#include "frc971/zeroing/continuous_absolute_encoder.h"
2
3#include "gmock/gmock.h"
4#include "gtest/gtest.h"
5
6#include "frc971/zeroing/wrap.h"
7#include "frc971/zeroing/zeroing_test.h"
8
9namespace frc971 {
10namespace zeroing {
11namespace testing {
12
13using constants::ContinuousAbsoluteEncoderZeroingConstants;
14
15class ContinuousAbsoluteEncoderZeroingTest : public ZeroingTest {
16 protected:
17 void MoveTo(PositionSensorSimulator *simulator,
18 ContinuousAbsoluteEncoderZeroingEstimator *estimator,
19 double new_position) {
20 simulator->MoveTo(new_position);
21 flatbuffers::FlatBufferBuilder fbb;
22 estimator->UpdateEstimate(
23 *simulator->FillSensorValues<AbsolutePosition>(&fbb));
24 }
25};
26
27// Makes sure that using an absolute encoder lets us zero without moving.
28TEST_F(ContinuousAbsoluteEncoderZeroingTest,
29 TestContinuousAbsoluteEncoderZeroingWithoutMovement) {
30 const double index_diff = 1.0;
31 PositionSensorSimulator sim(index_diff);
32
33 const double start_pos = 2.1;
34 double measured_absolute_position = 0.3 * index_diff;
35
36 ContinuousAbsoluteEncoderZeroingConstants constants{
37 kSampleSize, index_diff, measured_absolute_position,
38 0.1, kMovingBufferSize, kIndexErrorFraction};
39
40 sim.Initialize(start_pos, index_diff / 3.0, 0.0,
41 constants.measured_absolute_position);
42
43 ContinuousAbsoluteEncoderZeroingEstimator estimator(constants);
44
45 for (size_t i = 0; i < kSampleSize + kMovingBufferSize - 1; ++i) {
46 MoveTo(&sim, &estimator, start_pos);
47 ASSERT_FALSE(estimator.zeroed());
48 }
49
50 MoveTo(&sim, &estimator, start_pos);
51 ASSERT_TRUE(estimator.zeroed());
52 // Because the continuous estimator doesn't care about extra revolutions, it
53 // will have brought the offset down to less than index_diff.
54 EXPECT_NEAR(Wrap(0.0, start_pos, index_diff), estimator.offset(), 1e-12);
55}
56
57// Makes sure that if the underlying mechanism moves by >1 revolution that the
58// continuous zeroing estimator handles it correctly.
59TEST_F(ContinuousAbsoluteEncoderZeroingTest,
60 ContinuousEstimatorZeroesAcrossRevolution) {
61 const double index_diff = 1.0;
62 PositionSensorSimulator sim(index_diff);
63
64 const double start_pos = 2.1;
65 double measured_absolute_position = 0.3 * index_diff;
66
67 ContinuousAbsoluteEncoderZeroingConstants constants{
68 kSampleSize, index_diff, measured_absolute_position,
69 0.1, kMovingBufferSize, kIndexErrorFraction};
70
71 sim.Initialize(start_pos, index_diff / 3.0, 0.0,
72 constants.measured_absolute_position);
73
74 ContinuousAbsoluteEncoderZeroingEstimator estimator(constants);
75
76 for (size_t i = 0; i < kSampleSize + kMovingBufferSize - 1; ++i) {
77 MoveTo(&sim, &estimator, start_pos);
78 ASSERT_FALSE(estimator.zeroed());
79 }
80
81 MoveTo(&sim, &estimator, start_pos);
82 ASSERT_TRUE(estimator.zeroed());
83 // Because the continuous estimator doesn't care about extra revolutions, it
84 // will have brought the offset down to less than index_diff.
85 EXPECT_NEAR(Wrap(0.0, start_pos, index_diff), estimator.offset(), 1e-12);
86
87 // Now, rotate by a full revolution, then stay still. We should stay zeroed.
88 for (size_t i = 0; i < kSampleSize + kMovingBufferSize; ++i) {
89 MoveTo(&sim, &estimator, start_pos + 10 * index_diff);
90 }
91 ASSERT_TRUE(estimator.zeroed());
92 ASSERT_FALSE(estimator.error());
93}
94
95// Makes sure that we ignore a NAN if we get it, but will correctly zero
96// afterwards.
97TEST_F(ContinuousAbsoluteEncoderZeroingTest,
98 TestContinuousAbsoluteEncoderZeroingIgnoresNAN) {
99 const double index_diff = 1.0;
100 PositionSensorSimulator sim(index_diff);
101
102 const double start_pos = 2.1;
103 double measured_absolute_position = 0.3 * index_diff;
104
105 ContinuousAbsoluteEncoderZeroingConstants constants{
106 kSampleSize, index_diff, measured_absolute_position,
107 0.1, kMovingBufferSize, kIndexErrorFraction};
108
109 sim.Initialize(start_pos, index_diff / 3.0, 0.0,
110 constants.measured_absolute_position);
111
112 ContinuousAbsoluteEncoderZeroingEstimator estimator(constants);
113
114 // We tolerate a couple NANs before we start.
115 flatbuffers::FlatBufferBuilder fbb;
116 fbb.Finish(CreateAbsolutePosition(
117 fbb, 0.0, ::std::numeric_limits<double>::quiet_NaN()));
118 const auto sensor_values =
119 flatbuffers::GetRoot<AbsolutePosition>(fbb.GetBufferPointer());
120 for (size_t i = 0; i < kSampleSize - 1; ++i) {
121 estimator.UpdateEstimate(*sensor_values);
122 }
123
124 for (size_t i = 0; i < kSampleSize + kMovingBufferSize - 1; ++i) {
125 MoveTo(&sim, &estimator, start_pos);
126 ASSERT_FALSE(estimator.zeroed());
127 }
128
129 MoveTo(&sim, &estimator, start_pos);
130 ASSERT_TRUE(estimator.zeroed());
131 // Because the continuous estimator doesn't care about extra revolutions, it
132 // will have brought the offset down to less than index_diff.
133 EXPECT_NEAR(Wrap(0.0, start_pos, index_diff), estimator.offset(), 1e-12);
134}
135
136// Makes sure that using an absolute encoder doesn't let us zero while moving.
137TEST_F(ContinuousAbsoluteEncoderZeroingTest,
138 TestContinuousAbsoluteEncoderZeroingWithMovement) {
139 const double index_diff = 1.0;
140 PositionSensorSimulator sim(index_diff);
141
142 const double start_pos = 10 * index_diff;
143 double measured_absolute_position = 0.3 * index_diff;
144
145 ContinuousAbsoluteEncoderZeroingConstants constants{
146 kSampleSize, index_diff, measured_absolute_position,
147 0.1, kMovingBufferSize, kIndexErrorFraction};
148
149 sim.Initialize(start_pos, index_diff / 3.0, 0.0,
150 constants.measured_absolute_position);
151
152 ContinuousAbsoluteEncoderZeroingEstimator estimator(constants);
153
154 for (size_t i = 0; i < kSampleSize + kMovingBufferSize - 1; ++i) {
155 MoveTo(&sim, &estimator, start_pos + i * index_diff);
156 ASSERT_FALSE(estimator.zeroed());
157 }
158 MoveTo(&sim, &estimator, start_pos + 10 * index_diff);
159
160 MoveTo(&sim, &estimator, start_pos);
161 ASSERT_FALSE(estimator.zeroed());
162}
163
164// Makes sure we detect an error if the ZeroingEstimator gets sent a NaN.
165TEST_F(ContinuousAbsoluteEncoderZeroingTest,
166 TestContinuousAbsoluteEncoderZeroingWithNaN) {
167 ContinuousAbsoluteEncoderZeroingConstants constants{
168 kSampleSize, 1, 0.3, 0.1, kMovingBufferSize, kIndexErrorFraction};
169
170 ContinuousAbsoluteEncoderZeroingEstimator estimator(constants);
171
172 flatbuffers::FlatBufferBuilder fbb;
173 fbb.Finish(CreateAbsolutePosition(
174 fbb, 0.0, ::std::numeric_limits<double>::quiet_NaN()));
175 const auto sensor_values =
176 flatbuffers::GetRoot<AbsolutePosition>(fbb.GetBufferPointer());
177 for (size_t i = 0; i < kSampleSize - 1; ++i) {
178 estimator.UpdateEstimate(*sensor_values);
179 }
180 ASSERT_FALSE(estimator.error());
181
182 estimator.UpdateEstimate(*sensor_values);
183 ASSERT_TRUE(estimator.error());
184
185 flatbuffers::FlatBufferBuilder fbb2;
186 fbb2.Finish(estimator.GetEstimatorState(&fbb2));
187
188 const AbsoluteEncoderEstimatorState *state =
189 flatbuffers::GetRoot<AbsoluteEncoderEstimatorState>(
190 fbb2.GetBufferPointer());
191
192 EXPECT_THAT(*state->errors(),
193 ::testing::ElementsAre(ZeroingError::LOST_ABSOLUTE_ENCODER));
194}
195
196} // namespace testing
197} // namespace zeroing
198} // namespace frc971