blob: dc33698b3f13df24abb930105b75d246332c01aa [file] [log] [blame]
Sabina Davisadc58542019-02-01 22:23:00 -08001#include "frc971/wpilib/sensor_reader.h"
2
Sabina Davis399dbd82019-02-01 23:06:08 -08003#include <inttypes.h>
4#include <unistd.h>
5
Sabina Davisadc58542019-02-01 22:23:00 -08006#include "aos/init.h"
7#include "aos/util/compiler_memory_barrier.h"
Sabina Davis399dbd82019-02-01 23:06:08 -08008#include "aos/util/phased_loop.h"
Sabina Davisadc58542019-02-01 22:23:00 -08009#include "frc971/wpilib/ahal/DigitalInput.h"
10#include "frc971/wpilib/ahal/Utility.h"
Austin Schuh45a549f2019-02-02 15:43:56 -080011#include "frc971/wpilib/wpilib_interface.h"
Sabina Davisadc58542019-02-01 22:23:00 -080012
13namespace frc971 {
14namespace wpilib {
15
Austin Schuh45a549f2019-02-02 15:43:56 -080016SensorReader::SensorReader() {
17 // Set some defaults. We don't tend to exceed these, so old robots should
18 // just work with them.
19 UpdateFastEncoderFilterHz(500000);
20 UpdateMediumEncoderFilterHz(100000);
21}
22
23void SensorReader::UpdateFastEncoderFilterHz(int hz) {
24 fast_encoder_filter_.SetPeriodHz(::std::max(hz, 100000));
25}
26
27void SensorReader::UpdateMediumEncoderFilterHz(int hz) {
28 medium_encoder_filter_.SetPeriodHz(::std::max(hz, 50000));
29}
Sabina Davisadc58542019-02-01 22:23:00 -080030
Sabina Davisb6317b72019-02-01 22:53:23 -080031void SensorReader::set_drivetrain_left_encoder(
32 ::std::unique_ptr<frc::Encoder> encoder) {
33 fast_encoder_filter_.Add(encoder.get());
34 drivetrain_left_encoder_ = ::std::move(encoder);
Austin Schuh45a549f2019-02-02 15:43:56 -080035 drivetrain_left_encoder_->SetMaxPeriod(0.005);
Sabina Davisb6317b72019-02-01 22:53:23 -080036}
37
38void SensorReader::set_drivetrain_right_encoder(
39 ::std::unique_ptr<frc::Encoder> encoder) {
40 fast_encoder_filter_.Add(encoder.get());
41 drivetrain_right_encoder_ = ::std::move(encoder);
Austin Schuh45a549f2019-02-02 15:43:56 -080042 drivetrain_right_encoder_->SetMaxPeriod(0.005);
Sabina Davisb6317b72019-02-01 22:53:23 -080043}
44
45// All of the DMA-related set_* calls must be made before this, and it
46// doesn't hurt to do all of them.
Austin Schuh45a549f2019-02-02 15:43:56 -080047// TODO(austin): Does anyone actually do anything other than set this? Or can
48// we just take care of that automatically?
Sabina Davis1ffa4172019-02-01 22:38:33 -080049void SensorReader::set_dma(::std::unique_ptr<DMA> dma) {
50 dma_synchronizer_.reset(
51 new ::frc971::wpilib::DMASynchronizer(::std::move(dma)));
52}
53
Sabina Davisadc58542019-02-01 22:23:00 -080054void SensorReader::set_pwm_trigger(
55 ::std::unique_ptr<frc::DigitalInput> pwm_trigger) {
Austin Schuh2c2cc2e2019-02-02 20:19:45 -080056 fast_encoder_filter_.Add(pwm_trigger.get());
Sabina Davisadc58542019-02-01 22:23:00 -080057 pwm_trigger_ = ::std::move(pwm_trigger);
58}
59
60void SensorReader::RunPWMDetecter() {
61 ::aos::SetCurrentThreadRealtimePriority(41);
62
63 pwm_trigger_->RequestInterrupts();
64 // Rising edge only.
65 pwm_trigger_->SetUpSourceEdge(true, false);
66
67 monotonic_clock::time_point last_posedge_monotonic =
68 monotonic_clock::min_time;
69
70 while (run_) {
71 auto ret = pwm_trigger_->WaitForInterrupt(1.0, true);
72 if (ret == frc::InterruptableSensorBase::WaitResult::kRisingEdge) {
73 // Grab all the clocks.
74 const double pwm_fpga_time = pwm_trigger_->ReadRisingTimestamp();
75
76 aos_compiler_memory_barrier();
77 const double fpga_time_before = frc::GetFPGATime() * 1e-6;
78 aos_compiler_memory_barrier();
79 const monotonic_clock::time_point monotonic_now = monotonic_clock::now();
80 aos_compiler_memory_barrier();
81 const double fpga_time_after = frc::GetFPGATime() * 1e-6;
82 aos_compiler_memory_barrier();
83
84 const double fpga_offset =
85 (fpga_time_after + fpga_time_before) / 2.0 - pwm_fpga_time;
86
87 // Compute when the edge was.
88 const monotonic_clock::time_point monotonic_edge =
89 monotonic_now - chrono::duration_cast<chrono::nanoseconds>(
90 chrono::duration<double>(fpga_offset));
91
92 LOG(DEBUG, "Got PWM pulse %f spread, %f offset, %lld trigger\n",
93 fpga_time_after - fpga_time_before, fpga_offset,
94 monotonic_edge.time_since_epoch().count());
95
96 // Compute bounds on the timestep and sampling times.
97 const double fpga_sample_length = fpga_time_after - fpga_time_before;
98 const chrono::nanoseconds elapsed_time =
99 monotonic_edge - last_posedge_monotonic;
100
101 last_posedge_monotonic = monotonic_edge;
102
103 // Verify that the values are sane.
104 if (fpga_sample_length > 2e-5 || fpga_sample_length < 0) {
105 continue;
106 }
107 if (fpga_offset < 0 || fpga_offset > 0.00015) {
108 continue;
109 }
110 if (elapsed_time > chrono::microseconds(5050) + chrono::microseconds(4) ||
111 elapsed_time < chrono::microseconds(5050) - chrono::microseconds(4)) {
112 continue;
113 }
114 // Good edge!
115 {
116 ::std::unique_lock<::aos::stl_mutex> locker(tick_time_mutex_);
117 last_tick_time_monotonic_timepoint_ = last_posedge_monotonic;
118 last_period_ = elapsed_time;
119 }
120 } else {
121 LOG(INFO, "PWM triggered %d\n", ret);
122 }
123 }
124 pwm_trigger_->CancelInterrupts();
125}
126
Sabina Davis399dbd82019-02-01 23:06:08 -0800127void SensorReader::operator()() {
128 ::aos::SetCurrentThreadName("SensorReader");
129
Austin Schuh45a549f2019-02-02 15:43:56 -0800130 int32_t my_pid = getpid();
Sabina Davis399dbd82019-02-01 23:06:08 -0800131
Austin Schuh2c2cc2e2019-02-02 20:19:45 -0800132 Start();
Sabina Davis399dbd82019-02-01 23:06:08 -0800133 dma_synchronizer_->Start();
Austin Schuh2c2cc2e2019-02-02 20:19:45 -0800134
Austin Schuh45a549f2019-02-02 15:43:56 -0800135 if (pwm_trigger_) {
136 last_period_ = chrono::microseconds(5050);
137 LOG(INFO, "Using PWM trigger and a 5.05 ms period\n");
138 } else {
139 LOG(INFO, "Defaulting to open loop pwm synchronization\n");
140 last_period_ = chrono::microseconds(5000);
141 }
142 ::aos::time::PhasedLoop phased_loop(
143 last_period_,
144 pwm_trigger_ ? ::std::chrono::milliseconds(3) : chrono::milliseconds(4));
Sabina Davis399dbd82019-02-01 23:06:08 -0800145
Austin Schuh45a549f2019-02-02 15:43:56 -0800146 ::std::thread pwm_detecter_thread;
147 if (pwm_trigger_) {
148 pwm_detecter_thread =
149 ::std::thread(::std::bind(&SensorReader::RunPWMDetecter, this));
150 }
Sabina Davis399dbd82019-02-01 23:06:08 -0800151
152 ::aos::SetCurrentThreadRealtimePriority(40);
153 while (run_) {
154 {
155 const int iterations = phased_loop.SleepUntilNext();
156 if (iterations != 1) {
157 LOG(WARNING, "SensorReader skipped %d iterations\n", iterations - 1);
158 }
159 }
Austin Schuh45a549f2019-02-02 15:43:56 -0800160
161 ::frc971::wpilib::SendRobotState(my_pid);
Sabina Davis399dbd82019-02-01 23:06:08 -0800162 RunIteration();
Austin Schuh45a549f2019-02-02 15:43:56 -0800163 dma_synchronizer_->RunIteration();
Austin Schuh2c2cc2e2019-02-02 20:19:45 -0800164 RunDmaIteration();
Sabina Davis399dbd82019-02-01 23:06:08 -0800165
Austin Schuh45a549f2019-02-02 15:43:56 -0800166 if (pwm_trigger_) {
167 monotonic_clock::time_point last_tick_timepoint;
168 chrono::nanoseconds period;
169 {
170 ::std::unique_lock<::aos::stl_mutex> locker(tick_time_mutex_);
171 last_tick_timepoint = last_tick_time_monotonic_timepoint_;
172 period = last_period_;
173 }
174
175 if (last_tick_timepoint == monotonic_clock::min_time) {
176 continue;
177 }
178 chrono::nanoseconds new_offset = phased_loop.OffsetFromIntervalAndTime(
179 period, last_tick_timepoint + chrono::microseconds(2050));
180
181 // TODO(austin): If this is the first edge in a while, skip to it (plus
182 // an offset). Otherwise, slowly drift time to line up.
183
184 phased_loop.set_interval_and_offset(period, new_offset);
Sabina Davis399dbd82019-02-01 23:06:08 -0800185 }
Sabina Davis399dbd82019-02-01 23:06:08 -0800186 }
Austin Schuh2c2cc2e2019-02-02 20:19:45 -0800187
Austin Schuh45a549f2019-02-02 15:43:56 -0800188 if (pwm_trigger_) {
189 pwm_detecter_thread.join();
190 }
Sabina Davis399dbd82019-02-01 23:06:08 -0800191}
192
Sabina Davisadc58542019-02-01 22:23:00 -0800193} // namespace wpilib
194} // namespace frc971