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