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.math.filter;
006
007import edu.wpi.first.math.MathSharedStore;
008
009/**
010 * A simple debounce filter for boolean streams. Requires that the boolean change value from
011 * baseline for a specified period of time before the filtered value changes.
012 */
013public class Debouncer {
014  /** Type of debouncing to perform. */
015  public enum DebounceType {
016    /** Rising edge. */
017    kRising,
018    /** Falling edge. */
019    kFalling,
020    /** Both rising and falling edges. */
021    kBoth
022  }
023
024  private final double m_debounceTimeSeconds;
025  private final DebounceType m_debounceType;
026  private boolean m_baseline;
027
028  private double m_prevTimeSeconds;
029
030  /**
031   * Creates a new Debouncer.
032   *
033   * @param debounceTime The number of seconds the value must change from baseline for the filtered
034   *     value to change.
035   * @param type Which type of state change the debouncing will be performed on.
036   */
037  public Debouncer(double debounceTime, DebounceType type) {
038    m_debounceTimeSeconds = debounceTime;
039    m_debounceType = type;
040
041    resetTimer();
042
043    switch (m_debounceType) {
044      case kBoth: // fall-through
045      case kRising:
046        m_baseline = false;
047        break;
048      case kFalling:
049        m_baseline = true;
050        break;
051      default:
052        throw new IllegalArgumentException("Invalid debounce type!");
053    }
054  }
055
056  /**
057   * Creates a new Debouncer. Baseline value defaulted to "false."
058   *
059   * @param debounceTime The number of seconds the value must change from baseline for the filtered
060   *     value to change.
061   */
062  public Debouncer(double debounceTime) {
063    this(debounceTime, DebounceType.kRising);
064  }
065
066  private void resetTimer() {
067    m_prevTimeSeconds = MathSharedStore.getTimestamp();
068  }
069
070  private boolean hasElapsed() {
071    return MathSharedStore.getTimestamp() - m_prevTimeSeconds >= m_debounceTimeSeconds;
072  }
073
074  /**
075   * Applies the debouncer to the input stream.
076   *
077   * @param input The current value of the input stream.
078   * @return The debounced value of the input stream.
079   */
080  public boolean calculate(boolean input) {
081    if (input == m_baseline) {
082      resetTimer();
083    }
084
085    if (hasElapsed()) {
086      if (m_debounceType == DebounceType.kBoth) {
087        m_baseline = input;
088        resetTimer();
089      }
090      return input;
091    } else {
092      return m_baseline;
093    }
094  }
095}