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.HAL;
010import edu.wpi.first.math.geometry.Rotation2d;
011import edu.wpi.first.util.sendable.Sendable;
012import edu.wpi.first.util.sendable.SendableBuilder;
013import edu.wpi.first.util.sendable.SendableRegistry;
014
015/**
016 * Use a rate gyro to return the robots heading relative to a starting position. The Gyro class
017 * tracks the robots heading based on the starting position. As the robot rotates the new heading is
018 * computed by integrating the rate of rotation returned by the sensor. When the class is
019 * instantiated, it does a short calibration routine where it samples the gyro while at rest to
020 * determine the default offset. This is subtracted from each sample to determine the heading.
021 *
022 * <p>This class is for gyro sensors that connect to an analog input.
023 */
024public class AnalogGyro implements Sendable, AutoCloseable {
025  private AnalogInput m_analog;
026  private boolean m_channelAllocated;
027
028  /** Initialize the gyro. Calibration is handled by calibrate(). */
029  private void initGyro() {
030    HAL.reportUsage("AnalogGyro", m_analog.getChannel(), "");
031    SendableRegistry.add(this, "AnalogGyro", m_analog.getChannel());
032  }
033
034  /**
035   * Calibrate the gyro by running for a number of samples and computing the center value. Then use
036   * the center value as the Accumulator center value for subsequent measurements.
037   *
038   * <p>It's important to make sure that the robot is not moving while the centering calculations
039   * are in progress, this is typically done when the robot is first turned on while it's sitting at
040   * rest before the competition starts.
041   */
042  public void calibrate() {}
043
044  /**
045   * Return the heading of the robot as a {@link edu.wpi.first.math.geometry.Rotation2d}.
046   *
047   * <p>The angle is continuous, that is it will continue from 360 to 361 degrees. This allows
048   * algorithms that wouldn't want to see a discontinuity in the gyro output as it sweeps past from
049   * 360 to 0 on the second time around.
050   *
051   * <p>The angle is expected to increase as the gyro turns counterclockwise when looked at from the
052   * top. It needs to follow the NWU axis convention.
053   *
054   * <p>This heading is based on integration of the returned rate from the gyro.
055   *
056   * @return the current heading of the robot as a {@link edu.wpi.first.math.geometry.Rotation2d}.
057   */
058  public Rotation2d getRotation2d() {
059    return Rotation2d.fromDegrees(-getAngle());
060  }
061
062  /**
063   * Gyro constructor using the channel number.
064   *
065   * @param channel The analog channel the gyro is connected to. Gyros can only be used on on-board
066   *     channels 0-1.
067   */
068  @SuppressWarnings("this-escape")
069  public AnalogGyro(int channel) {
070    this(new AnalogInput(channel));
071    m_channelAllocated = true;
072    SendableRegistry.addChild(this, m_analog);
073  }
074
075  /**
076   * Gyro constructor with a precreated analog channel object. Use this constructor when the analog
077   * channel needs to be shared.
078   *
079   * @param channel The AnalogInput object that the gyro is connected to. Gyros can only be used on
080   *     on-board channels 0-1.
081   */
082  @SuppressWarnings("this-escape")
083  public AnalogGyro(AnalogInput channel) {
084    requireNonNullParam(channel, "channel", "AnalogGyro");
085
086    m_analog = channel;
087    initGyro();
088    calibrate();
089  }
090
091  /**
092   * Gyro constructor using the channel number along with parameters for presetting the center and
093   * offset values. Bypasses calibration.
094   *
095   * @param channel The analog channel the gyro is connected to. Gyros can only be used on on-board
096   *     channels 0-1.
097   * @param center Preset uncalibrated value to use as the accumulator center value.
098   * @param offset Preset uncalibrated value to use as the gyro offset.
099   */
100  @SuppressWarnings("this-escape")
101  public AnalogGyro(int channel, int center, double offset) {
102    this(new AnalogInput(channel), center, offset);
103    m_channelAllocated = true;
104    SendableRegistry.addChild(this, m_analog);
105  }
106
107  /**
108   * Gyro constructor with a precreated analog channel object along with parameters for presetting
109   * the center and offset values. Bypasses calibration.
110   *
111   * @param channel The analog channel the gyro is connected to. Gyros can only be used on on-board
112   *     channels 0-1.
113   * @param center Preset uncalibrated value to use as the accumulator center value.
114   * @param offset Preset uncalibrated value to use as the gyro offset.
115   */
116  @SuppressWarnings({"this-escape", "PMD.UnusedFormalParameter"})
117  public AnalogGyro(AnalogInput channel, int center, double offset) {
118    requireNonNullParam(channel, "channel", "AnalogGyro");
119
120    m_analog = channel;
121    initGyro();
122    reset();
123  }
124
125  /**
126   * Reset the gyro.
127   *
128   * <p>Resets the gyro to a heading of zero. This can be used if there is significant drift in the
129   * gyro, and it needs to be recalibrated after it has been running.
130   */
131  public void reset() {}
132
133  /** Delete (free) the accumulator and the analog components used for the gyro. */
134  @Override
135  public void close() {
136    SendableRegistry.remove(this);
137    if (m_analog != null && m_channelAllocated) {
138      m_analog.close();
139    }
140    m_analog = null;
141  }
142
143  /**
144   * Return the heading of the robot in degrees.
145   *
146   * <p>The angle is continuous, that is it will continue from 360 to 361 degrees. This allows
147   * algorithms that wouldn't want to see a discontinuity in the gyro output as it sweeps past from
148   * 360 to 0 on the second time around.
149   *
150   * <p>The angle is expected to increase as the gyro turns clockwise when looked at from the top.
151   * It needs to follow the NED axis convention.
152   *
153   * <p>This heading is based on integration of the returned rate from the gyro.
154   *
155   * @return the current heading of the robot in degrees.
156   */
157  public double getAngle() {
158    return 0.0;
159  }
160
161  /**
162   * Return the rate of rotation of the gyro.
163   *
164   * <p>The rate is based on the most recent reading of the gyro analog value
165   *
166   * <p>The rate is expected to be positive as the gyro turns clockwise when looked at from the top.
167   * It needs to follow the NED axis convention.
168   *
169   * @return the current rate in degrees per second
170   */
171  public double getRate() {
172    return 0.0;
173  }
174
175  /**
176   * Return the gyro offset value set during calibration to use as a future preset.
177   *
178   * @return the current offset value
179   */
180  public double getOffset() {
181    return 0.0;
182  }
183
184  /**
185   * Return the gyro center value set during calibration to use as a future preset.
186   *
187   * @return the current center value
188   */
189  public int getCenter() {
190    return 0;
191  }
192
193  /**
194   * Set the gyro sensitivity. This takes the number of volts/degree/second sensitivity of the gyro
195   * and uses it in subsequent calculations to allow the code to work with multiple gyros. This
196   * value is typically found in the gyro datasheet.
197   *
198   * @param voltsPerDegreePerSecond The sensitivity in Volts/degree/second.
199   */
200  public void setSensitivity(double voltsPerDegreePerSecond) {}
201
202  /**
203   * Set the size of the neutral zone. Any voltage from the gyro less than this amount from the
204   * center is considered stationary. Setting a deadband will decrease the amount of drift when the
205   * gyro isn't rotating, but will make it less accurate.
206   *
207   * @param volts The size of the deadband in volts
208   */
209  public void setDeadband(double volts) {}
210
211  /**
212   * Gets the analog input for the gyro.
213   *
214   * @return AnalogInput
215   */
216  public AnalogInput getAnalogInput() {
217    return m_analog;
218  }
219
220  @Override
221  public void initSendable(SendableBuilder builder) {
222    builder.setSmartDashboardType("Gyro");
223    builder.addDoubleProperty("Value", this::getAngle, null);
224  }
225}