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.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  public int getFPGAIndex() {
286    return EncoderJNI.getEncoderFPGAIndex(m_encoder);
287  }
288
289  /**
290   * Used to divide raw edge counts down to spec'd counts.
291   *
292   * @return The encoding scale factor 1x, 2x, or 4x, per the requested encoding type.
293   */
294  public int getEncodingScale() {
295    return EncoderJNI.getEncoderEncodingScale(m_encoder);
296  }
297
298  @Override
299  public void close() {
300    SendableRegistry.remove(this);
301    if (m_aSource != null && m_allocatedA) {
302      m_aSource.close();
303      m_allocatedA = false;
304    }
305    if (m_bSource != null && m_allocatedB) {
306      m_bSource.close();
307      m_allocatedB = false;
308    }
309    if (m_indexSource != null && m_allocatedI) {
310      m_indexSource.close();
311      m_allocatedI = false;
312    }
313
314    m_aSource = null;
315    m_bSource = null;
316    m_indexSource = null;
317    EncoderJNI.freeEncoder(m_encoder);
318    m_encoder = 0;
319  }
320
321  /**
322   * Gets the raw value from the encoder. The raw value is the actual count unscaled by the 1x, 2x,
323   * or 4x scale factor.
324   *
325   * @return Current raw count from the encoder
326   */
327  public int getRaw() {
328    return EncoderJNI.getEncoderRaw(m_encoder);
329  }
330
331  /**
332   * Gets the current count. Returns the current count on the Encoder. This method compensates for
333   * the decoding type.
334   *
335   * @return Current count from the Encoder adjusted for the 1x, 2x, or 4x scale factor.
336   */
337  @Override
338  public int get() {
339    return EncoderJNI.getEncoder(m_encoder);
340  }
341
342  /** Reset the Encoder distance to zero. Resets the current count to zero on the encoder. */
343  @Override
344  public void reset() {
345    EncoderJNI.resetEncoder(m_encoder);
346  }
347
348  /**
349   * Returns the period of the most recent pulse. Returns the period of the most recent Encoder
350   * pulse in seconds. This method compensates for the decoding type.
351   *
352   * <p><b>Warning:</b> This returns unscaled periods. Use getRate() for rates that are scaled using
353   * the value from setDistancePerPulse().
354   *
355   * @return Period in seconds of the most recent pulse.
356   * @deprecated Use getRate() in favor of this method.
357   */
358  @Override
359  @Deprecated
360  public double getPeriod() {
361    return EncoderJNI.getEncoderPeriod(m_encoder);
362  }
363
364  /**
365   * Sets the maximum period for stopped detection. Sets the value that represents the maximum
366   * period of the Encoder before it will assume that the attached device is stopped. This timeout
367   * allows users to determine if the wheels or other shaft has stopped rotating. This method
368   * compensates for the decoding type.
369   *
370   * @param maxPeriod The maximum time between rising and falling edges before the FPGA will report
371   *     the device stopped. This is expressed in seconds.
372   * @deprecated Use setMinRate() in favor of this method. This takes unscaled periods and
373   *     setMinRate() scales using value from setDistancePerPulse().
374   */
375  @Override
376  @Deprecated
377  public void setMaxPeriod(double maxPeriod) {
378    EncoderJNI.setEncoderMaxPeriod(m_encoder, maxPeriod);
379  }
380
381  /**
382   * Determine if the encoder is stopped. Using the MaxPeriod value, a boolean is returned that is
383   * true if the encoder is considered stopped and false if it is still moving. A stopped encoder is
384   * one where the most recent pulse width exceeds the MaxPeriod.
385   *
386   * @return True if the encoder is considered stopped.
387   */
388  @Override
389  public boolean getStopped() {
390    return EncoderJNI.getEncoderStopped(m_encoder);
391  }
392
393  /**
394   * The last direction the encoder value changed.
395   *
396   * @return The last direction the encoder value changed.
397   */
398  @Override
399  public boolean getDirection() {
400    return EncoderJNI.getEncoderDirection(m_encoder);
401  }
402
403  /**
404   * Get the distance the robot has driven since the last reset as scaled by the value from {@link
405   * #setDistancePerPulse(double)}.
406   *
407   * @return The distance driven since the last reset
408   */
409  public double getDistance() {
410    return EncoderJNI.getEncoderDistance(m_encoder);
411  }
412
413  /**
414   * Get the current rate of the encoder. Units are distance per second as scaled by the value from
415   * setDistancePerPulse().
416   *
417   * @return The current rate of the encoder.
418   */
419  public double getRate() {
420    return EncoderJNI.getEncoderRate(m_encoder);
421  }
422
423  /**
424   * Set the minimum rate of the device before the hardware reports it stopped.
425   *
426   * @param minRate The minimum rate. The units are in distance per second as scaled by the value
427   *     from setDistancePerPulse().
428   */
429  public void setMinRate(double minRate) {
430    EncoderJNI.setEncoderMinRate(m_encoder, minRate);
431  }
432
433  /**
434   * Set the distance per pulse for this encoder. This sets the multiplier used to determine the
435   * distance driven based on the count value from the encoder. Do not include the decoding type in
436   * this scale. The library already compensates for the decoding type. Set this value based on the
437   * encoder's rated Pulses per Revolution and factor in gearing reductions following the encoder
438   * shaft. This distance can be in any units you like, linear or angular.
439   *
440   * @param distancePerPulse The scale factor that will be used to convert pulses to useful units.
441   */
442  public void setDistancePerPulse(double distancePerPulse) {
443    EncoderJNI.setEncoderDistancePerPulse(m_encoder, distancePerPulse);
444  }
445
446  /**
447   * Get the distance per pulse for this encoder.
448   *
449   * @return The scale factor that will be used to convert pulses to useful units.
450   */
451  public double getDistancePerPulse() {
452    return EncoderJNI.getEncoderDistancePerPulse(m_encoder);
453  }
454
455  /**
456   * Set the direction sensing for this encoder. This sets the direction sensing on the encoder so
457   * that it could count in the correct software direction regardless of the mounting.
458   *
459   * @param reverseDirection true if the encoder direction should be reversed
460   */
461  public void setReverseDirection(boolean reverseDirection) {
462    EncoderJNI.setEncoderReverseDirection(m_encoder, reverseDirection);
463  }
464
465  /**
466   * Set the Samples to Average which specifies the number of samples of the timer to average when
467   * calculating the period. Perform averaging to account for mechanical imperfections or as
468   * oversampling to increase resolution.
469   *
470   * @param samplesToAverage The number of samples to average from 1 to 127.
471   */
472  public void setSamplesToAverage(int samplesToAverage) {
473    EncoderJNI.setEncoderSamplesToAverage(m_encoder, samplesToAverage);
474  }
475
476  /**
477   * Get the Samples to Average which specifies the number of samples of the timer to average when
478   * calculating the period. Perform averaging to account for mechanical imperfections or as
479   * oversampling to increase resolution.
480   *
481   * @return SamplesToAverage The number of samples being averaged (from 1 to 127)
482   */
483  public int getSamplesToAverage() {
484    return EncoderJNI.getEncoderSamplesToAverage(m_encoder);
485  }
486
487  /**
488   * Set the index source for the encoder. When this source is activated, the encoder count
489   * automatically resets.
490   *
491   * @param channel A DIO channel to set as the encoder index
492   */
493  public void setIndexSource(int channel) {
494    setIndexSource(channel, IndexingType.kResetOnRisingEdge);
495  }
496
497  /**
498   * Set the index source for the encoder. When this source is activated, the encoder count
499   * automatically resets.
500   *
501   * @param source A digital source to set as the encoder index
502   */
503  public void setIndexSource(DigitalSource source) {
504    setIndexSource(source, IndexingType.kResetOnRisingEdge);
505  }
506
507  /**
508   * Set the index source for the encoder. When this source rises, the encoder count automatically
509   * resets.
510   *
511   * @param channel A DIO channel to set as the encoder index
512   * @param type The state that will cause the encoder to reset
513   */
514  public void setIndexSource(int channel, IndexingType type) {
515    if (m_allocatedI) {
516      throw new AllocationException("Digital Input for Indexing already allocated");
517    }
518    m_indexSource = new DigitalInput(channel);
519    m_allocatedI = true;
520    SendableRegistry.addChild(this, m_indexSource);
521    setIndexSource(m_indexSource, type);
522  }
523
524  /**
525   * Set the index source for the encoder. When this source rises, the encoder count automatically
526   * resets.
527   *
528   * @param source A digital source to set as the encoder index
529   * @param type The state that will cause the encoder to reset
530   */
531  public void setIndexSource(DigitalSource source, IndexingType type) {
532    EncoderJNI.setEncoderIndexSource(
533        m_encoder,
534        source.getPortHandleForRouting(),
535        source.getAnalogTriggerTypeForRouting(),
536        type.value);
537  }
538
539  /**
540   * Indicates this input is used by a simulated device.
541   *
542   * @param device simulated device handle
543   */
544  public void setSimDevice(SimDevice device) {
545    EncoderJNI.setEncoderSimDevice(m_encoder, device.getNativeHandle());
546  }
547
548  /**
549   * Gets the decoding scale factor for scaling raw values to full counts.
550   *
551   * @return decoding scale factor
552   */
553  public double getDecodingScaleFactor() {
554    switch (m_encodingType) {
555      case k1X:
556        return 1.0;
557      case k2X:
558        return 0.5;
559      case k4X:
560        return 0.25;
561      default:
562        return 0.0;
563    }
564  }
565
566  @Override
567  public void initSendable(SendableBuilder builder) {
568    if (EncoderJNI.getEncoderEncodingType(m_encoder) == EncodingType.k4X.value) {
569      builder.setSmartDashboardType("Quadrature Encoder");
570    } else {
571      builder.setSmartDashboardType("Encoder");
572    }
573
574    builder.addDoubleProperty("Speed", this::getRate, null);
575    builder.addDoubleProperty("Distance", this::getDistance, null);
576    builder.addDoubleProperty("Distance per Tick", this::getDistancePerPulse, null);
577  }
578}