blob: 6a6341531ce78ed3ee332cf42a43ca08410c38c6 [file] [log] [blame]
Brian Silvermana57b7012020-03-11 20:19:23 -07001#include "frc971/zeroing/pot_and_index.h"
2
3#include <cmath>
4
Austin Schuh99f7c6a2024-06-25 22:07:44 -07005#include "absl/log/check.h"
6#include "absl/log/log.h"
Brian Silvermana57b7012020-03-11 20:19:23 -07007
Stephan Pleinesf63bde82024-01-13 15:59:33 -08008namespace frc971::zeroing {
Brian Silvermana57b7012020-03-11 20:19:23 -07009
10PotAndIndexPulseZeroingEstimator::PotAndIndexPulseZeroingEstimator(
11 const constants::PotAndIndexPulseZeroingConstants &constants)
12 : constants_(constants) {
13 start_pos_samples_.reserve(constants_.average_filter_size);
14 Reset();
15}
16
17void PotAndIndexPulseZeroingEstimator::Reset() {
18 samples_idx_ = 0;
19 offset_ = 0;
20 start_pos_samples_.clear();
21 zeroed_ = false;
22 wait_for_index_pulse_ = true;
23 last_used_index_pulse_count_ = 0;
24 error_ = false;
25}
26
27void PotAndIndexPulseZeroingEstimator::TriggerError() {
28 if (!error_) {
29 VLOG(1) << "Manually triggered zeroing error.";
30 error_ = true;
31 }
32}
33
34double PotAndIndexPulseZeroingEstimator::CalculateStartPosition(
35 double start_average, double latched_encoder) const {
36 // We calculate an aproximation of the value of the last index position.
37 // Also account for index pulses not lining up with integer multiples of the
38 // index_diff.
39 double index_pos =
40 start_average + latched_encoder - constants_.measured_index_position;
41 // We round index_pos to the closest valid value of the index.
42 double accurate_index_pos = (round(index_pos / constants_.index_difference)) *
43 constants_.index_difference;
44 // Now we reverse the first calculation to get the accurate start position.
45 return accurate_index_pos - latched_encoder +
46 constants_.measured_index_position;
47}
48
49void PotAndIndexPulseZeroingEstimator::UpdateEstimate(
50 const PotAndIndexPosition &info) {
51 // We want to make sure that we encounter at least one index pulse while
52 // zeroing. So we take the index pulse count from the first sample after
53 // reset and wait for that count to change before we consider ourselves
54 // zeroed.
55 if (wait_for_index_pulse_) {
56 last_used_index_pulse_count_ = info.index_pulses();
57 wait_for_index_pulse_ = false;
58 }
59
60 if (start_pos_samples_.size() < constants_.average_filter_size) {
61 start_pos_samples_.push_back(info.pot() - info.encoder());
62 } else {
63 start_pos_samples_[samples_idx_] = info.pot() - info.encoder();
64 }
65
66 // Drop the oldest sample when we run this function the next time around.
67 samples_idx_ = (samples_idx_ + 1) % constants_.average_filter_size;
68
69 double sample_sum = 0.0;
70
71 for (size_t i = 0; i < start_pos_samples_.size(); ++i) {
72 sample_sum += start_pos_samples_[i];
73 }
74
75 // Calculates the average of the starting position.
76 double start_average = sample_sum / start_pos_samples_.size();
77
78 // If there are no index pulses to use or we don't have enough samples yet to
79 // have a well-filtered starting position then we use the filtered value as
80 // our best guess.
Philipp Schrader790cb542023-07-05 21:06:52 -070081 if (!zeroed_ && (info.index_pulses() == last_used_index_pulse_count_ ||
82 !offset_ready())) {
Brian Silvermana57b7012020-03-11 20:19:23 -070083 offset_ = start_average;
84 } else if (!zeroed_ || last_used_index_pulse_count_ != info.index_pulses()) {
85 // Note the accurate start position and the current index pulse count so
86 // that we only run this logic once per index pulse. That should be more
87 // resilient to corrupted intermediate data.
88 offset_ = CalculateStartPosition(start_average, info.latched_encoder());
89 last_used_index_pulse_count_ = info.index_pulses();
90
91 // TODO(austin): Reject encoder positions which have x% error rather than
92 // rounding to the closest index pulse.
93
94 // Save the first starting position.
95 if (!zeroed_) {
96 first_start_pos_ = offset_;
97 VLOG(2) << "latching start position" << first_start_pos_;
98 }
99
100 // Now that we have an accurate starting position we can consider ourselves
101 // zeroed.
102 zeroed_ = true;
103 // Throw an error if first_start_pos is bigger/smaller than
104 // constants_.allowable_encoder_error * index_diff + start_pos.
105 if (::std::abs(first_start_pos_ - offset_) >
106 constants_.allowable_encoder_error * constants_.index_difference) {
107 if (!error_) {
108 VLOG(1)
109 << "Encoder ticks out of range since last index pulse. first start "
110 "position: "
111 << first_start_pos_ << " recent starting position: " << offset_
112 << ", allowable error: "
113 << constants_.allowable_encoder_error * constants_.index_difference;
114 error_ = true;
115 }
116 }
117 }
118
119 position_ = offset_ + info.encoder();
120 filtered_position_ = start_average + info.encoder();
121}
122
123flatbuffers::Offset<PotAndIndexPulseZeroingEstimator::State>
124PotAndIndexPulseZeroingEstimator::GetEstimatorState(
125 flatbuffers::FlatBufferBuilder *fbb) const {
126 State::Builder builder(*fbb);
127 builder.add_error(error_);
128 builder.add_zeroed(zeroed_);
129 builder.add_position(position_);
130 builder.add_pot_position(filtered_position_);
131 return builder.Finish();
132}
133
Stephan Pleinesf63bde82024-01-13 15:59:33 -0800134} // namespace frc971::zeroing