mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-20 00:51:42 +00:00
Add return-to-zero test for LinearDigitalFilter moving average (#751)
Ensures LinearDigitalFilter moving average returns to zero after non-zero values are inserted. Tests for issue #642
This commit is contained in:
committed by
Peter Johnson
parent
11f37683c3
commit
7a0dd9baa9
@@ -58,7 +58,8 @@ public class FilterOutputTest extends AbstractComsSetup {
|
||||
return Arrays.asList(new FilterOutputFixture<?>[][]{
|
||||
{TestBench.getInstance().getSinglePoleIIROutputFixture()},
|
||||
{TestBench.getInstance().getHighPassOutputFixture()},
|
||||
{TestBench.getInstance().getMovAvgOutputFixture()}});
|
||||
{TestBench.getInstance().getMovAvgOutputFixture()},
|
||||
{TestBench.getInstance().getPulseFixture()}});
|
||||
}
|
||||
|
||||
@Before
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
package edu.wpi.first.wpilibj.fixtures;
|
||||
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.util.function.DoubleFunction;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import edu.wpi.first.wpilibj.PIDSource;
|
||||
@@ -15,11 +16,10 @@ import edu.wpi.first.wpilibj.PIDSourceType;
|
||||
import edu.wpi.first.wpilibj.test.TestBench;
|
||||
|
||||
/**
|
||||
* Represents a filterphysically connected Motor and Encoder to allow for unit tests on these
|
||||
* different pairs<br> Designed to allow the user to easily setup and tear down the fixture to allow
|
||||
* for reuse. This class should be explicitly instantiated in the TestBed class to allow any test to
|
||||
* access this fixture. This allows tests to be mailable so that you can easily reconfigure the
|
||||
* physical testbed without breaking the tests.
|
||||
* Represents a filter to allow for unit tests on them<br> Designed to allow the user to easily
|
||||
* setup and tear down the fixture to allow for reuse. This class should be explicitly instantiated
|
||||
* in the TestBed class to allow any test to access this fixture. This allows tests to be mailable
|
||||
* so that you can easily reconfigure the physical testbed without breaking the tests.
|
||||
*/
|
||||
public abstract class FilterOutputFixture<T extends PIDSource> implements ITestFixture {
|
||||
private static final Logger logger = Logger.getLogger(FilterOutputFixture.class.getName());
|
||||
@@ -40,24 +40,37 @@ public abstract class FilterOutputFixture<T extends PIDSource> implements ITestF
|
||||
return m_expectedOutput;
|
||||
}
|
||||
|
||||
public static DoubleFunction<Double> getData = new DoubleFunction<Double>() {
|
||||
@Override
|
||||
@SuppressWarnings("ParameterName")
|
||||
public Double apply(double t) {
|
||||
return 100.0 * Math.sin(2.0 * Math.PI * t) + 20.0 * Math.cos(50.0 * Math.PI * t);
|
||||
}
|
||||
};
|
||||
|
||||
public static DoubleFunction<Double> getPulseData = new DoubleFunction<Double>() {
|
||||
@Override
|
||||
@SuppressWarnings("ParameterName")
|
||||
public Double apply(double t) {
|
||||
if (Math.abs(t - 1.0) < 0.001) {
|
||||
return 1.0;
|
||||
} else {
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Where the implementer of this class should pass the filter constructor.
|
||||
*/
|
||||
protected abstract T giveFilter(PIDSource source);
|
||||
protected abstract T giveFilter();
|
||||
|
||||
private void initialize() {
|
||||
synchronized (this) {
|
||||
if (!m_initialized) {
|
||||
m_initialized = true; // This ensures it is only initialized once
|
||||
|
||||
m_data = new DataWrapper() {
|
||||
@Override
|
||||
@SuppressWarnings("ParameterName")
|
||||
public double getData(double t) {
|
||||
return 100.0 * Math.sin(2.0 * Math.PI * t) + 20.0 * Math.cos(50.0 * Math.PI * t);
|
||||
}
|
||||
};
|
||||
m_filter = giveFilter(m_data);
|
||||
m_filter = giveFilter();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -78,16 +91,6 @@ public abstract class FilterOutputFixture<T extends PIDSource> implements ITestF
|
||||
return m_filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the data wrapper for this object.
|
||||
*
|
||||
* @return the data wrapper that this object refers too
|
||||
*/
|
||||
public DataWrapper getDataWrapper() {
|
||||
initialize();
|
||||
return m_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the name of the filter that this object refers to.
|
||||
*
|
||||
@@ -122,13 +125,15 @@ public abstract class FilterOutputFixture<T extends PIDSource> implements ITestF
|
||||
return string.toString();
|
||||
}
|
||||
|
||||
public abstract class DataWrapper implements PIDSource {
|
||||
public class DataWrapper implements PIDSource {
|
||||
// Make sure first call to pidGet() uses count == 0
|
||||
private double m_count = -TestBench.kFilterStep;
|
||||
|
||||
@SuppressWarnings("ParameterName")
|
||||
public abstract double getData(double t);
|
||||
private DoubleFunction<Double> m_func;
|
||||
|
||||
public DataWrapper(DoubleFunction<Double> func) {
|
||||
m_func = func;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPIDSourceType(PIDSourceType pidSource) {
|
||||
@@ -144,7 +149,7 @@ public abstract class FilterOutputFixture<T extends PIDSource> implements ITestF
|
||||
@Override
|
||||
public double pidGet() {
|
||||
m_count += TestBench.kFilterStep;
|
||||
return getData(m_count);
|
||||
return m_func.apply(m_count);
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
|
||||
@@ -387,8 +387,9 @@ public final class TestBench {
|
||||
public FilterOutputFixture<LinearDigitalFilter> getSinglePoleIIROutputFixture() {
|
||||
return new FilterOutputFixture<LinearDigitalFilter>(kSinglePoleIIRExpectedOutput) {
|
||||
@Override
|
||||
protected LinearDigitalFilter giveFilter(PIDSource source) {
|
||||
return LinearDigitalFilter.singlePoleIIR(source,
|
||||
protected LinearDigitalFilter giveFilter() {
|
||||
DataWrapper data = new DataWrapper(getData);
|
||||
return LinearDigitalFilter.singlePoleIIR(data,
|
||||
kSinglePoleIIRTimeConstant,
|
||||
kFilterStep);
|
||||
}
|
||||
@@ -403,8 +404,9 @@ public final class TestBench {
|
||||
public FilterOutputFixture<LinearDigitalFilter> getHighPassOutputFixture() {
|
||||
return new FilterOutputFixture<LinearDigitalFilter>(kHighPassExpectedOutput) {
|
||||
@Override
|
||||
protected LinearDigitalFilter giveFilter(PIDSource source) {
|
||||
return LinearDigitalFilter.highPass(source, kHighPassTimeConstant,
|
||||
protected LinearDigitalFilter giveFilter() {
|
||||
DataWrapper data = new DataWrapper(getData);
|
||||
return LinearDigitalFilter.highPass(data, kHighPassTimeConstant,
|
||||
kFilterStep);
|
||||
}
|
||||
};
|
||||
@@ -419,8 +421,25 @@ public final class TestBench {
|
||||
public FilterOutputFixture<LinearDigitalFilter> getMovAvgOutputFixture() {
|
||||
return new FilterOutputFixture<LinearDigitalFilter>(kMovAvgExpectedOutput) {
|
||||
@Override
|
||||
protected LinearDigitalFilter giveFilter(PIDSource source) {
|
||||
return LinearDigitalFilter.movingAverage(source, kMovAvgTaps);
|
||||
protected LinearDigitalFilter giveFilter() {
|
||||
DataWrapper data = new DataWrapper(getData);
|
||||
return LinearDigitalFilter.movingAverage(data, kMovAvgTaps);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new set of objects representing a moving average filter with a repeatable data
|
||||
* source using a linear digital filter.
|
||||
*
|
||||
* @return a moving average filter with a repeatable data source
|
||||
*/
|
||||
public FilterOutputFixture<LinearDigitalFilter> getPulseFixture() {
|
||||
return new FilterOutputFixture<LinearDigitalFilter>(0.0) {
|
||||
@Override
|
||||
protected LinearDigitalFilter giveFilter() {
|
||||
DataWrapper data = new DataWrapper(getPulseData);
|
||||
return LinearDigitalFilter.movingAverage(data, kMovAvgTaps);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user