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.DigitalGlitchFilterJNI;
008import edu.wpi.first.hal.FRCNetComm.tResourceType;
009import edu.wpi.first.hal.HAL;
010import edu.wpi.first.hal.util.AllocationException;
011import edu.wpi.first.util.sendable.Sendable;
012import edu.wpi.first.util.sendable.SendableBuilder;
013import edu.wpi.first.util.sendable.SendableRegistry;
014import java.util.concurrent.locks.Lock;
015import java.util.concurrent.locks.ReentrantLock;
016
017/**
018 * Class to enable glitch filtering on a set of digital inputs. This class will manage adding and
019 * removing digital inputs from an FPGA glitch filter. The filter lets the user configure the time
020 * that an input must remain high or low before it is classified as high or low.
021 */
022public class DigitalGlitchFilter implements Sendable, AutoCloseable {
023  /** Configures the Digital Glitch Filter to its default settings. */
024  @SuppressWarnings("this-escape")
025  public DigitalGlitchFilter() {
026    m_channelIndex = allocateFilterIndex();
027    if (m_channelIndex < 0) {
028      throw new AllocationException("No filters available to allocate.");
029    }
030    HAL.report(tResourceType.kResourceType_DigitalGlitchFilter, m_channelIndex + 1, 0);
031    SendableRegistry.addLW(this, "DigitalGlitchFilter", m_channelIndex);
032  }
033
034  @Override
035  public void close() {
036    SendableRegistry.remove(this);
037    if (m_channelIndex >= 0) {
038      m_mutex.lock();
039      try {
040        m_filterAllocated[m_channelIndex] = false;
041      } finally {
042        m_mutex.unlock();
043      }
044
045      m_channelIndex = -1;
046    }
047  }
048
049  /**
050   * Allocates the next available filter index, or -1 if there are no filters available.
051   *
052   * @return the filter index
053   */
054  private static int allocateFilterIndex() {
055    m_mutex.lock();
056    try {
057      for (int index = 0; index < m_filterAllocated.length; index++) {
058        if (!m_filterAllocated[index]) {
059          m_filterAllocated[index] = true;
060          return index;
061        }
062      }
063    } finally {
064      m_mutex.unlock();
065    }
066    return -1;
067  }
068
069  private static void setFilter(DigitalSource input, int channelIndex) {
070    if (input != null) { // Counter might have just one input
071      // analog triggers are not supported for DigitalGlitchFilters
072      if (input.isAnalogTrigger()) {
073        throw new IllegalStateException("Analog Triggers not supported for DigitalGlitchFilters");
074      }
075      DigitalGlitchFilterJNI.setFilterSelect(input.getPortHandleForRouting(), channelIndex);
076
077      int selected = DigitalGlitchFilterJNI.getFilterSelect(input.getPortHandleForRouting());
078      if (selected != channelIndex) {
079        throw new IllegalStateException(
080            "DigitalGlitchFilterJNI.setFilterSelect(" + channelIndex + ") failed -> " + selected);
081      }
082    }
083  }
084
085  /**
086   * Assigns the DigitalSource to this glitch filter.
087   *
088   * @param input The DigitalSource to add.
089   */
090  public void add(DigitalSource input) {
091    setFilter(input, m_channelIndex + 1);
092  }
093
094  /**
095   * Assigns the Encoder to this glitch filter.
096   *
097   * @param input The Encoder to add.
098   */
099  public void add(Encoder input) {
100    add(input.m_aSource);
101    add(input.m_bSource);
102  }
103
104  /**
105   * Assigns the Counter to this glitch filter.
106   *
107   * @param input The Counter to add.
108   */
109  public void add(Counter input) {
110    add(input.m_upSource);
111    add(input.m_downSource);
112  }
113
114  /**
115   * Removes this filter from the given digital input.
116   *
117   * @param input The DigitalSource to stop filtering.
118   */
119  public void remove(DigitalSource input) {
120    setFilter(input, 0);
121  }
122
123  /**
124   * Removes this filter from the given Encoder.
125   *
126   * @param input the Encoder to stop filtering.
127   */
128  public void remove(Encoder input) {
129    remove(input.m_aSource);
130    remove(input.m_bSource);
131  }
132
133  /**
134   * Removes this filter from the given Counter.
135   *
136   * @param input The Counter to stop filtering.
137   */
138  public void remove(Counter input) {
139    remove(input.m_upSource);
140    remove(input.m_downSource);
141  }
142
143  /**
144   * Sets the number of FPGA cycles that the input must hold steady to pass through this glitch
145   * filter.
146   *
147   * @param fpgaCycles The number of FPGA cycles.
148   */
149  public void setPeriodCycles(int fpgaCycles) {
150    DigitalGlitchFilterJNI.setFilterPeriod(m_channelIndex, fpgaCycles);
151  }
152
153  /**
154   * Sets the number of nanoseconds that the input must hold steady to pass through this glitch
155   * filter.
156   *
157   * @param nanoseconds The number of nanoseconds.
158   */
159  public void setPeriodNanoSeconds(long nanoseconds) {
160    int fpgaCycles = (int) (nanoseconds * SensorUtil.kSystemClockTicksPerMicrosecond / 4 / 1000);
161    setPeriodCycles(fpgaCycles);
162  }
163
164  /**
165   * Gets the number of FPGA cycles that the input must hold steady to pass through this glitch
166   * filter.
167   *
168   * @return The number of cycles.
169   */
170  public int getPeriodCycles() {
171    return DigitalGlitchFilterJNI.getFilterPeriod(m_channelIndex);
172  }
173
174  /**
175   * Gets the number of nanoseconds that the input must hold steady to pass through this glitch
176   * filter.
177   *
178   * @return The number of nanoseconds.
179   */
180  public long getPeriodNanoSeconds() {
181    int fpgaCycles = getPeriodCycles();
182
183    return fpgaCycles * 1000L / (SensorUtil.kSystemClockTicksPerMicrosecond / 4);
184  }
185
186  @Override
187  public void initSendable(SendableBuilder builder) {}
188
189  private int m_channelIndex;
190  private static final Lock m_mutex = new ReentrantLock(true);
191  private static final boolean[] m_filterAllocated = new boolean[3];
192}