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