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