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.counter;
006
007import edu.wpi.first.hal.CounterJNI;
008import edu.wpi.first.hal.HAL;
009import edu.wpi.first.util.sendable.Sendable;
010import edu.wpi.first.util.sendable.SendableBuilder;
011import edu.wpi.first.util.sendable.SendableRegistry;
012
013/**
014 * Tachometer.
015 *
016 * <p>The Tachometer class measures the time between digital pulses to determine the rotation speed
017 * of a mechanism. Examples of devices that could be used with the tachometer class are a hall
018 * effect sensor, break beam sensor, or optical sensor detecting tape on a shooter wheel. Unlike
019 * encoders, this class only needs a single digital input.
020 */
021public class Tachometer implements Sendable, AutoCloseable {
022  private final int m_handle;
023  private int m_edgesPerRevolution = 1;
024
025  /**
026   * Constructs a new tachometer.
027   *
028   * @param channel The channel of the Tachometer.
029   * @param configuration The edge configuration
030   */
031  @SuppressWarnings("this-escape")
032  public Tachometer(int channel, EdgeConfiguration configuration) {
033    m_handle = CounterJNI.initializeCounter(channel, configuration.rising);
034
035    HAL.reportUsage("IO", channel, "Tachometer");
036    SendableRegistry.add(this, "Tachometer", channel);
037  }
038
039  @Override
040  public void close() {
041    SendableRegistry.remove(this);
042    CounterJNI.freeCounter(m_handle);
043  }
044
045  /**
046   * Gets the tachometer period.
047   *
048   * @return Current period (in seconds).
049   */
050  public double getPeriod() {
051    return CounterJNI.getCounterPeriod(m_handle);
052  }
053
054  /**
055   * Gets the tachometer frequency.
056   *
057   * @return Current frequency (in hertz).
058   */
059  public double getFrequency() {
060    double period = getPeriod();
061    if (period == 0) {
062      return 0;
063    }
064    return 1 / period;
065  }
066
067  /**
068   * Gets the number of edges per revolution.
069   *
070   * @return Edges per revolution.
071   */
072  public int getEdgesPerRevolution() {
073    return m_edgesPerRevolution;
074  }
075
076  /**
077   * Sets the number of edges per revolution.
078   *
079   * @param edgesPerRevolution Edges per revolution.
080   */
081  public void setEdgesPerRevolution(int edgesPerRevolution) {
082    m_edgesPerRevolution = edgesPerRevolution;
083  }
084
085  /**
086   * Gets the current tachometer revolutions per second.
087   *
088   * <p>setEdgesPerRevolution must be set with a non 0 value for this to return valid values.
089   *
090   * @return Current RPS.
091   */
092  public double getRevolutionsPerSecond() {
093    double period = getPeriod();
094    if (period == 0) {
095      return 0;
096    }
097    int edgesPerRevolution = getEdgesPerRevolution();
098    if (edgesPerRevolution == 0) {
099      return 0;
100    }
101    return (1.0 / edgesPerRevolution) / period;
102  }
103
104  /**
105   * Gets the current tachometer revolutions per minute.
106   *
107   * <p>setEdgesPerRevolution must be set with a non 0 value for this to return valid values.
108   *
109   * @return Current RPM.
110   */
111  public double getRevolutionsPerMinute() {
112    return getRevolutionsPerSecond() * 60;
113  }
114
115  /**
116   * Gets if the tachometer is stopped.
117   *
118   * @return True if the tachometer is stopped.
119   */
120  public boolean getStopped() {
121    return CounterJNI.getCounterStopped(m_handle);
122  }
123
124  /**
125   * Sets the maximum period before the tachometer is considered stopped.
126   *
127   * @param maxPeriod The max period (in seconds).
128   */
129  public void setMaxPeriod(double maxPeriod) {
130    CounterJNI.setCounterMaxPeriod(m_handle, maxPeriod);
131  }
132
133  @Override
134  public void initSendable(SendableBuilder builder) {
135    builder.setSmartDashboardType("Tachometer");
136    builder.addDoubleProperty("RPS", this::getRevolutionsPerSecond, null);
137    builder.addDoubleProperty("RPM", this::getRevolutionsPerMinute, null);
138  }
139}