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        m_channelIndex = -1;
042      } finally {
043        m_mutex.unlock();
044      }
045    }
046  }
047
048  /**
049   * Allocates the next available filter index, or -1 if there are no filters available.
050   *
051   * @return the filter index
052   */
053  private static int allocateFilterIndex() {
054    m_mutex.lock();
055    try {
056      for (int index = 0; index < m_filterAllocated.length; index++) {
057        if (!m_filterAllocated[index]) {
058          m_filterAllocated[index] = true;
059          return index;
060        }
061      }
062    } finally {
063      m_mutex.unlock();
064    }
065    return -1;
066  }
067
068  private static void setFilter(DigitalSource input, int channelIndex) {
069    if (input != null) { // Counter might have just one input
070      // analog triggers are not supported for DigitalGlitchFilters
071      if (input.isAnalogTrigger()) {
072        throw new IllegalStateException("Analog Triggers not supported for DigitalGlitchFilters");
073      }
074      DigitalGlitchFilterJNI.setFilterSelect(input.getPortHandleForRouting(), channelIndex);
075
076      int selected = DigitalGlitchFilterJNI.getFilterSelect(input.getPortHandleForRouting());
077      if (selected != channelIndex) {
078        throw new IllegalStateException(
079            "DigitalGlitchFilterJNI.setFilterSelect(" + channelIndex + ") failed -> " + selected);
080      }
081    }
082  }
083
084  /**
085   * Assigns the DigitalSource to this glitch filter.
086   *
087   * @param input The DigitalSource to add.
088   */
089  public void add(DigitalSource input) {
090    setFilter(input, m_channelIndex + 1);
091  }
092
093  /**
094   * Assigns the Encoder to this glitch filter.
095   *
096   * @param input The Encoder to add.
097   */
098  public void add(Encoder input) {
099    add(input.m_aSource);
100    add(input.m_bSource);
101  }
102
103  /**
104   * Assigns the Counter to this glitch filter.
105   *
106   * @param input The Counter to add.
107   */
108  public void add(Counter input) {
109    add(input.m_upSource);
110    add(input.m_downSource);
111  }
112
113  /**
114   * Removes this filter from the given digital input.
115   *
116   * @param input The DigitalSource to stop filtering.
117   */
118  public void remove(DigitalSource input) {
119    setFilter(input, 0);
120  }
121
122  /**
123   * Removes this filter from the given Encoder.
124   *
125   * @param input the Encoder to stop filtering.
126   */
127  public void remove(Encoder input) {
128    remove(input.m_aSource);
129    remove(input.m_bSource);
130  }
131
132  /**
133   * Removes this filter from the given Counter.
134   *
135   * @param input The Counter to stop filtering.
136   */
137  public void remove(Counter input) {
138    remove(input.m_upSource);
139    remove(input.m_downSource);
140  }
141
142  /**
143   * Sets the number of FPGA cycles that the input must hold steady to pass through this glitch
144   * filter.
145   *
146   * @param fpgaCycles The number of FPGA cycles.
147   */
148  public void setPeriodCycles(int fpgaCycles) {
149    DigitalGlitchFilterJNI.setFilterPeriod(m_channelIndex, fpgaCycles);
150  }
151
152  /**
153   * Sets the number of nanoseconds that the input must hold steady to pass through this glitch
154   * filter.
155   *
156   * @param nanoseconds The number of nanoseconds.
157   */
158  public void setPeriodNanoSeconds(long nanoseconds) {
159    int fpgaCycles = (int) (nanoseconds * SensorUtil.kSystemClockTicksPerMicrosecond / 4 / 1000);
160    setPeriodCycles(fpgaCycles);
161  }
162
163  /**
164   * Gets the number of FPGA cycles that the input must hold steady to pass through this glitch
165   * filter.
166   *
167   * @return The number of cycles.
168   */
169  public int getPeriodCycles() {
170    return DigitalGlitchFilterJNI.getFilterPeriod(m_channelIndex);
171  }
172
173  /**
174   * Gets the number of nanoseconds that the input must hold steady to pass through this glitch
175   * filter.
176   *
177   * @return The number of nanoseconds.
178   */
179  public long getPeriodNanoSeconds() {
180    int fpgaCycles = getPeriodCycles();
181
182    return fpgaCycles * 1000L / (SensorUtil.kSystemClockTicksPerMicrosecond / 4);
183  }
184
185  @Override
186  public void initSendable(SendableBuilder builder) {}
187
188  private int m_channelIndex;
189  private static final Lock m_mutex = new ReentrantLock(true);
190  private static final boolean[] m_filterAllocated = new boolean[3];
191}