blob: 7c5a499c1b0246e100e702ff08b257a072a427ff [file] [log] [blame]
Brian Silverman26e4e522015-12-17 01:56:40 -05001/*----------------------------------------------------------------------------*/
Brian Silverman1a675112016-02-20 20:42:49 -05002/* Copyright (c) FIRST 2008-2016. All Rights Reserved. */
Brian Silverman26e4e522015-12-17 01:56:40 -05003/* Open Source Software - may be modified and shared by FRC teams. The code */
Brian Silverman1a675112016-02-20 20:42:49 -05004/* must be accompanied by the FIRST BSD license file in the root directory of */
5/* the project. */
Brian Silverman26e4e522015-12-17 01:56:40 -05006/*----------------------------------------------------------------------------*/
7
8#include "Encoder.h"
9#include "DigitalInput.h"
10#include "Resource.h"
11#include "WPIErrors.h"
12#include "LiveWindow/LiveWindow.h"
13
14/**
15 * Common initialization code for Encoders.
16 * This code allocates resources for Encoders and is common to all constructors.
17 *
18 * The counter will start counting immediately.
19 *
20 * @param reverseDirection If true, counts down instead of up (this is all
21 * relative)
22 * @param encodingType either k1X, k2X, or k4X to indicate 1X, 2X or 4X
23 * decoding. If 4X is
24 * selected, then an encoder FPGA object is used and the returned counts will be
25 * 4x the encoder
26 * spec'd value since all rising and falling edges are counted. If 1X or 2X are
27 * selected then
28 * a counter object will be used and the returned value will either exactly
29 * match the spec'd count
30 * or be double (2x) the spec'd count.
31 */
32void Encoder::InitEncoder(bool reverseDirection, EncodingType encodingType) {
33 m_encodingType = encodingType;
34 switch (encodingType) {
35 case k4X: {
36 m_encodingScale = 4;
37 if (m_aSource->StatusIsFatal()) {
38 CloneError(*m_aSource);
39 return;
40 }
41 if (m_bSource->StatusIsFatal()) {
42 CloneError(*m_bSource);
43 return;
44 }
45 int32_t status = 0;
46 m_encoder = initializeEncoder(
47 m_aSource->GetModuleForRouting(), m_aSource->GetChannelForRouting(),
48 m_aSource->GetAnalogTriggerForRouting(),
49 m_bSource->GetModuleForRouting(), m_bSource->GetChannelForRouting(),
50 m_bSource->GetAnalogTriggerForRouting(), reverseDirection, &m_index,
51 &status);
52 wpi_setErrorWithContext(status, getHALErrorMessage(status));
53 m_counter = nullptr;
54 SetMaxPeriod(.5);
55 break;
56 }
57 case k1X:
58 case k2X: {
59 m_encodingScale = encodingType == k1X ? 1 : 2;
60 m_counter = std::make_unique<Counter>(m_encodingType, m_aSource,
61 m_bSource, reverseDirection);
62 m_index = m_counter->GetFPGAIndex();
63 break;
64 }
65 default:
66 wpi_setErrorWithContext(-1, "Invalid encodingType argument");
67 break;
68 }
69
70 HALReport(HALUsageReporting::kResourceType_Encoder, m_index, encodingType);
71 LiveWindow::GetInstance()->AddSensor("Encoder",
72 m_aSource->GetChannelForRouting(), this);
73}
74
75/**
76 * Encoder constructor.
77 * Construct a Encoder given a and b channels.
78 *
79 * The counter will start counting immediately.
80 *
81 * @param aChannel The a channel DIO channel. 0-9 are on-board, 10-25 are on the
82 * MXP port
83 * @param bChannel The b channel DIO channel. 0-9 are on-board, 10-25 are on the
84 * MXP port
85 * @param reverseDirection represents the orientation of the encoder and inverts
86 * the output values
87 * if necessary so forward represents positive values.
88 * @param encodingType either k1X, k2X, or k4X to indicate 1X, 2X or 4X
89 * decoding. If 4X is
90 * selected, then an encoder FPGA object is used and the returned counts will be
91 * 4x the encoder
92 * spec'd value since all rising and falling edges are counted. If 1X or 2X are
93 * selected then
94 * a counter object will be used and the returned value will either exactly
95 * match the spec'd count
96 * or be double (2x) the spec'd count.
97 */
98Encoder::Encoder(uint32_t aChannel, uint32_t bChannel, bool reverseDirection,
99 EncodingType encodingType) {
100 m_aSource = std::make_shared<DigitalInput>(aChannel);
101 m_bSource = std::make_shared<DigitalInput>(bChannel);
102 InitEncoder(reverseDirection, encodingType);
103}
104
105/**
106 * Encoder constructor.
107 * Construct a Encoder given a and b channels as digital inputs. This is used in
108 * the case where the digital inputs are shared. The Encoder class will not
109 * allocate the digital inputs and assume that they already are counted.
110 * The counter will start counting immediately.
111 *
112 * @param aSource The source that should be used for the a channel.
113 * @param bSource the source that should be used for the b channel.
114 * @param reverseDirection represents the orientation of the encoder and inverts
115 * the output values
116 * if necessary so forward represents positive values.
117 * @param encodingType either k1X, k2X, or k4X to indicate 1X, 2X or 4X
118 * decoding. If 4X is
119 * selected, then an encoder FPGA object is used and the returned counts will be
120 * 4x the encoder
121 * spec'd value since all rising and falling edges are counted. If 1X or 2X are
122 * selected then
123 * a counter object will be used and the returned value will either exactly
124 * match the spec'd count
125 * or be double (2x) the spec'd count.
126 */
127Encoder::Encoder(DigitalSource *aSource, DigitalSource *bSource,
128 bool reverseDirection, EncodingType encodingType)
129 : m_aSource(aSource, NullDeleter<DigitalSource>()),
130 m_bSource(bSource, NullDeleter<DigitalSource>()) {
131 if (m_aSource == nullptr || m_bSource == nullptr)
132 wpi_setWPIError(NullParameter);
133 else
134 InitEncoder(reverseDirection, encodingType);
135}
136
137Encoder::Encoder(std::shared_ptr<DigitalSource> aSource,
138 std::shared_ptr<DigitalSource> bSource,
139 bool reverseDirection, EncodingType encodingType)
140 : m_aSource(aSource), m_bSource(bSource) {
141 if (m_aSource == nullptr || m_bSource == nullptr)
142 wpi_setWPIError(NullParameter);
143 else
144 InitEncoder(reverseDirection, encodingType);
145}
146
147/**
148 * Encoder constructor.
149 * Construct a Encoder given a and b channels as digital inputs. This is used in
150 * the case
151 * where the digital inputs are shared. The Encoder class will not allocate the
152 * digital inputs
153 * and assume that they already are counted.
154 *
155 * The counter will start counting immediately.
156 *
157 * @param aSource The source that should be used for the a channel.
158 * @param bSource the source that should be used for the b channel.
159 * @param reverseDirection represents the orientation of the encoder and inverts
160 * the output values
161 * if necessary so forward represents positive values.
162 * @param encodingType either k1X, k2X, or k4X to indicate 1X, 2X or 4X
163 * decoding. If 4X is
164 * selected, then an encoder FPGA object is used and the returned counts will be
165 * 4x the encoder
166 * spec'd value since all rising and falling edges are counted. If 1X or 2X are
167 * selected then
168 * a counter object will be used and the returned value will either exactly
169 * match the spec'd count
170 * or be double (2x) the spec'd count.
171 */
172Encoder::Encoder(DigitalSource &aSource, DigitalSource &bSource,
173 bool reverseDirection, EncodingType encodingType)
174 : m_aSource(&aSource, NullDeleter<DigitalSource>()),
175 m_bSource(&bSource, NullDeleter<DigitalSource>())
176{
177 InitEncoder(reverseDirection, encodingType);
178}
179
180/**
181 * Free the resources for an Encoder.
182 * Frees the FPGA resources associated with an Encoder.
183 */
184Encoder::~Encoder() {
185 if (!m_counter) {
186 int32_t status = 0;
187 freeEncoder(m_encoder, &status);
188 wpi_setErrorWithContext(status, getHALErrorMessage(status));
189 }
190}
191
192/**
193 * The encoding scale factor 1x, 2x, or 4x, per the requested encodingType.
194 * Used to divide raw edge counts down to spec'd counts.
195 */
196int32_t Encoder::GetEncodingScale() const { return m_encodingScale; }
197
198/**
199 * Gets the raw value from the encoder.
200 * The raw value is the actual count unscaled by the 1x, 2x, or 4x scale
201 * factor.
202 * @return Current raw count from the encoder
203 */
204int32_t Encoder::GetRaw() const {
205 if (StatusIsFatal()) return 0;
206 int32_t value;
207 if (m_counter)
208 value = m_counter->Get();
209 else {
210 int32_t status = 0;
211 value = getEncoder(m_encoder, &status);
212 wpi_setErrorWithContext(status, getHALErrorMessage(status));
213 }
214 return value;
215}
216
217/**
218 * Gets the current count.
219 * Returns the current count on the Encoder.
220 * This method compensates for the decoding type.
221 *
222 * @return Current count from the Encoder adjusted for the 1x, 2x, or 4x scale
223 * factor.
224 */
225int32_t Encoder::Get() const {
226 if (StatusIsFatal()) return 0;
227 return (int32_t)(GetRaw() * DecodingScaleFactor());
228}
229
230/**
231 * Reset the Encoder distance to zero.
232 * Resets the current count to zero on the encoder.
233 */
234void Encoder::Reset() {
235 if (StatusIsFatal()) return;
236 if (m_counter)
237 m_counter->Reset();
238 else {
239 int32_t status = 0;
240 resetEncoder(m_encoder, &status);
241 wpi_setErrorWithContext(status, getHALErrorMessage(status));
242 }
243}
244
245/**
246 * Returns the period of the most recent pulse.
247 * Returns the period of the most recent Encoder pulse in seconds.
248 * This method compensates for the decoding type.
249 *
250 * @deprecated Use GetRate() in favor of this method. This returns unscaled
251 * periods and GetRate() scales using value from SetDistancePerPulse().
252 *
253 * @return Period in seconds of the most recent pulse.
254 */
255double Encoder::GetPeriod() const {
256 if (StatusIsFatal()) return 0.0;
257 if (m_counter) {
258 return m_counter->GetPeriod() / DecodingScaleFactor();
259 } else {
260 int32_t status = 0;
261 double period = getEncoderPeriod(m_encoder, &status);
262 wpi_setErrorWithContext(status, getHALErrorMessage(status));
263 return period;
264 }
265}
266
267/**
268 * Sets the maximum period for stopped detection.
269 * Sets the value that represents the maximum period of the Encoder before it
270 * will assume
271 * that the attached device is stopped. This timeout allows users to determine
272 * if the wheels or
273 * other shaft has stopped rotating.
274 * This method compensates for the decoding type.
275 *
276 * @deprecated Use SetMinRate() in favor of this method. This takes unscaled
277 * periods and SetMinRate() scales using value from SetDistancePerPulse().
278 *
279 * @param maxPeriod The maximum time between rising and falling edges before the
280 * FPGA will
281 * report the device stopped. This is expressed in seconds.
282 */
283void Encoder::SetMaxPeriod(double maxPeriod) {
284 if (StatusIsFatal()) return;
285 if (m_counter) {
286 m_counter->SetMaxPeriod(maxPeriod * DecodingScaleFactor());
287 } else {
288 int32_t status = 0;
289 setEncoderMaxPeriod(m_encoder, maxPeriod, &status);
290 wpi_setErrorWithContext(status, getHALErrorMessage(status));
291 }
292}
293
294/**
295 * Determine if the encoder is stopped.
296 * Using the MaxPeriod value, a boolean is returned that is true if the encoder
297 * is considered
298 * stopped and false if it is still moving. A stopped encoder is one where the
299 * most recent pulse
300 * width exceeds the MaxPeriod.
301 * @return True if the encoder is considered stopped.
302 */
303bool Encoder::GetStopped() const {
304 if (StatusIsFatal()) return true;
305 if (m_counter) {
306 return m_counter->GetStopped();
307 } else {
308 int32_t status = 0;
309 bool value = getEncoderStopped(m_encoder, &status);
310 wpi_setErrorWithContext(status, getHALErrorMessage(status));
311 return value;
312 }
313}
314
315/**
316 * The last direction the encoder value changed.
317 * @return The last direction the encoder value changed.
318 */
319bool Encoder::GetDirection() const {
320 if (StatusIsFatal()) return false;
321 if (m_counter) {
322 return m_counter->GetDirection();
323 } else {
324 int32_t status = 0;
325 bool value = getEncoderDirection(m_encoder, &status);
326 wpi_setErrorWithContext(status, getHALErrorMessage(status));
327 return value;
328 }
329}
330
331/**
332 * The scale needed to convert a raw counter value into a number of encoder
333 * pulses.
334 */
335double Encoder::DecodingScaleFactor() const {
336 if (StatusIsFatal()) return 0.0;
337 switch (m_encodingType) {
338 case k1X:
339 return 1.0;
340 case k2X:
341 return 0.5;
342 case k4X:
343 return 0.25;
344 default:
345 return 0.0;
346 }
347}
348
349/**
350 * Get the distance the robot has driven since the last reset.
351 *
352 * @return The distance driven since the last reset as scaled by the value from
353 * SetDistancePerPulse().
354 */
355double Encoder::GetDistance() const {
356 if (StatusIsFatal()) return 0.0;
357 return GetRaw() * DecodingScaleFactor() * m_distancePerPulse;
358}
359
360/**
361 * Get the current rate of the encoder.
362 * Units are distance per second as scaled by the value from
363 * SetDistancePerPulse().
364 *
365 * @return The current rate of the encoder.
366 */
367double Encoder::GetRate() const {
368 if (StatusIsFatal()) return 0.0;
369 return (m_distancePerPulse / GetPeriod());
370}
371
372/**
373 * Set the minimum rate of the device before the hardware reports it stopped.
374 *
375 * @param minRate The minimum rate. The units are in distance per second as
376 * scaled by the value from SetDistancePerPulse().
377 */
378void Encoder::SetMinRate(double minRate) {
379 if (StatusIsFatal()) return;
380 SetMaxPeriod(m_distancePerPulse / minRate);
381}
382
383/**
384 * Set the distance per pulse for this encoder.
385 * This sets the multiplier used to determine the distance driven based on the
386 * count value
387 * from the encoder.
388 * Do not include the decoding type in this scale. The library already
389 * compensates for the decoding type.
390 * Set this value based on the encoder's rated Pulses per Revolution and
391 * factor in gearing reductions following the encoder shaft.
392 * This distance can be in any units you like, linear or angular.
393 *
394 * @param distancePerPulse The scale factor that will be used to convert pulses
395 * to useful units.
396 */
397void Encoder::SetDistancePerPulse(double distancePerPulse) {
398 if (StatusIsFatal()) return;
399 m_distancePerPulse = distancePerPulse;
400}
401
402/**
403 * Set the direction sensing for this encoder.
404 * This sets the direction sensing on the encoder so that it could count in the
405 * correct
406 * software direction regardless of the mounting.
407 * @param reverseDirection true if the encoder direction should be reversed
408 */
409void Encoder::SetReverseDirection(bool reverseDirection) {
410 if (StatusIsFatal()) return;
411 if (m_counter) {
412 m_counter->SetReverseDirection(reverseDirection);
413 } else {
414 int32_t status = 0;
415 setEncoderReverseDirection(m_encoder, reverseDirection, &status);
416 wpi_setErrorWithContext(status, getHALErrorMessage(status));
417 }
418}
419
420/**
421 * Set the Samples to Average which specifies the number of samples of the timer
422 * to
423 * average when calculating the period. Perform averaging to account for
424 * mechanical imperfections or as oversampling to increase resolution.
425 * @param samplesToAverage The number of samples to average from 1 to 127.
426 */
427void Encoder::SetSamplesToAverage(int samplesToAverage) {
428 if (samplesToAverage < 1 || samplesToAverage > 127) {
429 wpi_setWPIErrorWithContext(
430 ParameterOutOfRange,
431 "Average counter values must be between 1 and 127");
432 }
433 int32_t status = 0;
434 switch (m_encodingType) {
435 case k4X:
436 setEncoderSamplesToAverage(m_encoder, samplesToAverage, &status);
437 wpi_setErrorWithContext(status, getHALErrorMessage(status));
438 break;
439 case k1X:
440 case k2X:
441 m_counter->SetSamplesToAverage(samplesToAverage);
442 break;
443 }
444}
445
446/**
447 * Get the Samples to Average which specifies the number of samples of the timer
448 * to
449 * average when calculating the period. Perform averaging to account for
450 * mechanical imperfections or as oversampling to increase resolution.
451 * @return SamplesToAverage The number of samples being averaged (from 1 to 127)
452 */
453int Encoder::GetSamplesToAverage() const {
454 int result = 1;
455 int32_t status = 0;
456 switch (m_encodingType) {
457 case k4X:
458 result = getEncoderSamplesToAverage(m_encoder, &status);
459 wpi_setErrorWithContext(status, getHALErrorMessage(status));
460 break;
461 case k1X:
462 case k2X:
463 result = m_counter->GetSamplesToAverage();
464 break;
465 }
466 return result;
467}
468
469/**
470 * Implement the PIDSource interface.
471 *
472 * @return The current value of the selected source parameter.
473 */
474double Encoder::PIDGet() {
475 if (StatusIsFatal()) return 0.0;
476 switch (GetPIDSourceType()) {
477 case PIDSourceType::kDisplacement:
478 return GetDistance();
479 case PIDSourceType::kRate:
480 return GetRate();
481 default:
482 return 0.0;
483 }
484}
485
486/**
487 * Set the index source for the encoder. When this source is activated, the
488 * encoder count automatically resets.
489 *
490 * @param channel A DIO channel to set as the encoder index
491 * @param type The state that will cause the encoder to reset
492 */
493void Encoder::SetIndexSource(uint32_t channel, Encoder::IndexingType type) {
494 int32_t status = 0;
495 bool activeHigh = (type == kResetWhileHigh) || (type == kResetOnRisingEdge);
496 bool edgeSensitive =
497 (type == kResetOnFallingEdge) || (type == kResetOnRisingEdge);
498
499 setEncoderIndexSource(m_encoder, channel, false, activeHigh, edgeSensitive,
500 &status);
501 wpi_setGlobalErrorWithContext(status, getHALErrorMessage(status));
502}
503
504/**
505 * Set the index source for the encoder. When this source is activated, the
506 * encoder count automatically resets.
507 *
508 * @param channel A digital source to set as the encoder index
509 * @param type The state that will cause the encoder to reset
510 */
511DEPRECATED("Use pass-by-reference instead.")
512void Encoder::SetIndexSource(DigitalSource *source,
513 Encoder::IndexingType type) {
514 SetIndexSource(*source, type);
515}
516
517/**
518 * Set the index source for the encoder. When this source is activated, the
519 * encoder count automatically resets.
520 *
521 * @param channel A digital source to set as the encoder index
522 * @param type The state that will cause the encoder to reset
523 */
524void Encoder::SetIndexSource(const DigitalSource &source,
525 Encoder::IndexingType type) {
526 int32_t status = 0;
527 bool activeHigh = (type == kResetWhileHigh) || (type == kResetOnRisingEdge);
528 bool edgeSensitive =
529 (type == kResetOnFallingEdge) || (type == kResetOnRisingEdge);
530
531 setEncoderIndexSource(m_encoder, source.GetChannelForRouting(),
532 source.GetAnalogTriggerForRouting(), activeHigh,
533 edgeSensitive, &status);
534 wpi_setGlobalErrorWithContext(status, getHALErrorMessage(status));
535}
536
537void Encoder::UpdateTable() {
538 if (m_table != nullptr) {
539 m_table->PutNumber("Speed", GetRate());
540 m_table->PutNumber("Distance", GetDistance());
541 m_table->PutNumber("Distance per Tick", m_distancePerPulse);
542 }
543}
544
545void Encoder::StartLiveWindowMode() {}
546
547void Encoder::StopLiveWindowMode() {}
548
549std::string Encoder::GetSmartDashboardType() const {
550 if (m_encodingType == k4X)
551 return "Quadrature Encoder";
552 else
553 return "Encoder";
554}
555
556void Encoder::InitTable(std::shared_ptr<ITable> subTable) {
557 m_table = subTable;
558 UpdateTable();
559}
560
561std::shared_ptr<ITable> Encoder::GetTable() const { return m_table; }