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}