-
Notifications
You must be signed in to change notification settings - Fork 671
Add multi tap boolean stream filter and multi tap trigger modifier (double tap detector) #8307
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
MichaelLesirge
wants to merge
14
commits into
wpilibsuite:main
Choose a base branch
from
MichaelLesirge:tap-counting-filter
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+470
−0
Open
Changes from 8 commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
7680126
create tap counting filter
MichaelLesirge 5a9ad11
Rename TapCountFilter to MultiTapFilter
MichaelLesirge e811954
rename files for MultiTap name
MichaelLesirge a746dc5
Add multi-tap functionality to Trigger class
MichaelLesirge c904a83
remove <p> tags from C++ docstring
MichaelLesirge 2e3c196
fixes after gradlew check
MichaelLesirge 973a6a9
update logic
MichaelLesirge a0da978
add to documentation for multi-tap trigger functionality
MichaelLesirge f322383
reset now in C++ test
MichaelLesirge 7eb4440
wpiformat and documentation fix
MichaelLesirge 18fbb28
rename MultiTapFilter to EdgeCounterFilter
MichaelLesirge 6ef1c8c
fix param names
MichaelLesirge fcb444f
wpiformat
MichaelLesirge c7c7d8b
gradlew spotlessApply
MichaelLesirge File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
117 changes: 117 additions & 0 deletions
117
wpimath/src/main/java/edu/wpi/first/math/filter/MultiTapFilter.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,117 @@ | ||
| package edu.wpi.first.math.filter; | ||
|
|
||
| import edu.wpi.first.math.MathSharedStore; | ||
|
|
||
| /** | ||
| * A simple tap counting filter for boolean streams. Requires that the boolean change value to true | ||
| * for a specified number of times within a specified time window after the first rising edge before | ||
| * the filtered value changes. | ||
| * | ||
| * <p>The filter activates when the input has risen (transitioned from false to true) the required | ||
| * number of times within the time window. Once activated, the output remains true as long as the | ||
| * input is true. The tap count resets when the time window expires or when the input goes false | ||
| * after activation. | ||
| * | ||
| * <p>Input must be stable; consider using a Debouncer before this filter to avoid counting noise as | ||
| * multiple taps. | ||
| */ | ||
| public class MultiTapFilter { | ||
|
|
||
| private int m_requiredTaps; | ||
| private double m_tapWindowSeconds; | ||
|
|
||
| private double m_firstTapTimeSeconds; | ||
| private int m_currentTapCount; | ||
|
|
||
| private boolean m_lastInput; | ||
|
|
||
| /** | ||
| * Creates a new MultiTapFilter. | ||
| * | ||
| * @param requiredTaps The number of rising edges (taps) required before the output goes true. | ||
| * @param tapWindowSeconds The maximum time window in which all required taps must occur after the | ||
| * first rising edge. | ||
| */ | ||
| public MultiTapFilter(int requiredTaps, double tapWindowSeconds) { | ||
| m_requiredTaps = requiredTaps; | ||
| m_tapWindowSeconds = tapWindowSeconds; | ||
|
|
||
| resetTimer(); | ||
| } | ||
|
|
||
| private void resetTimer() { | ||
| m_firstTapTimeSeconds = MathSharedStore.getTimestamp(); | ||
| } | ||
|
|
||
| private boolean hasElapsed() { | ||
| return MathSharedStore.getTimestamp() - m_firstTapTimeSeconds >= m_tapWindowSeconds; | ||
| } | ||
|
|
||
| /** | ||
| * Applies the tap counter filter to the input stream. | ||
| * | ||
| * @param input The current value of the input stream. | ||
| * @return True if the required number of taps have occurred within the time window and the input | ||
| * is currently true; false otherwise. | ||
| */ | ||
| public boolean calculate(boolean input) { | ||
| boolean enoughTaps = m_currentTapCount >= m_requiredTaps; | ||
|
|
||
| boolean expired = hasElapsed() && !enoughTaps; | ||
| boolean activationEnded = !input && enoughTaps; | ||
|
|
||
| if (expired || activationEnded) { | ||
| m_currentTapCount = 0; | ||
| } | ||
|
|
||
| if (input && !m_lastInput) { | ||
| if (m_currentTapCount == 0) { | ||
| resetTimer(); // Start timer on first rising edge | ||
| } | ||
|
|
||
| m_currentTapCount++; | ||
| } | ||
|
|
||
| m_lastInput = input; | ||
|
|
||
| return input && m_currentTapCount >= m_requiredTaps; | ||
| } | ||
|
|
||
| /** | ||
| * Sets the tap window duration. | ||
| * | ||
| * @param tapWindowSeconds The maximum time window in which all required taps must occur after the | ||
| * first rising edge. | ||
| */ | ||
| public void setTapWindowSeconds(double tapWindowSeconds) { | ||
| m_tapWindowSeconds = tapWindowSeconds; | ||
| } | ||
|
|
||
| /** | ||
| * Gets the tap window duration. | ||
| * | ||
| * @return The maximum time window in which all required taps must occur after the first rising | ||
| * edge. | ||
| */ | ||
| public double getTapWindowSeconds() { | ||
| return m_tapWindowSeconds; | ||
| } | ||
|
|
||
| /** | ||
| * Sets the required number of taps. | ||
| * | ||
| * @param requiredTaps The number of rising edges (taps) required before the output goes true. | ||
| */ | ||
| public void setRequiredTaps(int requiredTaps) { | ||
| m_requiredTaps = requiredTaps; | ||
| } | ||
|
|
||
| /** | ||
| * Gets the required number of taps. | ||
| * | ||
| * @return The number of rising edges (taps) required before the output goes true. | ||
| */ | ||
| public int getRequiredTaps() { | ||
| return m_requiredTaps; | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| // Copyright (c) FIRST and other WPILib contributors. | ||
| // Open Source Software; you can modify and/or share it under the terms of | ||
| // the WPILib BSD license file in the root directory of this project. | ||
|
|
||
| #include "frc/filter/MultiTapFilter.h" | ||
|
|
||
| #include "wpimath/MathShared.h" | ||
|
|
||
| using namespace frc; | ||
|
|
||
| MultiTapFilter::MultiTapFilter(int requiredTaps, units::second_t tapWindow) | ||
| : m_requiredTaps(requiredTaps), m_tapWindow(tapWindow) { | ||
| ResetTimer(); | ||
| } | ||
|
|
||
| void MultiTapFilter::ResetTimer() { | ||
| m_firstTapTime = wpi::math::MathSharedStore::GetTimestamp(); | ||
| } | ||
|
|
||
| bool MultiTapFilter::HasElapsed() const { | ||
| return wpi::math::MathSharedStore::GetTimestamp() - m_firstTapTime >= | ||
| m_tapWindow; | ||
| } | ||
|
|
||
| bool MultiTapFilter::Calculate(bool input) { | ||
| bool enoughTaps = m_currentTapCount >= m_requiredTaps; | ||
|
|
||
| bool expired = HasElapsed() && !enoughTaps; | ||
| bool activationEnded = !input && enoughTaps; | ||
|
|
||
| if (expired || activationEnded) { | ||
| m_currentTapCount = 0; | ||
| } | ||
|
|
||
| if (input && !m_lastInput) { | ||
| if (m_currentTapCount == 0) { | ||
| ResetTimer(); // Start timer on first rising edge | ||
| } | ||
|
|
||
| m_currentTapCount++; | ||
| } | ||
|
|
||
| m_lastInput = input; | ||
|
|
||
| return input && m_currentTapCount >= m_requiredTaps; | ||
| } |
95 changes: 95 additions & 0 deletions
95
wpimath/src/main/native/include/frc/filter/MultiTapFilter.h
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| // Copyright (c) FIRST and other WPILib contributors. | ||
| // Open Source Software; you can modify and/or share it under the terms of | ||
| // the WPILib BSD license file in the root directory of this project. | ||
|
|
||
| #pragma once | ||
|
|
||
| #include <wpi/SymbolExports.h> | ||
|
|
||
| #include "units/time.h" | ||
|
|
||
| namespace frc { | ||
| /** | ||
| * A simple tap counting filter for boolean streams. Requires that the boolean | ||
MichaelLesirge marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| * change value to true for a specified number of times within a specified time | ||
| * window after the first rising edge before the filtered value changes. | ||
| * | ||
| * The filter activates when the input has risen (transitioned from false to | ||
| * true) the required number of times within the time window. Once activated, | ||
| * the output remains true as long as the input is true. The tap count resets | ||
| * when the time window expires or when the input goes false after activation. | ||
| * | ||
| * Input must be stable; consider using a Debouncer before this filter to | ||
| * avoid counting noise as multiple taps. | ||
| */ | ||
| class WPILIB_DLLEXPORT MultiTapFilter { | ||
| public: | ||
| /** | ||
| * Creates a new MultiTapFilter. | ||
| * | ||
| * @param requiredTaps The number of rising edges (taps) required before the | ||
| * output goes true. | ||
| * @param tapWindow The maximum time window in which all required taps must | ||
| * occur after the first rising edge. | ||
| */ | ||
| explicit MultiTapFilter(int requiredTaps, units::second_t tapWindow); | ||
|
|
||
| /** | ||
| * Applies the tap counter filter to the input stream. | ||
| * | ||
| * @param input The current value of the input stream. | ||
| * @return True if the required number of taps have occurred within the time | ||
| * window and the input is currently true; false otherwise. | ||
| */ | ||
| bool Calculate(bool input); | ||
|
|
||
| /** | ||
| * Sets the tap window duration. | ||
| * | ||
| * @param tapWindow The maximum time window in which all required taps must | ||
| * occur after the first rising edge. | ||
| */ | ||
| constexpr void SetTapWindow(units::second_t tapWindow) { | ||
| m_tapWindow = tapWindow; | ||
| } | ||
|
|
||
| /** | ||
| * Gets the tap window duration. | ||
| * | ||
| * @return The maximum time window in which all required taps must occur | ||
| * after the first rising edge. | ||
| */ | ||
| constexpr units::second_t GetTapWindow() const { return m_tapWindow; } | ||
|
|
||
| /** | ||
| * Sets the required number of taps. | ||
| * | ||
| * @param requiredTaps The number of rising edges (taps) required before the | ||
| * output goes true. | ||
| */ | ||
| constexpr void SetRequiredTaps(int requiredTaps) { | ||
| m_requiredTaps = requiredTaps; | ||
| } | ||
|
|
||
| /** | ||
| * Gets the required number of taps. | ||
| * | ||
| * @return The number of rising edges (taps) required before the output goes | ||
| * true. | ||
| */ | ||
| constexpr int GetRequiredTaps() const { return m_requiredTaps; } | ||
|
|
||
| private: | ||
| int m_requiredTaps; | ||
| units::second_t m_tapWindow; | ||
|
|
||
| units::second_t m_firstTapTime; | ||
| int m_currentTapCount = 0; | ||
|
|
||
| bool m_lastInput = false; | ||
|
|
||
| void ResetTimer(); | ||
|
|
||
| bool HasElapsed() const; | ||
| }; | ||
| } // namespace frc | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.