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 edu.wpi.first.hal.AccumulatorResult;
008import edu.wpi.first.hal.AnalogJNI;
009import edu.wpi.first.hal.FRCNetComm.tResourceType;
010import edu.wpi.first.hal.HAL;
011import edu.wpi.first.hal.SimDevice;
012import edu.wpi.first.hal.util.AllocationException;
013import edu.wpi.first.util.sendable.Sendable;
014import edu.wpi.first.util.sendable.SendableBuilder;
015import edu.wpi.first.util.sendable.SendableRegistry;
016
017/**
018 * Analog channel class.
019 *
020 * <p>Each analog channel is read from hardware as a 12-bit number representing 0V to 5V.
021 *
022 * <p>Connected to each analog channel is an averaging and oversampling engine. This engine
023 * accumulates the specified ( by setAverageBits() and setOversampleBits() ) number of samples
024 * before returning a new value. This is not a sliding window average. The only difference between
025 * the oversampled samples and the averaged samples is that the oversampled samples are simply
026 * accumulated effectively increasing the resolution, while the averaged samples are divided by the
027 * number of samples to retain the resolution, but get more stable values.
028 */
029public class AnalogInput implements Sendable, AutoCloseable {
030  private static final int kAccumulatorSlot = 1;
031  int m_port; // explicit no modifier, private and package accessible.
032  private int m_channel;
033  private static final int[] kAccumulatorChannels = {0, 1};
034  private long m_accumulatorOffset;
035
036  /**
037   * Construct an analog channel.
038   *
039   * @param channel The channel number to represent. 0-3 are on-board 4-7 are on the MXP port.
040   */
041  public AnalogInput(final int channel) {
042    AnalogJNI.checkAnalogInputChannel(channel);
043    m_channel = channel;
044
045    final int portHandle = HAL.getPort((byte) channel);
046    m_port = AnalogJNI.initializeAnalogInputPort(portHandle);
047
048    HAL.report(tResourceType.kResourceType_AnalogChannel, channel + 1);
049    SendableRegistry.addLW(this, "AnalogInput", channel);
050  }
051
052  @Override
053  public void close() {
054    SendableRegistry.remove(this);
055    AnalogJNI.freeAnalogInputPort(m_port);
056    m_port = 0;
057    m_channel = 0;
058    m_accumulatorOffset = 0;
059  }
060
061  /**
062   * Get a sample straight from this channel. The sample is a 12-bit value representing the 0V to 5V
063   * range of the A/D converter. The units are in A/D converter codes. Use GetVoltage() to get the
064   * analog value in calibrated units.
065   *
066   * @return A sample straight from this channel.
067   */
068  public int getValue() {
069    return AnalogJNI.getAnalogValue(m_port);
070  }
071
072  /**
073   * Get a sample from the output of the oversample and average engine for this channel. The sample
074   * is 12-bit + the bits configured in SetOversampleBits(). The value configured in
075   * setAverageBits() will cause this value to be averaged 2^bits number of samples. This is not a
076   * sliding window. The sample will not change until 2^(OversampleBits + AverageBits) samples have
077   * been acquired from this channel. Use getAverageVoltage() to get the analog value in calibrated
078   * units.
079   *
080   * @return A sample from the oversample and average engine for this channel.
081   */
082  public int getAverageValue() {
083    return AnalogJNI.getAnalogAverageValue(m_port);
084  }
085
086  /**
087   * Get a scaled sample straight from this channel. The value is scaled to units of Volts using the
088   * calibrated scaling data from getLSBWeight() and getOffset().
089   *
090   * @return A scaled sample straight from this channel.
091   */
092  public double getVoltage() {
093    return AnalogJNI.getAnalogVoltage(m_port);
094  }
095
096  /**
097   * Get a scaled sample from the output of the oversample and average engine for this channel. The
098   * value is scaled to units of Volts using the calibrated scaling data from getLSBWeight() and
099   * getOffset(). Using oversampling will cause this value to be higher resolution, but it will
100   * update more slowly. Using averaging will cause this value to be more stable, but it will update
101   * more slowly.
102   *
103   * @return A scaled sample from the output of the oversample and average engine for this channel.
104   */
105  public double getAverageVoltage() {
106    return AnalogJNI.getAnalogAverageVoltage(m_port);
107  }
108
109  /**
110   * Get the factory scaling least significant bit weight constant. The least significant bit weight
111   * constant for the channel that was calibrated in manufacturing and stored in an eeprom.
112   *
113   * <p>Volts = ((LSB_Weight * 1e-9) * raw) - (Offset * 1e-9)
114   *
115   * @return Least significant bit weight.
116   */
117  public long getLSBWeight() {
118    return AnalogJNI.getAnalogLSBWeight(m_port);
119  }
120
121  /**
122   * Get the factory scaling offset constant. The offset constant for the channel that was
123   * calibrated in manufacturing and stored in an eeprom.
124   *
125   * <p>Volts = ((LSB_Weight * 1e-9) * raw) - (Offset * 1e-9)
126   *
127   * @return Offset constant.
128   */
129  public int getOffset() {
130    return AnalogJNI.getAnalogOffset(m_port);
131  }
132
133  /**
134   * Get the channel number.
135   *
136   * @return The channel number.
137   */
138  public int getChannel() {
139    return m_channel;
140  }
141
142  /**
143   * Set the number of averaging bits. This sets the number of averaging bits. The actual number of
144   * averaged samples is 2^bits. The averaging is done automatically in the FPGA.
145   *
146   * @param bits The number of averaging bits.
147   */
148  public void setAverageBits(final int bits) {
149    AnalogJNI.setAnalogAverageBits(m_port, bits);
150  }
151
152  /**
153   * Get the number of averaging bits. This gets the number of averaging bits from the FPGA. The
154   * actual number of averaged samples is 2^bits. The averaging is done automatically in the FPGA.
155   *
156   * @return The number of averaging bits.
157   */
158  public int getAverageBits() {
159    return AnalogJNI.getAnalogAverageBits(m_port);
160  }
161
162  /**
163   * Set the number of oversample bits. This sets the number of oversample bits. The actual number
164   * of oversampled values is 2^bits. The oversampling is done automatically in the FPGA.
165   *
166   * @param bits The number of oversample bits.
167   */
168  public void setOversampleBits(final int bits) {
169    AnalogJNI.setAnalogOversampleBits(m_port, bits);
170  }
171
172  /**
173   * Get the number of oversample bits. This gets the number of oversample bits from the FPGA. The
174   * actual number of oversampled values is 2^bits. The oversampling is done automatically in the
175   * FPGA.
176   *
177   * @return The number of oversample bits.
178   */
179  public int getOversampleBits() {
180    return AnalogJNI.getAnalogOversampleBits(m_port);
181  }
182
183  /** Initialize the accumulator. */
184  public void initAccumulator() {
185    if (!isAccumulatorChannel()) {
186      throw new AllocationException(
187          "Accumulators are only available on slot "
188              + kAccumulatorSlot
189              + " on channels "
190              + kAccumulatorChannels[0]
191              + ", "
192              + kAccumulatorChannels[1]);
193    }
194    m_accumulatorOffset = 0;
195    AnalogJNI.initAccumulator(m_port);
196  }
197
198  /**
199   * Set an initial value for the accumulator.
200   *
201   * <p>This will be added to all values returned to the user.
202   *
203   * @param initialValue The value that the accumulator should start from when reset.
204   */
205  public void setAccumulatorInitialValue(long initialValue) {
206    m_accumulatorOffset = initialValue;
207  }
208
209  /** Resets the accumulator to the initial value. */
210  public void resetAccumulator() {
211    AnalogJNI.resetAccumulator(m_port);
212
213    // Wait until the next sample, so the next call to getAccumulator*()
214    // won't have old values.
215    final double sampleTime = 1.0 / getGlobalSampleRate();
216    final double overSamples = 1 << getOversampleBits();
217    final double averageSamples = 1 << getAverageBits();
218    Timer.delay(sampleTime * overSamples * averageSamples);
219  }
220
221  /**
222   * Set the center value of the accumulator.
223   *
224   * <p>The center value is subtracted from each A/D value before it is added to the accumulator.
225   * This is used for the center value of devices like gyros and accelerometers to take the device
226   * offset into account when integrating.
227   *
228   * <p>This center value is based on the output of the oversampled and averaged source the
229   * accumulator channel. Because of this, any non-zero oversample bits will affect the size of the
230   * value for this field.
231   *
232   * @param center The accumulator's center value.
233   */
234  public void setAccumulatorCenter(int center) {
235    AnalogJNI.setAccumulatorCenter(m_port, center);
236  }
237
238  /**
239   * Set the accumulator's deadband.
240   *
241   * @param deadband The deadband size in ADC codes (12-bit value)
242   */
243  public void setAccumulatorDeadband(int deadband) {
244    AnalogJNI.setAccumulatorDeadband(m_port, deadband);
245  }
246
247  /**
248   * Read the accumulated value.
249   *
250   * <p>Read the value that has been accumulating. The accumulator is attached after the oversample
251   * and average engine.
252   *
253   * @return The 64-bit value accumulated since the last Reset().
254   */
255  public long getAccumulatorValue() {
256    return AnalogJNI.getAccumulatorValue(m_port) + m_accumulatorOffset;
257  }
258
259  /**
260   * Read the number of accumulated values.
261   *
262   * <p>Read the count of the accumulated values since the accumulator was last Reset().
263   *
264   * @return The number of times samples from the channel were accumulated.
265   */
266  public long getAccumulatorCount() {
267    return AnalogJNI.getAccumulatorCount(m_port);
268  }
269
270  /**
271   * Read the accumulated value and the number of accumulated values atomically.
272   *
273   * <p>This function reads the value and count from the FPGA atomically. This can be used for
274   * averaging.
275   *
276   * @param result AccumulatorResult object to store the results in.
277   */
278  public void getAccumulatorOutput(AccumulatorResult result) {
279    if (result == null) {
280      throw new IllegalArgumentException("Null parameter `result'");
281    }
282    if (!isAccumulatorChannel()) {
283      throw new IllegalArgumentException(
284          "Channel " + m_channel + " is not an accumulator channel.");
285    }
286    AnalogJNI.getAccumulatorOutput(m_port, result);
287    result.value += m_accumulatorOffset;
288  }
289
290  /**
291   * Is the channel attached to an accumulator.
292   *
293   * @return The analog channel is attached to an accumulator.
294   */
295  public boolean isAccumulatorChannel() {
296    for (int channel : kAccumulatorChannels) {
297      if (m_channel == channel) {
298        return true;
299      }
300    }
301    return false;
302  }
303
304  /**
305   * Set the sample rate per channel.
306   *
307   * <p>This is a global setting for all channels. The maximum rate is 500kS/s divided by the number
308   * of channels in use. This is 62500 samples/s per channel if all 8 channels are used.
309   *
310   * @param samplesPerSecond The number of samples per second.
311   */
312  public static void setGlobalSampleRate(final double samplesPerSecond) {
313    AnalogJNI.setAnalogSampleRate(samplesPerSecond);
314  }
315
316  /**
317   * Get the current sample rate.
318   *
319   * <p>This assumes one entry in the scan list. This is a global setting for all channels.
320   *
321   * @return Sample rate.
322   */
323  public static double getGlobalSampleRate() {
324    return AnalogJNI.getAnalogSampleRate();
325  }
326
327  /**
328   * Indicates this input is used by a simulated device.
329   *
330   * @param device simulated device handle
331   */
332  public void setSimDevice(SimDevice device) {
333    AnalogJNI.setAnalogInputSimDevice(m_port, device.getNativeHandle());
334  }
335
336  @Override
337  public void initSendable(SendableBuilder builder) {
338    builder.setSmartDashboardType("Analog Input");
339    builder.addDoubleProperty("Value", this::getAverageVoltage, null);
340  }
341}