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