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}