blob: 4fe0236e17de75de5b3601006aa0aff1071659fa [file] [log] [blame]
Adam Snaiderc4b3c192015-02-01 01:30:39 +00001#include "frc971/zeroing/zeroing.h"
Adam Snaiderb4119252015-02-15 01:30:57 +00002
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -08003#include <algorithm>
Isaac Wilcove0851ffd2017-02-16 04:13:14 +00004#include <cmath>
5#include <limits>
6#include <vector>
Adam Snaiderc4b3c192015-02-01 01:30:39 +00007
Austin Schuh5f01f152017-02-11 21:34:08 -08008#include "frc971/zeroing/wrap.h"
9
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -080010
Adam Snaiderc4b3c192015-02-01 01:30:39 +000011namespace frc971 {
12namespace zeroing {
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -080013namespace {
14
15bool compare_encoder(const PotAndAbsolutePosition &left,
16 const PotAndAbsolutePosition &right) {
17 return left.encoder < right.encoder;
18}
19
20} // namespace
Adam Snaiderc4b3c192015-02-01 01:30:39 +000021
Tyler Chatowf8f03112017-02-05 14:31:34 -080022PotAndIndexPulseZeroingEstimator::PotAndIndexPulseZeroingEstimator(
Austin Schuh5f01f152017-02-11 21:34:08 -080023 const constants::PotAndIndexPulseZeroingConstants &constants)
24 : constants_(constants) {
25 start_pos_samples_.reserve(constants_.average_filter_size);
Adam Snaiderb4119252015-02-15 01:30:57 +000026 Reset();
Austin Schuh703b8d42015-02-01 14:56:34 -080027}
28
Isaac Wilcove0851ffd2017-02-16 04:13:14 +000029
Tyler Chatowf8f03112017-02-05 14:31:34 -080030void PotAndIndexPulseZeroingEstimator::Reset() {
Adam Snaiderc4b3c192015-02-01 01:30:39 +000031 samples_idx_ = 0;
Isaac Wilcove0851ffd2017-02-16 04:13:14 +000032 offset_ = 0;
Adam Snaiderb4119252015-02-15 01:30:57 +000033 start_pos_samples_.clear();
34 zeroed_ = false;
Philipp Schrader41d82912015-02-15 03:44:23 +000035 wait_for_index_pulse_ = true;
Philipp Schradere828be72015-02-15 07:07:37 +000036 last_used_index_pulse_count_ = 0;
Philipp Schrader53f4b6d2015-02-15 22:32:08 +000037 error_ = false;
38}
39
Tyler Chatowf8f03112017-02-05 14:31:34 -080040void PotAndIndexPulseZeroingEstimator::TriggerError() {
Philipp Schrader53f4b6d2015-02-15 22:32:08 +000041 if (!error_) {
42 LOG(ERROR, "Manually triggered zeroing error.\n");
43 error_ = true;
44 }
Philipp Schradere828be72015-02-15 07:07:37 +000045}
46
Tyler Chatowf8f03112017-02-05 14:31:34 -080047double PotAndIndexPulseZeroingEstimator::CalculateStartPosition(
48 double start_average, double latched_encoder) const {
Philipp Schradere828be72015-02-15 07:07:37 +000049 // We calculate an aproximation of the value of the last index position.
50 // Also account for index pulses not lining up with integer multiples of the
51 // index_diff.
Austin Schuh5f01f152017-02-11 21:34:08 -080052 double index_pos =
53 start_average + latched_encoder - constants_.measured_index_position;
Philipp Schradere828be72015-02-15 07:07:37 +000054 // We round index_pos to the closest valid value of the index.
Austin Schuh5f01f152017-02-11 21:34:08 -080055 double accurate_index_pos = (round(index_pos / constants_.index_difference)) *
56 constants_.index_difference;
Philipp Schradere828be72015-02-15 07:07:37 +000057 // Now we reverse the first calculation to get the accurate start position.
Austin Schuh5f01f152017-02-11 21:34:08 -080058 return accurate_index_pos - latched_encoder +
59 constants_.measured_index_position;
Adam Snaiderc4b3c192015-02-01 01:30:39 +000060}
61
Tyler Chatowf8f03112017-02-05 14:31:34 -080062void PotAndIndexPulseZeroingEstimator::UpdateEstimate(
63 const PotAndIndexPosition &info) {
Philipp Schrader41d82912015-02-15 03:44:23 +000064 // We want to make sure that we encounter at least one index pulse while
65 // zeroing. So we take the index pulse count from the first sample after
66 // reset and wait for that count to change before we consider ourselves
67 // zeroed.
68 if (wait_for_index_pulse_) {
Philipp Schradere828be72015-02-15 07:07:37 +000069 last_used_index_pulse_count_ = info.index_pulses;
Philipp Schrader41d82912015-02-15 03:44:23 +000070 wait_for_index_pulse_ = false;
71 }
72
Austin Schuh5f01f152017-02-11 21:34:08 -080073 if (start_pos_samples_.size() < constants_.average_filter_size) {
Adam Snaiderc4b3c192015-02-01 01:30:39 +000074 start_pos_samples_.push_back(info.pot - info.encoder);
75 } else {
76 start_pos_samples_[samples_idx_] = info.pot - info.encoder;
77 }
Adam Snaiderb4119252015-02-15 01:30:57 +000078
79 // Drop the oldest sample when we run this function the next time around.
Austin Schuh5f01f152017-02-11 21:34:08 -080080 samples_idx_ = (samples_idx_ + 1) % constants_.average_filter_size;
Adam Snaiderc4b3c192015-02-01 01:30:39 +000081
Adam Snaiderb4119252015-02-15 01:30:57 +000082 double sample_sum = 0.0;
83
Adam Snaiderc4b3c192015-02-01 01:30:39 +000084 for (size_t i = 0; i < start_pos_samples_.size(); ++i) {
Adam Snaiderb4119252015-02-15 01:30:57 +000085 sample_sum += start_pos_samples_[i];
Adam Snaiderc4b3c192015-02-01 01:30:39 +000086 }
87
88 // Calculates the average of the starting position.
Adam Snaiderb4119252015-02-15 01:30:57 +000089 double start_average = sample_sum / start_pos_samples_.size();
90
91 // If there are no index pulses to use or we don't have enough samples yet to
92 // have a well-filtered starting position then we use the filtered value as
93 // our best guess.
Austin Schuh7485dbb2016-02-08 00:21:58 -080094 if (!zeroed_ &&
95 (info.index_pulses == last_used_index_pulse_count_ || !offset_ready())) {
Isaac Wilcove0851ffd2017-02-16 04:13:14 +000096 offset_ = start_average;
Philipp Schradere828be72015-02-15 07:07:37 +000097 } else if (!zeroed_ || last_used_index_pulse_count_ != info.index_pulses) {
98 // Note the accurate start position and the current index pulse count so
99 // that we only run this logic once per index pulse. That should be more
100 // resilient to corrupted intermediate data.
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000101 offset_ = CalculateStartPosition(start_average, info.latched_encoder);
Philipp Schradere828be72015-02-15 07:07:37 +0000102 last_used_index_pulse_count_ = info.index_pulses;
Austin Schuh7485dbb2016-02-08 00:21:58 -0800103
104 // TODO(austin): Reject encoder positions which have x% error rather than
105 // rounding to the closest index pulse.
106
Adam Snaider3cd11c52015-02-16 02:16:09 +0000107 // Save the first starting position.
108 if (!zeroed_) {
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000109 first_start_pos_ = offset_;
Adam Snaider3cd11c52015-02-16 02:16:09 +0000110 LOG(INFO, "latching start position %f\n", first_start_pos_);
111 }
Adam Snaiderb4119252015-02-15 01:30:57 +0000112
113 // Now that we have an accurate starting position we can consider ourselves
114 // zeroed.
Austin Schuh703b8d42015-02-01 14:56:34 -0800115 zeroed_ = true;
Adam Snaider3cd11c52015-02-16 02:16:09 +0000116 // Throw an error if first_start_pos is bigger/smaller than
Austin Schuh5f01f152017-02-11 21:34:08 -0800117 // constants_.allowable_encoder_error * index_diff + start_pos.
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000118 if (::std::abs(first_start_pos_ - offset_) >
Austin Schuh5f01f152017-02-11 21:34:08 -0800119 constants_.allowable_encoder_error * constants_.index_difference) {
Adam Snaider3cd11c52015-02-16 02:16:09 +0000120 if (!error_) {
121 LOG(ERROR,
122 "Encoder ticks out of range since last index pulse. first start "
Austin Schuh1c85bc82016-04-03 21:36:31 -0700123 "position: %f recent starting position: %f, allowable error: %f\n",
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000124 first_start_pos_, offset_,
Austin Schuh5f01f152017-02-11 21:34:08 -0800125 constants_.allowable_encoder_error * constants_.index_difference);
Adam Snaider3cd11c52015-02-16 02:16:09 +0000126 error_ = true;
127 }
128 }
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000129 }
Adam Snaiderb4119252015-02-15 01:30:57 +0000130
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000131 position_ = offset_ + info.encoder;
Austin Schuhbe133ed2016-03-11 21:23:34 -0800132 filtered_position_ = start_average + info.encoder;
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000133}
134
Brian Silverman4f2e2ce2017-02-19 17:49:47 -0800135PotAndIndexPulseZeroingEstimator::State
136PotAndIndexPulseZeroingEstimator::GetEstimatorState() const {
137 State r;
138 r.error = error_;
139 r.zeroed = zeroed_;
140 r.position = position_;
141 r.pot_position = filtered_position_;
142 return r;
143}
144
Austin Schuh55934032017-03-11 12:45:27 -0800145HallEffectAndPositionZeroingEstimator::HallEffectAndPositionZeroingEstimator(
146 const ZeroingConstants &constants)
147 : constants_(constants) {
148 Reset();
149}
150
151void HallEffectAndPositionZeroingEstimator::Reset() {
152 offset_ = 0.0;
153 min_low_position_ = ::std::numeric_limits<double>::max();
154 max_low_position_ = ::std::numeric_limits<double>::lowest();
155 zeroed_ = false;
156 initialized_ = false;
157 last_used_posedge_count_ = 0;
158 cycles_high_ = 0;
159 high_long_enough_ = false;
160 first_start_pos_ = 0.0;
161 error_ = false;
162 current_ = 0.0;
163 first_start_pos_ = 0.0;
164}
165
166void HallEffectAndPositionZeroingEstimator::TriggerError() {
167 if (!error_) {
168 LOG(ERROR, "Manually triggered zeroing error.\n");
169 error_ = true;
170 }
171}
172
173void HallEffectAndPositionZeroingEstimator::StoreEncoderMaxAndMin(
174 const HallEffectAndPosition &info) {
175 // If we have a new posedge.
176 if (!info.current) {
177 if (last_hall_) {
178 min_low_position_ = max_low_position_ = info.position;
179 } else {
180 min_low_position_ = ::std::min(min_low_position_, info.position);
181 max_low_position_ = ::std::max(max_low_position_, info.position);
182 }
183 }
184 last_hall_ = info.current;
185}
186
187void HallEffectAndPositionZeroingEstimator::UpdateEstimate(
188 const HallEffectAndPosition &info) {
189 // We want to make sure that we encounter at least one posedge while zeroing.
190 // So we take the posedge count from the first sample after reset and wait for
191 // that count to change and for the hall effect to stay high before we
192 // consider ourselves zeroed.
193 if (!initialized_) {
194 last_used_posedge_count_ = info.posedge_count;
195 initialized_ = true;
196 last_hall_ = info.current;
197 }
198
199 StoreEncoderMaxAndMin(info);
200
201 if (info.current) {
202 cycles_high_++;
203 } else {
204 cycles_high_ = 0;
205 last_used_posedge_count_ = info.posedge_count;
206 }
207
208 high_long_enough_ = cycles_high_ >= constants_.hall_trigger_zeroing_length;
209
210 bool moving_backward = false;
211 if (constants_.zeroing_move_direction) {
212 moving_backward = info.position > min_low_position_;
213 } else {
214 moving_backward = info.position < max_low_position_;
215 }
216
217 // If there are no posedges to use or we don't have enough samples yet to
218 // have a well-filtered starting position then we use the filtered value as
219 // our best guess.
220 if (last_used_posedge_count_ != info.posedge_count && high_long_enough_ &&
221 moving_backward) {
222 // Note the offset and the current posedge count so that we only run this
223 // logic once per posedge. That should be more resilient to corrupted
224 // intermediate data.
225 offset_ = -info.posedge_value;
226 if (constants_.zeroing_move_direction) {
227 offset_ += constants_.lower_hall_position;
228 } else {
229 offset_ += constants_.upper_hall_position;
230 }
231 last_used_posedge_count_ = info.posedge_count;
232
233 // Save the first starting position.
234 if (!zeroed_) {
235 first_start_pos_ = offset_;
236 LOG(INFO, "latching start position %f\n", first_start_pos_);
237 }
238
239 // Now that we have an accurate starting position we can consider ourselves
240 // zeroed.
241 zeroed_ = true;
242 }
243
244 position_ = info.position - offset_;
245}
246
247HallEffectAndPositionZeroingEstimator::State
248HallEffectAndPositionZeroingEstimator::GetEstimatorState() const {
249 State r;
250 r.error = error_;
251 r.zeroed = zeroed_;
252 r.encoder = position_;
253 r.high_long_enough = high_long_enough_;
254 r.offset = offset_;
255 return r;
256}
Brian Silverman4f2e2ce2017-02-19 17:49:47 -0800257
Austin Schuh5f01f152017-02-11 21:34:08 -0800258PotAndAbsEncoderZeroingEstimator::PotAndAbsEncoderZeroingEstimator(
259 const constants::PotAndAbsoluteEncoderZeroingConstants &constants)
260 : constants_(constants) {
261 relative_to_absolute_offset_samples_.reserve(constants_.average_filter_size);
262 offset_samples_.reserve(constants_.average_filter_size);
263 Reset();
264}
265
266void PotAndAbsEncoderZeroingEstimator::Reset() {
267 zeroed_ = false;
268 relative_to_absolute_offset_samples_.clear();
269 offset_samples_.clear();
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800270 buffered_samples_.clear();
Brian Silvermana10d20a2017-02-19 14:28:53 -0800271 error_ = false;
Austin Schuh5f01f152017-02-11 21:34:08 -0800272}
273
274// So, this needs to be a multistep process. We need to first estimate the
275// offset between the absolute encoder and the relative encoder. That process
276// should get us an absolute number which is off by integer multiples of the
277// distance/rev. In parallel, we can estimate the offset between the pot and
278// encoder. When both estimates have converged, we can then compute the offset
279// in a cycle, and which cycle, which gives us the accurate global offset.
280//
281// It's tricky to compute the offset between the absolute and relative encoder.
282// We need to compute this inside 1 revolution. The easiest way to do this
283// would be to wrap the encoder, subtract the two of them, and then average the
284// result. That will struggle when they are off by PI. Instead, we need to
285// wrap the number to +- PI from the current averaged offset.
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800286//
287// To guard against the robot moving while updating estimates, buffer a number
288// of samples and check that the buffered samples are not different than the
289// zeroing threshold. At any point that the samples differ too much, do not
290// update estimates based on those samples.
Austin Schuh5f01f152017-02-11 21:34:08 -0800291void PotAndAbsEncoderZeroingEstimator::UpdateEstimate(
292 const PotAndAbsolutePosition &info) {
Neil Balch16275e32017-02-18 16:38:45 -0800293 // Check for Abs Encoder NaN value that would mess up the rest of the zeroing
294 // code below. NaN values are given when the Absolute Encoder is disconnected.
295 if (::std::isnan(info.absolute_encoder)) {
296 error_ = true;
297 return;
298 }
299
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800300 bool moving = true;
301 if (buffered_samples_.size() < constants_.moving_buffer_size) {
302 // Not enough samples to start determining if the robot is moving or not,
303 // don't use the samples yet.
304 buffered_samples_.push_back(info);
Austin Schuh5f01f152017-02-11 21:34:08 -0800305 } else {
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800306 // Have enough samples to start determining if the robot is moving or not.
307 buffered_samples_[buffered_samples_idx_] = info;
308 auto max_value =
309 ::std::max_element(buffered_samples_.begin(), buffered_samples_.end(),
310 compare_encoder)
311 ->encoder;
312 auto min_value =
313 ::std::min_element(buffered_samples_.begin(), buffered_samples_.end(),
314 compare_encoder)
315 ->encoder;
316 if (::std::abs(max_value - min_value) < constants_.zeroing_threshold) {
317 // Robot isn't moving, use middle sample to determine offsets.
318 moving = false;
319 }
320 }
321 buffered_samples_idx_ =
322 (buffered_samples_idx_ + 1) % constants_.moving_buffer_size;
323
324 if (!moving) {
325 // The robot is not moving, use the middle sample to determine offsets.
326 const int middle_index =
327 (buffered_samples_idx_ + (constants_.moving_buffer_size - 1) / 2) %
328 constants_.moving_buffer_size;
329
330 // Compute the sum of all the offset samples.
331 double relative_to_absolute_offset_sum = 0.0;
332 for (size_t i = 0; i < relative_to_absolute_offset_samples_.size(); ++i) {
333 relative_to_absolute_offset_sum +=
334 relative_to_absolute_offset_samples_[i];
335 }
336
337 // Compute the average offset between the absolute encoder and relative
338 // encoder. If we have 0 samples, assume it is 0.
339 double average_relative_to_absolute_offset =
340 relative_to_absolute_offset_samples_.size() == 0
341 ? 0.0
342 : relative_to_absolute_offset_sum /
343 relative_to_absolute_offset_samples_.size();
344
Austin Schuh0e1c2c62017-02-21 02:03:25 -0800345 const double adjusted_incremental_encoder =
346 buffered_samples_[middle_index].encoder +
347 average_relative_to_absolute_offset;
348
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800349 // Now, compute the nearest absolute encoder value to the offset relative
350 // encoder position.
351 const double adjusted_absolute_encoder =
Austin Schuh0e1c2c62017-02-21 02:03:25 -0800352 Wrap(adjusted_incremental_encoder,
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800353 buffered_samples_[middle_index].absolute_encoder -
354 constants_.measured_absolute_position,
355 constants_.one_revolution_distance);
356
Austin Schuh0e1c2c62017-02-21 02:03:25 -0800357 // Reverse the math on the previous line to compute the absolute encoder.
358 // Do this by taking the adjusted encoder, and then subtracting off the
359 // second argument above, and the value that was added by Wrap.
360 filtered_absolute_encoder_ =
361 ((buffered_samples_[middle_index].encoder +
362 average_relative_to_absolute_offset) -
363 (-constants_.measured_absolute_position +
364 (adjusted_absolute_encoder -
365 (buffered_samples_[middle_index].absolute_encoder -
366 constants_.measured_absolute_position))));
367
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800368 const double relative_to_absolute_offset =
369 adjusted_absolute_encoder - buffered_samples_[middle_index].encoder;
370
371 // Add the sample and update the average with the new reading.
372 const size_t relative_to_absolute_offset_samples_size =
373 relative_to_absolute_offset_samples_.size();
374 if (relative_to_absolute_offset_samples_size <
375 constants_.average_filter_size) {
376 average_relative_to_absolute_offset =
377 (average_relative_to_absolute_offset *
378 relative_to_absolute_offset_samples_size +
379 relative_to_absolute_offset) /
380 (relative_to_absolute_offset_samples_size + 1);
381
382 relative_to_absolute_offset_samples_.push_back(
383 relative_to_absolute_offset);
384 } else {
385 average_relative_to_absolute_offset -=
386 relative_to_absolute_offset_samples_[samples_idx_] /
387 relative_to_absolute_offset_samples_size;
388 relative_to_absolute_offset_samples_[samples_idx_] =
389 relative_to_absolute_offset;
390 average_relative_to_absolute_offset +=
391 relative_to_absolute_offset /
392 relative_to_absolute_offset_samples_size;
393 }
394
395 // Now compute the offset between the pot and relative encoder.
396 if (offset_samples_.size() < constants_.average_filter_size) {
397 offset_samples_.push_back(buffered_samples_[middle_index].pot -
398 buffered_samples_[middle_index].encoder);
399 } else {
400 offset_samples_[samples_idx_] = buffered_samples_[middle_index].pot -
401 buffered_samples_[middle_index].encoder;
402 }
403
404 // Drop the oldest sample when we run this function the next time around.
405 samples_idx_ = (samples_idx_ + 1) % constants_.average_filter_size;
406
407 double pot_relative_encoder_offset_sum = 0.0;
408 for (size_t i = 0; i < offset_samples_.size(); ++i) {
409 pot_relative_encoder_offset_sum += offset_samples_[i];
410 }
411 pot_relative_encoder_offset_ =
412 pot_relative_encoder_offset_sum / offset_samples_.size();
413
414 offset_ = Wrap(buffered_samples_[middle_index].encoder +
415 pot_relative_encoder_offset_,
416 average_relative_to_absolute_offset +
417 buffered_samples_[middle_index].encoder,
418 constants_.one_revolution_distance) -
419 buffered_samples_[middle_index].encoder;
420 if (offset_ready()) {
Brian Silvermana10d20a2017-02-19 14:28:53 -0800421 if (!zeroed_) {
422 first_offset_ = offset_;
423 }
424
425 if (::std::abs(first_offset_ - offset_) >
426 constants_.allowable_encoder_error *
427 constants_.one_revolution_distance) {
428 LOG(ERROR,
429 "Offset moved too far. Initial: %f, current %f, allowable change: "
430 "%f\n",
431 first_offset_, offset_, constants_.allowable_encoder_error *
432 constants_.one_revolution_distance);
433 error_ = true;
434 }
435
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800436 zeroed_ = true;
437 }
Austin Schuh5f01f152017-02-11 21:34:08 -0800438 }
439
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800440 // Update the position.
441 filtered_position_ = pot_relative_encoder_offset_ + info.encoder;
Austin Schuh5f01f152017-02-11 21:34:08 -0800442 position_ = offset_ + info.encoder;
443}
444
Brian Silverman4f2e2ce2017-02-19 17:49:47 -0800445PotAndAbsEncoderZeroingEstimator::State
446PotAndAbsEncoderZeroingEstimator::GetEstimatorState() const {
447 State r;
448 r.error = error_;
449 r.zeroed = zeroed_;
450 r.position = position_;
451 r.pot_position = filtered_position_;
Austin Schuh0e1c2c62017-02-21 02:03:25 -0800452 r.absolute_position = filtered_absolute_encoder_;
Brian Silverman4f2e2ce2017-02-19 17:49:47 -0800453 return r;
454}
455
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000456void PulseIndexZeroingEstimator::Reset() {
457 max_index_position_ = ::std::numeric_limits<double>::lowest();
458 min_index_position_ = ::std::numeric_limits<double>::max();
459 offset_ = 0;
460 last_used_index_pulse_count_ = 0;
461 zeroed_ = false;
462 error_ = false;
463}
464
465void PulseIndexZeroingEstimator::StoreIndexPulseMaxAndMin(
466 const IndexPosition &info) {
467 // If we have a new index pulse.
468 if (last_used_index_pulse_count_ != info.index_pulses) {
469 // If the latest pulses's position is outside the range we've currently
470 // seen, record it appropriately.
471 if (info.latched_encoder > max_index_position_) {
472 max_index_position_ = info.latched_encoder;
473 }
474 if (info.latched_encoder < min_index_position_) {
475 min_index_position_ = info.latched_encoder;
476 }
477 last_used_index_pulse_count_ = info.index_pulses;
478 }
479}
480
Brian Silvermanf37839c2017-02-19 18:07:15 -0800481int PulseIndexZeroingEstimator::IndexPulseCount() const {
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000482 if (min_index_position_ > max_index_position_) {
483 // This condition means we haven't seen a pulse yet.
484 return 0;
485 }
486
487 // Calculate the number of pulses encountered so far.
488 return 1 + static_cast<int>(
489 ::std::round((max_index_position_ - min_index_position_) /
490 constants_.index_difference));
491}
492
493void PulseIndexZeroingEstimator::UpdateEstimate(const IndexPosition &info) {
494 StoreIndexPulseMaxAndMin(info);
495 const int index_pulse_count = IndexPulseCount();
496 if (index_pulse_count > constants_.index_pulse_count) {
497 error_ = true;
498 }
499
500 // TODO(austin): Detect if the encoder or index pulse is unplugged.
501 // TODO(austin): Detect missing counts.
502
503 if (index_pulse_count == constants_.index_pulse_count && !zeroed_) {
504 offset_ = constants_.measured_index_position -
505 constants_.known_index_pulse * constants_.index_difference -
506 min_index_position_;
507 zeroed_ = true;
508 }
Brian Silvermanf37839c2017-02-19 18:07:15 -0800509
510 position_ = info.encoder + offset_;
511}
512
513PulseIndexZeroingEstimator::State
514PulseIndexZeroingEstimator::GetEstimatorState() const {
515 State r;
516 r.error = error_;
517 r.zeroed = zeroed_;
518 r.position = position_;
519 r.min_index_position = min_index_position_;
520 r.max_index_position = max_index_position_;
521 r.index_pulses_seen = IndexPulseCount();
522 return r;
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000523}
524
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000525} // namespace zeroing
526} // namespace frc971