001/*----------------------------------------------------------------------------*/
002/* Copyright (c) 2008-2018 FIRST. All Rights Reserved.                        */
003/* Open Source Software - may be modified and shared by FRC teams. The code   */
004/* must be accompanied by the FIRST BSD license file in the root directory of */
005/* the project.                                                               */
006/*----------------------------------------------------------------------------*/
007
008package edu.wpi.first.wpilibj;
009
010import edu.wpi.first.wpilibj.hal.EncoderJNI;
011import edu.wpi.first.wpilibj.hal.FRCNetComm.tResourceType;
012import edu.wpi.first.wpilibj.hal.HAL;
013import edu.wpi.first.wpilibj.smartdashboard.SendableBuilder;
014import edu.wpi.first.wpilibj.util.AllocationException;
015
016import static java.util.Objects.requireNonNull;
017
018/**
019 * Class to read quadrature encoders.
020 *
021 * <p>Quadrature encoders are devices that count shaft rotation and can sense direction. The output
022 * of the Encoder class is an integer that can count either up or down, and can go negative for
023 * reverse direction counting. When creating Encoders, a direction can be supplied that inverts the
024 * sense of the output to make code more readable if the encoder is mounted such that forward
025 * movement generates negative values. Quadrature encoders have two digital outputs, an A Channel
026 * and a B Channel, that are out of phase with each other for direction sensing.
027 *
028 * <p>All encoders will immediately start counting - reset() them if you need them to be zeroed
029 * before use.
030 */
031public class Encoder extends SensorBase implements CounterBase, PIDSource, Sendable {
032  public enum IndexingType {
033    kResetWhileHigh(0), kResetWhileLow(1), kResetOnFallingEdge(2), kResetOnRisingEdge(3);
034
035    @SuppressWarnings("MemberName")
036    public final int value;
037
038    IndexingType(int value) {
039      this.value = value;
040    }
041  }
042
043  /**
044   * The a source.
045   */
046  @SuppressWarnings("MemberName")
047  protected DigitalSource m_aSource; // the A phase of the quad encoder
048  /**
049   * The b source.
050   */
051  @SuppressWarnings("MemberName")
052  protected DigitalSource m_bSource; // the B phase of the quad encoder
053  /**
054   * The index source.
055   */
056  protected DigitalSource m_indexSource = null; // Index on some encoders
057  private boolean m_allocatedA;
058  private boolean m_allocatedB;
059  private boolean m_allocatedI;
060  private PIDSourceType m_pidSource;
061
062  private int m_encoder; // the HAL encoder object
063
064
065  /**
066   * Common initialization code for Encoders. This code allocates resources for Encoders and is
067   * common to all constructors.
068   *
069   * <p>The encoder will start counting immediately.
070   *
071   * @param reverseDirection If true, counts down instead of up (this is all relative)
072   */
073  private void initEncoder(boolean reverseDirection, final EncodingType type) {
074    m_encoder = EncoderJNI.initializeEncoder(m_aSource.getPortHandleForRouting(),
075        m_aSource.getAnalogTriggerTypeForRouting(), m_bSource.getPortHandleForRouting(),
076        m_bSource.getAnalogTriggerTypeForRouting(), reverseDirection, type.value);
077
078    m_pidSource = PIDSourceType.kDisplacement;
079
080    int fpgaIndex = getFPGAIndex();
081    HAL.report(tResourceType.kResourceType_Encoder, fpgaIndex, type.value);
082    setName("Encoder", fpgaIndex);
083  }
084
085  /**
086   * Encoder constructor. Construct a Encoder given a and b channels.
087   *
088   * <p>The encoder will start counting immediately.
089   *
090   * @param channelA         The a channel DIO channel. 0-9 are on-board, 10-25 are on the MXP port
091   * @param channelB         The b channel DIO channel. 0-9 are on-board, 10-25 are on the MXP port
092   * @param reverseDirection represents the orientation of the encoder and inverts the output values
093   *                         if necessary so forward represents positive values.
094   */
095  public Encoder(final int channelA, final int channelB, boolean reverseDirection) {
096    this(channelA, channelB, reverseDirection, EncodingType.k4X);
097  }
098
099  /**
100   * Encoder constructor. Construct a Encoder given a and b channels.
101   *
102   * <p>The encoder will start counting immediately.
103   *
104   * @param channelA The a channel digital input channel.
105   * @param channelB The b channel digital input channel.
106   */
107  public Encoder(final int channelA, final int channelB) {
108    this(channelA, channelB, false);
109  }
110
111  /**
112   * Encoder constructor. Construct a Encoder given a and b channels.
113   *
114   * <p>The encoder will start counting immediately.
115   *
116   * @param channelA         The a channel digital input channel.
117   * @param channelB         The b channel digital input channel.
118   * @param reverseDirection represents the orientation of the encoder and inverts the output values
119   *                         if necessary so forward represents positive values.
120   * @param encodingType     either k1X, k2X, or k4X to indicate 1X, 2X or 4X decoding. If 4X is
121   *                         selected, then an encoder FPGA object is used and the returned counts
122   *                         will be 4x the encoder spec'd value since all rising and falling edges
123   *                         are counted. If 1X or 2X are selected then a m_counter object will be
124   *                         used and the returned value will either exactly match the spec'd count
125   *                         or be double (2x) the spec'd count.
126   */
127  public Encoder(final int channelA, final int channelB, boolean reverseDirection,
128                 final EncodingType encodingType) {
129    requireNonNull(encodingType, "Given encoding type was null");
130
131    m_allocatedA = true;
132    m_allocatedB = true;
133    m_allocatedI = false;
134    m_aSource = new DigitalInput(channelA);
135    m_bSource = new DigitalInput(channelB);
136    addChild(m_aSource);
137    addChild(m_bSource);
138    initEncoder(reverseDirection, encodingType);
139  }
140
141  /**
142   * Encoder constructor. Construct a Encoder given a and b channels. Using an index pulse forces 4x
143   * encoding
144   *
145   * <p>The encoder will start counting immediately.
146   *
147   * @param channelA         The a channel digital input channel.
148   * @param channelB         The b channel digital input channel.
149   * @param indexChannel     The index channel digital input channel.
150   * @param reverseDirection represents the orientation of the encoder and inverts the output values
151   *                         if necessary so forward represents positive values.
152   */
153  public Encoder(final int channelA, final int channelB, final int indexChannel,
154                 boolean reverseDirection) {
155    this(channelA, channelB, reverseDirection);
156    m_allocatedI = true;
157    m_indexSource = new DigitalInput(indexChannel);
158    addChild(m_indexSource);
159    setIndexSource(m_indexSource);
160  }
161
162  /**
163   * Encoder constructor. Construct a Encoder given a and b channels. Using an index pulse forces 4x
164   * encoding
165   *
166   * <p>The encoder will start counting immediately.
167   *
168   * @param channelA     The a channel digital input channel.
169   * @param channelB     The b channel digital input channel.
170   * @param indexChannel The index channel digital input channel.
171   */
172  public Encoder(final int channelA, final int channelB, final int indexChannel) {
173    this(channelA, channelB, indexChannel, false);
174  }
175
176  /**
177   * Encoder constructor. Construct a Encoder given a and b channels as digital inputs. This is used
178   * in the case where the digital inputs are shared. The Encoder class will not allocate the
179   * digital inputs and assume that they already are counted.
180   *
181   * <p>The encoder will start counting immediately.
182   *
183   * @param sourceA          The source that should be used for the a channel.
184   * @param sourceB          the source that should be used for the b channel.
185   * @param reverseDirection represents the orientation of the encoder and inverts the output values
186   *                         if necessary so forward represents positive values.
187   */
188  public Encoder(DigitalSource sourceA, DigitalSource sourceB, boolean reverseDirection) {
189    this(sourceA, sourceB, reverseDirection, EncodingType.k4X);
190  }
191
192  /**
193   * Encoder constructor. Construct a Encoder given a and b channels as digital inputs. This is used
194   * in the case where the digital inputs are shared. The Encoder class will not allocate the
195   * digital inputs and assume that they already are counted.
196   *
197   * <p>The encoder will start counting immediately.
198   *
199   * @param sourceA The source that should be used for the a channel.
200   * @param sourceB the source that should be used for the b channel.
201   */
202  public Encoder(DigitalSource sourceA, DigitalSource sourceB) {
203    this(sourceA, sourceB, false);
204  }
205
206  /**
207   * Encoder constructor. Construct a Encoder given a and b channels as digital inputs. This is used
208   * in the case where the digital inputs are shared. The Encoder class will not allocate the
209   * digital inputs and assume that they already are counted.
210   *
211   * <p>The encoder will start counting immediately.
212   *
213   * @param sourceA          The source that should be used for the a channel.
214   * @param sourceB          the source that should be used for the b channel.
215   * @param reverseDirection represents the orientation of the encoder and inverts the output values
216   *                         if necessary so forward represents positive values.
217   * @param encodingType     either k1X, k2X, or k4X to indicate 1X, 2X or 4X decoding. If 4X is
218   *                         selected, then an encoder FPGA object is used and the returned counts
219   *                         will be 4x the encoder spec'd value since all rising and falling edges
220   *                         are counted. If 1X or 2X are selected then a m_counter object will be
221   *                         used and the returned value will either exactly match the spec'd count
222   *                         or be double (2x) the spec'd count.
223   */
224  public Encoder(DigitalSource sourceA, DigitalSource sourceB, boolean reverseDirection,
225                 final EncodingType encodingType) {
226    requireNonNull(sourceA, "Digital Source A was null");
227    requireNonNull(sourceB, "Digital Source B was null");
228    requireNonNull(encodingType, "Given encoding type was null");
229
230    m_allocatedA = false;
231    m_allocatedB = false;
232    m_allocatedI = false;
233    m_aSource = sourceA;
234    m_bSource = sourceB;
235    initEncoder(reverseDirection, encodingType);
236  }
237
238  /**
239   * Encoder constructor. Construct a Encoder given a, b and index channels as digital inputs. This
240   * is used in the case where the digital inputs are shared. The Encoder class will not allocate
241   * the digital inputs and assume that they already are counted.
242   *
243   * <p>The encoder will start counting immediately.
244   *
245   * @param sourceA          The source that should be used for the a channel.
246   * @param sourceB          the source that should be used for the b channel.
247   * @param indexSource      the source that should be used for the index channel.
248   * @param reverseDirection represents the orientation of the encoder and inverts the output values
249   *                         if necessary so forward represents positive values.
250   */
251  public Encoder(DigitalSource sourceA, DigitalSource sourceB, DigitalSource indexSource,
252                 boolean reverseDirection) {
253    this(sourceA, sourceB, reverseDirection);
254    m_allocatedI = false;
255    m_indexSource = indexSource;
256    setIndexSource(indexSource);
257  }
258
259  /**
260   * Encoder constructor. Construct a Encoder given a, b and index channels as digital inputs. This
261   * is used in the case where the digital inputs are shared. The Encoder class will not allocate
262   * the digital inputs and assume that they already are counted.
263   *
264   * <p>The encoder will start counting immediately.
265   *
266   * @param sourceA     The source that should be used for the a channel.
267   * @param sourceB     the source that should be used for the b channel.
268   * @param indexSource the source that should be used for the index channel.
269   */
270  public Encoder(DigitalSource sourceA, DigitalSource sourceB, DigitalSource indexSource) {
271    this(sourceA, sourceB, indexSource, false);
272  }
273
274  /**
275   * Get the FPGA index of the encoder.
276   *
277   * @return The Encoder's FPGA index.
278   */
279  @SuppressWarnings("AbbreviationAsWordInName")
280  public int getFPGAIndex() {
281    return EncoderJNI.getEncoderFPGAIndex(m_encoder);
282  }
283
284  /**
285   * Used to divide raw edge counts down to spec'd counts.
286   *
287   * @return The encoding scale factor 1x, 2x, or 4x, per the requested encoding type.
288   */
289  public int getEncodingScale() {
290    return EncoderJNI.getEncoderEncodingScale(m_encoder);
291  }
292
293  /**
294   * Free the resources used by this object.
295   */
296  @Override
297  public void free() {
298    super.free();
299    if (m_aSource != null && m_allocatedA) {
300      m_aSource.free();
301      m_allocatedA = false;
302    }
303    if (m_bSource != null && m_allocatedB) {
304      m_bSource.free();
305      m_allocatedB = false;
306    }
307    if (m_indexSource != null && m_allocatedI) {
308      m_indexSource.free();
309      m_allocatedI = false;
310    }
311
312    m_aSource = null;
313    m_bSource = null;
314    m_indexSource = null;
315    EncoderJNI.freeEncoder(m_encoder);
316    m_encoder = 0;
317  }
318
319  /**
320   * Gets the raw value from the encoder. The raw value is the actual count unscaled by the 1x, 2x,
321   * or 4x scale factor.
322   *
323   * @return Current raw count from the encoder
324   */
325  public int getRaw() {
326    return EncoderJNI.getEncoderRaw(m_encoder);
327  }
328
329  /**
330   * Gets the current count. Returns the current count on the Encoder. This method compensates for
331   * the decoding type.
332   *
333   * @return Current count from the Encoder adjusted for the 1x, 2x, or 4x scale factor.
334   */
335  public int get() {
336    return EncoderJNI.getEncoder(m_encoder);
337  }
338
339  /**
340   * Reset the Encoder distance to zero. Resets the current count to zero on the encoder.
341   */
342  public void reset() {
343    EncoderJNI.resetEncoder(m_encoder);
344  }
345
346  /**
347   * Returns the period of the most recent pulse. Returns the period of the most recent Encoder
348   * pulse in seconds. This method compensates for the decoding type.
349   *
350   * <p><b>Warning:</b> This returns unscaled periods. Use getRate() for rates that are scaled using
351   * the value from setDistancePerPulse().
352   *
353   * @return Period in seconds of the most recent pulse.
354   * @deprecated Use getRate() in favor of this method.
355   */
356  @Deprecated
357  public double getPeriod() {
358    return EncoderJNI.getEncoderPeriod(m_encoder);
359  }
360
361  /**
362   * Sets the maximum period for stopped detection. Sets the value that represents the maximum
363   * period of the Encoder before it will assume that the attached device is stopped. This timeout
364   * allows users to determine if the wheels or other shaft has stopped rotating. This method
365   * compensates for the decoding type.
366   *
367   * @param maxPeriod The maximum time between rising and falling edges before the FPGA will report
368   *                  the device stopped. This is expressed in seconds.
369   */
370  public void setMaxPeriod(double maxPeriod) {
371    EncoderJNI.setEncoderMaxPeriod(m_encoder, maxPeriod);
372  }
373
374  /**
375   * Determine if the encoder is stopped. Using the MaxPeriod value, a boolean is returned that is
376   * true if the encoder is considered stopped and false if it is still moving. A stopped encoder is
377   * one where the most recent pulse width exceeds the MaxPeriod.
378   *
379   * @return True if the encoder is considered stopped.
380   */
381  public boolean getStopped() {
382    return EncoderJNI.getEncoderStopped(m_encoder);
383  }
384
385  /**
386   * The last direction the encoder value changed.
387   *
388   * @return The last direction the encoder value changed.
389   */
390  public boolean getDirection() {
391    return EncoderJNI.getEncoderDirection(m_encoder);
392  }
393
394  /**
395   * Get the distance the robot has driven since the last reset as scaled by the value from {@link
396   * #setDistancePerPulse(double)}.
397   *
398   * @return The distance driven since the last reset
399   */
400  public double getDistance() {
401    return EncoderJNI.getEncoderDistance(m_encoder);
402  }
403
404  /**
405   * Get the current rate of the encoder. Units are distance per second as scaled by the value from
406   * setDistancePerPulse().
407   *
408   * @return The current rate of the encoder.
409   */
410  public double getRate() {
411    return EncoderJNI.getEncoderRate(m_encoder);
412  }
413
414  /**
415   * Set the minimum rate of the device before the hardware reports it stopped.
416   *
417   * @param minRate The minimum rate. The units are in distance per second as scaled by the value
418   *                from setDistancePerPulse().
419   */
420  public void setMinRate(double minRate) {
421    EncoderJNI.setEncoderMinRate(m_encoder, minRate);
422  }
423
424  /**
425   * Set the distance per pulse for this encoder. This sets the multiplier used to determine the
426   * distance driven based on the count value from the encoder. Do not include the decoding type in
427   * this scale. The library already compensates for the decoding type. Set this value based on the
428   * encoder's rated Pulses per Revolution and factor in gearing reductions following the encoder
429   * shaft. This distance can be in any units you like, linear or angular.
430   *
431   * @param distancePerPulse The scale factor that will be used to convert pulses to useful units.
432   */
433  public void setDistancePerPulse(double distancePerPulse) {
434    EncoderJNI.setEncoderDistancePerPulse(m_encoder, distancePerPulse);
435  }
436
437  /**
438   * Get the distance per pulse for this encoder.
439   *
440   * @return The scale factor that will be used to convert pulses to useful units.
441   */
442  public double getDistancePerPulse() {
443    return EncoderJNI.getEncoderDistancePerPulse(m_encoder);
444  }
445
446  /**
447   * Set the direction sensing for this encoder. This sets the direction sensing on the encoder so
448   * that it could count in the correct software direction regardless of the mounting.
449   *
450   * @param reverseDirection true if the encoder direction should be reversed
451   */
452  public void setReverseDirection(boolean reverseDirection) {
453    EncoderJNI.setEncoderReverseDirection(m_encoder, reverseDirection);
454  }
455
456  /**
457   * Set the Samples to Average which specifies the number of samples of the timer to average when
458   * calculating the period. Perform averaging to account for mechanical imperfections or as
459   * oversampling to increase resolution.
460   *
461   * @param samplesToAverage The number of samples to average from 1 to 127.
462   */
463  public void setSamplesToAverage(int samplesToAverage) {
464    EncoderJNI.setEncoderSamplesToAverage(m_encoder, samplesToAverage);
465  }
466
467  /**
468   * Get the Samples to Average which specifies the number of samples of the timer to average when
469   * calculating the period. Perform averaging to account for mechanical imperfections or as
470   * oversampling to increase resolution.
471   *
472   * @return SamplesToAverage The number of samples being averaged (from 1 to 127)
473   */
474  public int getSamplesToAverage() {
475    return EncoderJNI.getEncoderSamplesToAverage(m_encoder);
476  }
477
478  /**
479   * Set which parameter of the encoder you are using as a process control variable. The encoder
480   * class supports the rate and distance parameters.
481   *
482   * @param pidSource An enum to select the parameter.
483   */
484  public void setPIDSourceType(PIDSourceType pidSource) {
485    m_pidSource = pidSource;
486  }
487
488  @Override
489  public PIDSourceType getPIDSourceType() {
490    return m_pidSource;
491  }
492
493  /**
494   * Implement the PIDSource interface.
495   *
496   * @return The current value of the selected source parameter.
497   */
498  public double pidGet() {
499    switch (m_pidSource) {
500      case kDisplacement:
501        return getDistance();
502      case kRate:
503        return getRate();
504      default:
505        return 0.0;
506    }
507  }
508
509  /**
510   * Set the index source for the encoder. When this source is activated, the encoder count
511   * automatically resets.
512   *
513   * @param channel A DIO channel to set as the encoder index
514   */
515  public void setIndexSource(int channel) {
516    setIndexSource(channel, IndexingType.kResetOnRisingEdge);
517  }
518
519  /**
520   * Set the index source for the encoder. When this source is activated, the encoder count
521   * automatically resets.
522   *
523   * @param source A digital source to set as the encoder index
524   */
525  public void setIndexSource(DigitalSource source) {
526    setIndexSource(source, IndexingType.kResetOnRisingEdge);
527  }
528
529  /**
530   * Set the index source for the encoder. When this source rises, the encoder count automatically
531   * resets.
532   *
533   * @param channel A DIO channel to set as the encoder index
534   * @param type    The state that will cause the encoder to reset
535   */
536  public void setIndexSource(int channel, IndexingType type) {
537    if (m_allocatedI) {
538      throw new AllocationException("Digital Input for Indexing already allocated");
539    }
540    m_indexSource = new DigitalInput(channel);
541    m_allocatedI = true;
542    addChild(m_indexSource);
543    setIndexSource(m_indexSource, type);
544  }
545
546  /**
547   * Set the index source for the encoder. When this source rises, the encoder count automatically
548   * resets.
549   *
550   * @param source A digital source to set as the encoder index
551   * @param type   The state that will cause the encoder to reset
552   */
553  public void setIndexSource(DigitalSource source, IndexingType type) {
554    EncoderJNI.setEncoderIndexSource(m_encoder, source.getPortHandleForRouting(),
555        source.getAnalogTriggerTypeForRouting(), type.value);
556  }
557
558  @Override
559  public void initSendable(SendableBuilder builder) {
560    if (EncoderJNI.getEncoderEncodingType(m_encoder) == EncodingType.k4X.value) {
561      builder.setSmartDashboardType("Quadrature Encoder");
562    } else {
563      builder.setSmartDashboardType("Encoder");
564    }
565
566    builder.addDoubleProperty("Speed", this::getRate, null);
567    builder.addDoubleProperty("Distance", this::getDistance, null);
568    builder.addDoubleProperty("Distance per Tick", this::getDistancePerPulse, null);
569  }
570}