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