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 a 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  public DigitalGlitchFilter() {
024    m_mutex.lock();
025    try {
026      int index = 0;
027      while (m_filterAllocated[index] && index < m_filterAllocated.length) {
028        index++;
029      }
030      if (index != m_filterAllocated.length) {
031        m_channelIndex = index;
032        m_filterAllocated[index] = true;
033        HAL.report(tResourceType.kResourceType_DigitalGlitchFilter, m_channelIndex + 1, 0);
034        SendableRegistry.addLW(this, "DigitalGlitchFilter", index);
035      }
036    } finally {
037      m_mutex.unlock();
038    }
039  }
040
041  @Override
042  public void close() {
043    SendableRegistry.remove(this);
044    if (m_channelIndex >= 0) {
045      m_mutex.lock();
046      try {
047        m_filterAllocated[m_channelIndex] = false;
048      } finally {
049        m_mutex.unlock();
050      }
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(
067            "DigitalGlitchFilterJNI.setFilterSelect(" + 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 * SensorUtil.kSystemClockTicksPerMicrosecond / 4 / 1000);
148    setPeriodCycles(fpgaCycles);
149  }
150
151  /**
152   * Gets the number of FPGA cycles that the input must hold steady to pass through this glitch
153   * filter.
154   *
155   * @return The number of cycles.
156   */
157  public int getPeriodCycles() {
158    return DigitalGlitchFilterJNI.getFilterPeriod(m_channelIndex);
159  }
160
161  /**
162   * Gets the number of nanoseconds that the input must hold steady to pass through this glitch
163   * filter.
164   *
165   * @return The number of nanoseconds.
166   */
167  public long getPeriodNanoSeconds() {
168    int fpgaCycles = getPeriodCycles();
169
170    return (long) fpgaCycles * 1000L / (long) (SensorUtil.kSystemClockTicksPerMicrosecond / 4);
171  }
172
173  @Override
174  public void initSendable(SendableBuilder builder) {}
175
176  private int m_channelIndex = -1;
177  private static final Lock m_mutex = new ReentrantLock(true);
178  private static final boolean[] m_filterAllocated = new boolean[3];
179}