001/*----------------------------------------------------------------------------*/
002/* Copyright (c) 2015-2018 FIRST. All Rights Reserved.                        */
003/* Open Source Software - may be modified and shared by FRC teams. The code   */
004/* must be accompanied by the FIRST BSD license file in the root directory of */
005/* the project.                                                               */
006/*----------------------------------------------------------------------------*/
007
008package edu.wpi.first.wpilibj;
009
010import java.util.concurrent.locks.Lock;
011import java.util.concurrent.locks.ReentrantLock;
012
013import edu.wpi.first.wpilibj.hal.DigitalGlitchFilterJNI;
014import edu.wpi.first.wpilibj.hal.FRCNetComm.tResourceType;
015import edu.wpi.first.wpilibj.smartdashboard.SendableBuilder;
016import edu.wpi.first.wpilibj.hal.HAL;
017
018/**
019 * Class to enable glitch filtering on a set of digital inputs. This class will manage adding and
020 * removing digital inputs from a FPGA glitch filter. The filter lets the user configure the time
021 * that an input must remain high or low before it is classified as high or low.
022 */
023public class DigitalGlitchFilter extends SensorBase {
024  /**
025   * Configures the Digital Glitch Filter to its default settings.
026   */
027  public DigitalGlitchFilter() {
028    synchronized (m_mutex) {
029      int index = 0;
030      while (m_filterAllocated[index] && index < m_filterAllocated.length) {
031        index++;
032      }
033      if (index != m_filterAllocated.length) {
034        m_channelIndex = index;
035        m_filterAllocated[index] = true;
036        HAL.report(tResourceType.kResourceType_DigitalFilter,
037            m_channelIndex, 0);
038        setName("DigitalGlitchFilter", index);
039      }
040    }
041  }
042
043  /**
044   * Free the resources used by this object.
045   */
046  public void free() {
047    super.free();
048    if (m_channelIndex >= 0) {
049      synchronized (m_mutex) {
050        m_filterAllocated[m_channelIndex] = false;
051      }
052      m_channelIndex = -1;
053    }
054  }
055
056  private static void setFilter(DigitalSource input, int channelIndex) {
057    if (input != null) { // Counter might have just one input
058      // analog triggers are not supported for DigitalGlitchFilters
059      if (input.isAnalogTrigger()) {
060        throw new IllegalStateException("Analog Triggers not supported for DigitalGlitchFilters");
061      }
062      DigitalGlitchFilterJNI.setFilterSelect(input.getPortHandleForRouting(), channelIndex);
063
064      int selected = DigitalGlitchFilterJNI.getFilterSelect(input.getPortHandleForRouting());
065      if (selected != channelIndex) {
066        throw new IllegalStateException("DigitalGlitchFilterJNI.setFilterSelect("
067            + channelIndex + ") failed -> " + selected);
068      }
069    }
070  }
071
072  /**
073   * Assigns the DigitalSource to this glitch filter.
074   *
075   * @param input The DigitalSource to add.
076   */
077  public void add(DigitalSource input) {
078    setFilter(input, m_channelIndex + 1);
079  }
080
081  /**
082   * Assigns the Encoder to this glitch filter.
083   *
084   * @param input The Encoder to add.
085   */
086  public void add(Encoder input) {
087    add(input.m_aSource);
088    add(input.m_bSource);
089  }
090
091  /**
092   * Assigns the Counter to this glitch filter.
093   *
094   * @param input The Counter to add.
095   */
096  public void add(Counter input) {
097    add(input.m_upSource);
098    add(input.m_downSource);
099  }
100
101  /**
102   * Removes this filter from the given digital input.
103   *
104   * @param input The DigitalSource to stop filtering.
105   */
106  public void remove(DigitalSource input) {
107    setFilter(input, 0);
108  }
109
110  /**
111   * Removes this filter from the given Encoder.
112   *
113   * @param input the Encoder to stop filtering.
114   */
115  public void remove(Encoder input) {
116    remove(input.m_aSource);
117    remove(input.m_bSource);
118  }
119
120  /**
121   * Removes this filter from the given Counter.
122   *
123   * @param input The Counter to stop filtering.
124   */
125  public void remove(Counter input) {
126    remove(input.m_upSource);
127    remove(input.m_downSource);
128  }
129
130  /**
131   * Sets the number of FPGA cycles that the input must hold steady to pass through this glitch
132   * filter.
133   *
134   * @param fpgaCycles The number of FPGA cycles.
135   */
136  public void setPeriodCycles(int fpgaCycles) {
137    DigitalGlitchFilterJNI.setFilterPeriod(m_channelIndex, fpgaCycles);
138  }
139
140  /**
141   * Sets the number of nanoseconds that the input must hold steady to pass through this glitch
142   * filter.
143   *
144   * @param nanoseconds The number of nanoseconds.
145   */
146  public void setPeriodNanoSeconds(long nanoseconds) {
147    int fpgaCycles = (int) (nanoseconds * kSystemClockTicksPerMicrosecond / 4
148        / 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
172        / (long) (kSystemClockTicksPerMicrosecond / 4);
173  }
174
175  @SuppressWarnings("PMD.UnusedFormalParameter")
176  public void initSendable(SendableBuilder builder) {
177  }
178
179  private int m_channelIndex = -1;
180  private static final Lock m_mutex = new ReentrantLock(true);
181  private static final boolean[] m_filterAllocated = new boolean[3];
182}