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}