mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-21 01:01:43 +00:00
Added linear digital filters
Linear digital filter class based on code from FRC team 341 Change-Id: I4c5198e36a089e08a6d054bf1bf80392def27e23
This commit is contained in:
committed by
Peter Johnson
parent
6c89f34e44
commit
e15ca5a414
@@ -0,0 +1,101 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2014. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
package edu.wpi.first.wpilibj;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import edu.wpi.first.wpilibj.CircularBuffer;
|
||||
import edu.wpi.first.wpilibj.test.AbstractComsSetup;
|
||||
|
||||
public class CircularBufferTest extends AbstractComsSetup {
|
||||
private static final Logger logger = Logger.getLogger(CircularBufferTest.class.getName());
|
||||
private double[] values = {751.848, 766.366, 342.657, 234.252, 716.126,
|
||||
132.344, 445.697, 22.727, 421.125, 799.913};
|
||||
private double[] pushFrontOut = {799.913, 421.125, 22.727, 445.697, 132.344,
|
||||
716.126, 234.252, 342.657};
|
||||
private double[] pushBackOut = {342.657, 234.252, 716.126, 132.344, 445.697,
|
||||
22.727, 421.125, 799.913};
|
||||
|
||||
@Test
|
||||
public void pushFrontTest() {
|
||||
CircularBuffer queue = new CircularBuffer(8);
|
||||
|
||||
for (double value : values) {
|
||||
queue.pushFront(value);
|
||||
}
|
||||
|
||||
for (int i = 0; i < pushFrontOut.length; i++) {
|
||||
assertEquals(pushFrontOut[i], queue.get(i), 0.00005);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pushBackTest() {
|
||||
CircularBuffer queue = new CircularBuffer(8);
|
||||
|
||||
for (double value : values) {
|
||||
queue.pushBack(value);
|
||||
}
|
||||
|
||||
for (int i = 0; i < pushBackOut.length; i++) {
|
||||
assertEquals(pushBackOut[i], queue.get(i), 0.00005);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pushPopTest() {
|
||||
CircularBuffer queue = new CircularBuffer(3);
|
||||
|
||||
// Insert three elements into the buffer
|
||||
queue.pushBack(1.0);
|
||||
queue.pushBack(2.0);
|
||||
queue.pushBack(3.0);
|
||||
|
||||
assertEquals(1.0, queue.get(0), 0.00005);
|
||||
assertEquals(2.0, queue.get(1), 0.00005);
|
||||
assertEquals(3.0, queue.get(2), 0.00005);
|
||||
|
||||
/*
|
||||
* The buffer is full now, so pushing subsequent elements will overwrite the
|
||||
* front-most elements.
|
||||
*/
|
||||
|
||||
queue.pushBack(4.0); // Overwrite 1 with 4
|
||||
|
||||
// The buffer now contains 2, 3, and 4
|
||||
assertEquals(2.0, queue.get(0), 0.00005);
|
||||
assertEquals(3.0, queue.get(1), 0.00005);
|
||||
assertEquals(4.0, queue.get(2), 0.00005);
|
||||
|
||||
queue.pushBack(5.0); // Overwrite 2 with 5
|
||||
|
||||
// The buffer now contains 3, 4, and 5
|
||||
assertEquals(3.0, queue.get(0), 0.00005);
|
||||
assertEquals(4.0, queue.get(1), 0.00005);
|
||||
assertEquals(5.0, queue.get(2), 0.00005);
|
||||
|
||||
assertEquals(5.0, queue.popBack(), 0.00005); // 5 is removed
|
||||
|
||||
// The buffer now contains 3 and 4
|
||||
assertEquals(3.0, queue.get(0), 0.00005);
|
||||
assertEquals(4.0, queue.get(1), 0.00005);
|
||||
|
||||
assertEquals(3.0, queue.popFront(), 0.00005); // 3 is removed
|
||||
|
||||
// Leaving only one element with value == 4
|
||||
assertEquals(4.0, queue.get(0), 0.00005);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Logger getClassLogger() {
|
||||
return logger;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2015. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
package edu.wpi.first.wpilibj;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
|
||||
import edu.wpi.first.wpilibj.fixtures.FilterNoiseFixture;
|
||||
import edu.wpi.first.wpilibj.test.AbstractComsSetup;
|
||||
import edu.wpi.first.wpilibj.test.TestBench;
|
||||
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
public class FilterNoiseTest extends AbstractComsSetup {
|
||||
private static final Logger logger = Logger.getLogger(FilterNoiseTest.class.getName());
|
||||
|
||||
private static FilterNoiseFixture<?> me = null;
|
||||
|
||||
@Override
|
||||
protected Logger getClassLogger() {
|
||||
return logger;
|
||||
}
|
||||
|
||||
public FilterNoiseTest(FilterNoiseFixture<?> mef) {
|
||||
logger.fine("Constructor with: " + mef.getType());
|
||||
if (me != null && !me.equals(mef))
|
||||
me.teardown();
|
||||
me = mef;
|
||||
}
|
||||
|
||||
@Parameters(name = "{index}: {0}")
|
||||
public static Collection<FilterNoiseFixture<?>[]> generateData() {
|
||||
return Arrays.asList(new FilterNoiseFixture<?>[][] {
|
||||
{TestBench.getInstance().getSinglePoleIIRNoiseFixture()},
|
||||
{TestBench.getInstance().getMovAvgNoiseFixture()}});
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
me.setup();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
me.reset();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownAfterClass() {
|
||||
// Clean up the fixture after the test
|
||||
me.teardown();
|
||||
me = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the filter reduces the noise produced by a signal generator
|
||||
*/
|
||||
@Test
|
||||
public void testNoiseReduce() {
|
||||
double theoryData = 0.0;
|
||||
double noiseGenError = 0.0;
|
||||
double filterError = 0.0;
|
||||
|
||||
FilterNoiseFixture.NoiseGenerator noise = me.getNoiseGenerator();
|
||||
|
||||
noise.reset();
|
||||
for (double t = 0; t < TestBench.kFilterTime; t += TestBench.kFilterStep) {
|
||||
theoryData = noise.getData(t);
|
||||
filterError += Math.abs(me.getFilter().pidGet() - theoryData);
|
||||
noiseGenError += Math.abs(noise.get() - theoryData);
|
||||
}
|
||||
|
||||
assertTrue(me.getType() + " should have reduced noise accumulation from " + noiseGenError + " but failed. The filter error was " + filterError, noiseGenError > filterError);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2015. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
package edu.wpi.first.wpilibj;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
|
||||
import edu.wpi.first.wpilibj.fixtures.FilterOutputFixture;
|
||||
import edu.wpi.first.wpilibj.test.AbstractComsSetup;
|
||||
import edu.wpi.first.wpilibj.test.TestBench;
|
||||
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
public class FilterOutputTest extends AbstractComsSetup {
|
||||
private static final Logger logger = Logger.getLogger(FilterOutputTest.class.getName());
|
||||
|
||||
private double expectedOutput;
|
||||
|
||||
private static FilterOutputFixture<?> me = null;
|
||||
|
||||
@Override
|
||||
protected Logger getClassLogger() {
|
||||
return logger;
|
||||
}
|
||||
|
||||
public FilterOutputTest(FilterOutputFixture<?> mef) {
|
||||
logger.fine("Constructor with: " + mef.getType());
|
||||
if (me != null && !me.equals(mef))
|
||||
me.teardown();
|
||||
me = mef;
|
||||
expectedOutput = me.getExpectedOutput();
|
||||
}
|
||||
|
||||
@Parameters(name = "{index}: {0}")
|
||||
public static Collection<FilterOutputFixture<?>[]> generateData() {
|
||||
return Arrays.asList(new FilterOutputFixture<?>[][] {
|
||||
{TestBench.getInstance().getSinglePoleIIROutputFixture()},
|
||||
{TestBench.getInstance().getHighPassOutputFixture()},
|
||||
{TestBench.getInstance().getMovAvgOutputFixture()}});
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
me.setup();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
me.reset();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownAfterClass() {
|
||||
// Clean up the fixture after the test
|
||||
me.teardown();
|
||||
me = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the filter produces consistent output for a given data set
|
||||
*/
|
||||
@Test
|
||||
public void testOutput() {
|
||||
me.reset();
|
||||
|
||||
double filterOutput = 0.0;
|
||||
for (double t = 0.0; t < TestBench.kFilterTime; t += TestBench.kFilterStep) {
|
||||
filterOutput = me.getFilter().pidGet();
|
||||
}
|
||||
|
||||
assertEquals(me.getType() + " output was incorrect.", expectedOutput, filterOutput, 0.00005);
|
||||
}
|
||||
}
|
||||
@@ -19,10 +19,12 @@ import edu.wpi.first.wpilibj.test.AbstractTestSuite;
|
||||
*/
|
||||
@RunWith(Suite.class)
|
||||
@SuiteClasses({AnalogCrossConnectTest.class, AnalogPotentiometerTest.class,
|
||||
BuiltInAccelerometerTest.class, CANTalonTest.class, CounterTest.class,
|
||||
DigitalGlitchFilterTest.class, DIOCrossConnectTest.class, EncoderTest.class,
|
||||
GyroTest.class, MotorEncoderTest.class, MotorInvertingTest.class,
|
||||
PCMTest.class, PDPTest.class, PIDTest.class, PreferencesTest.class,
|
||||
RelayCrossConnectTest.class, SampleTest.class, TimerTest.class})
|
||||
BuiltInAccelerometerTest.class, CANTalonTest.class,
|
||||
CircularBufferTest.class, CounterTest.class, DigitalGlitchFilterTest.class,
|
||||
DIOCrossConnectTest.class, EncoderTest.class, FilterNoiseTest.class,
|
||||
FilterOutputTest.class, GyroTest.class, MotorEncoderTest.class,
|
||||
MotorInvertingTest.class, PCMTest.class, PDPTest.class, PIDTest.class,
|
||||
PreferencesTest.class, RelayCrossConnectTest.class, SampleTest.class,
|
||||
TimerTest.class})
|
||||
public class WpiLibJTestSuite extends AbstractTestSuite {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2014. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
package edu.wpi.first.wpilibj.fixtures;
|
||||
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.Random;
|
||||
|
||||
import edu.wpi.first.wpilibj.PIDSource;
|
||||
import edu.wpi.first.wpilibj.PIDSourceType;
|
||||
import edu.wpi.first.wpilibj.test.TestBench;
|
||||
|
||||
/**
|
||||
* Represents a physically 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.
|
||||
*/
|
||||
public abstract class FilterNoiseFixture<T extends PIDSource> implements ITestFixture {
|
||||
private static final Logger logger = Logger.getLogger(FilterNoiseFixture.class.getName());
|
||||
private boolean initialized = false;
|
||||
private boolean tornDown = false;
|
||||
protected T filter;
|
||||
private NoiseGenerator data;
|
||||
|
||||
/**
|
||||
* Where the implementer of this class should pass the filter constructor
|
||||
*$
|
||||
* @return
|
||||
*/
|
||||
abstract protected T giveFilter(PIDSource source);
|
||||
|
||||
final private void initialize() {
|
||||
synchronized (this) {
|
||||
if (!initialized) {
|
||||
initialized = true; // This ensures it is only initialized once
|
||||
|
||||
data = new NoiseGenerator(TestBench.kStdDev) {
|
||||
@Override
|
||||
public double getData(double t) {
|
||||
return 100.0 * Math.sin(2.0 * Math.PI * t);
|
||||
}
|
||||
};
|
||||
filter = giveFilter(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setup() {
|
||||
initialize();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the filter for this Object
|
||||
*$
|
||||
* @return the filter this object refers too
|
||||
*/
|
||||
public T getFilter() {
|
||||
initialize();
|
||||
return filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the noise generator for this object
|
||||
*$
|
||||
* @return the noise generator that this object refers too
|
||||
*/
|
||||
public NoiseGenerator getNoiseGenerator() {
|
||||
initialize();
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the name of the filter that this object refers to
|
||||
*$
|
||||
* @return The simple name of the filter {@link Class#getSimpleName()}
|
||||
*/
|
||||
public String getType() {
|
||||
initialize();
|
||||
return filter.getClass().getSimpleName();
|
||||
}
|
||||
|
||||
// test here?
|
||||
|
||||
@Override
|
||||
public boolean reset() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean teardown() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder string = new StringBuilder("FilterNoiseFixture<");
|
||||
// Get the generic type as a class
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<T> class1 =
|
||||
(Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
|
||||
string.append(class1.getSimpleName());
|
||||
string.append(">");
|
||||
return string.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds Gaussian white noise to a function returning data. The noise will have
|
||||
* the standard deviation provided in the constructor.
|
||||
*/
|
||||
public abstract class NoiseGenerator implements PIDSource {
|
||||
private double noise = 0.0;
|
||||
|
||||
// Make sure first call to pidGet() uses count == 0
|
||||
private double count = -TestBench.kFilterStep;
|
||||
|
||||
private double stdDev;
|
||||
private Random gen = new Random();
|
||||
|
||||
NoiseGenerator(double stdDev) {
|
||||
this.stdDev = stdDev;
|
||||
}
|
||||
|
||||
abstract public double getData(double t);
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void setPIDSourceType(PIDSourceType pidSource) {}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public PIDSourceType getPIDSourceType() {
|
||||
return PIDSourceType.kDisplacement;
|
||||
}
|
||||
|
||||
public double get() {
|
||||
return getData(count) + noise;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public double pidGet() {
|
||||
noise = gen.nextGaussian() * stdDev;
|
||||
count += TestBench.kFilterStep;
|
||||
return getData(count) + noise;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
count = -TestBench.kFilterStep;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2014. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
package edu.wpi.first.wpilibj.fixtures;
|
||||
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import edu.wpi.first.wpilibj.PIDSource;
|
||||
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.
|
||||
*/
|
||||
public abstract class FilterOutputFixture<T extends PIDSource> implements ITestFixture {
|
||||
private static final Logger logger = Logger.getLogger(FilterOutputFixture.class.getName());
|
||||
private boolean initialized = false;
|
||||
private boolean tornDown = false;
|
||||
protected T filter;
|
||||
private DataWrapper data;
|
||||
private double expectedOutput;
|
||||
|
||||
public FilterOutputFixture(double expectedOutput) {
|
||||
this.expectedOutput = expectedOutput;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get expected output of fixture
|
||||
*/
|
||||
public double getExpectedOutput() {
|
||||
return expectedOutput;
|
||||
}
|
||||
|
||||
/**
|
||||
* Where the implementer of this class should pass the filter constructor
|
||||
*$
|
||||
* @return
|
||||
*/
|
||||
abstract protected T giveFilter(PIDSource source);
|
||||
|
||||
final private void initialize() {
|
||||
synchronized (this) {
|
||||
if (!initialized) {
|
||||
initialized = true; // This ensures it is only initialized once
|
||||
|
||||
data = new DataWrapper() {
|
||||
@Override
|
||||
public double getData(double t) {
|
||||
return 100.0 * Math.sin(2.0 * Math.PI * t) + 20.0 * Math.cos(50.0 * Math.PI * t);
|
||||
}
|
||||
};
|
||||
filter = giveFilter(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setup() {
|
||||
initialize();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the filter for this Object
|
||||
*$
|
||||
* @return the filter this object refers too
|
||||
*/
|
||||
public T getFilter() {
|
||||
initialize();
|
||||
return filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the data wrapper for this object
|
||||
*$
|
||||
* @return the data wrapper that this object refers too
|
||||
*/
|
||||
public DataWrapper getDataWrapper() {
|
||||
initialize();
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the name of the filter that this object refers to
|
||||
*$
|
||||
* @return The simple name of the filter {@link Class#getSimpleName()}
|
||||
*/
|
||||
public String getType() {
|
||||
initialize();
|
||||
return filter.getClass().getSimpleName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean reset() {
|
||||
data.reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean teardown() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder string = new StringBuilder("FilterOutputFixture<");
|
||||
// Get the generic type as a class
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<T> class1 =
|
||||
(Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
|
||||
string.append(class1.getSimpleName());
|
||||
string.append(">");
|
||||
return string.toString();
|
||||
}
|
||||
|
||||
public abstract class DataWrapper implements PIDSource {
|
||||
// Make sure first call to pidGet() uses count == 0
|
||||
private double count = -TestBench.kFilterStep;
|
||||
|
||||
abstract public double getData(double t);
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void setPIDSourceType(PIDSourceType pidSource) {}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public PIDSourceType getPIDSourceType() {
|
||||
return PIDSourceType.kDisplacement;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public double pidGet() {
|
||||
count += TestBench.kFilterStep;
|
||||
return getData(count);
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
count = -TestBench.kFilterStep;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,13 +19,17 @@ import edu.wpi.first.wpilibj.CANJaguar;
|
||||
import edu.wpi.first.wpilibj.DigitalInput;
|
||||
import edu.wpi.first.wpilibj.DigitalOutput;
|
||||
import edu.wpi.first.wpilibj.Jaguar;
|
||||
import edu.wpi.first.wpilibj.PIDSource;
|
||||
import edu.wpi.first.wpilibj.Relay;
|
||||
import edu.wpi.first.wpilibj.Servo;
|
||||
import edu.wpi.first.wpilibj.Talon;
|
||||
import edu.wpi.first.wpilibj.Victor;
|
||||
import edu.wpi.first.wpilibj.filters.LinearDigitalFilter;
|
||||
import edu.wpi.first.wpilibj.fixtures.AnalogCrossConnectFixture;
|
||||
import edu.wpi.first.wpilibj.fixtures.CANMotorEncoderFixture;
|
||||
import edu.wpi.first.wpilibj.fixtures.DIOCrossConnectFixture;
|
||||
import edu.wpi.first.wpilibj.fixtures.FilterNoiseFixture;
|
||||
import edu.wpi.first.wpilibj.fixtures.FilterOutputFixture;
|
||||
import edu.wpi.first.wpilibj.fixtures.MotorEncoderFixture;
|
||||
import edu.wpi.first.wpilibj.fixtures.RelayCrossConnectFixture;
|
||||
import edu.wpi.first.wpilibj.fixtures.TiltPanCameraFixture;
|
||||
@@ -79,6 +83,17 @@ public final class TestBench {
|
||||
public static final int DIOCrossConnectA2 = 7;
|
||||
public static final int DIOCrossConnectA1 = 6;
|
||||
|
||||
// Filter constants
|
||||
public static final double kStdDev = 10.0;
|
||||
public static final double kFilterStep = 0.005;
|
||||
public static final double kFilterTime = 2.0;
|
||||
public static final double kSinglePoleIIRTimeConstant = 0.015915;
|
||||
public static final double kSinglePoleIIRExpectedOutput = -3.2172003;
|
||||
public static final double kHighPassTimeConstant = 0.006631;
|
||||
public static final double kHighPassExpectedOutput = 10.074717;
|
||||
public static final int kMovAvgTaps = 6;
|
||||
public static final double kMovAvgExpectedOutput = -10.191644;
|
||||
|
||||
/** The Singleton instance of the Test Bench */
|
||||
private static TestBench instance = null;
|
||||
|
||||
@@ -410,6 +425,86 @@ public final class TestBench {
|
||||
return encoderPortPairs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new set of objects representing a single-pole IIR filter with
|
||||
* a noisy data source
|
||||
*
|
||||
* @return a single-pole IIR filter with a noisy data source
|
||||
*/
|
||||
public FilterNoiseFixture<LinearDigitalFilter> getSinglePoleIIRNoiseFixture() {
|
||||
return new FilterNoiseFixture<LinearDigitalFilter>() {
|
||||
@Override
|
||||
protected LinearDigitalFilter giveFilter(PIDSource source) {
|
||||
return LinearDigitalFilter.singlePoleIIR(source,
|
||||
kSinglePoleIIRTimeConstant,
|
||||
kFilterStep);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new set of objects representing a moving average filter with a
|
||||
* noisy data source using a linear digital filter
|
||||
*
|
||||
* @return a moving average filter with a noisy data source
|
||||
*/
|
||||
public FilterNoiseFixture<LinearDigitalFilter> getMovAvgNoiseFixture() {
|
||||
return new FilterNoiseFixture<LinearDigitalFilter>() {
|
||||
@Override
|
||||
protected LinearDigitalFilter giveFilter(PIDSource source) {
|
||||
return LinearDigitalFilter.movingAverage(source, kMovAvgTaps);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new set of objects representing a single-pole IIR filter with
|
||||
* a repeatable data source
|
||||
*
|
||||
* @return a single-pole IIR filter with a repeatable data source
|
||||
*/
|
||||
public FilterOutputFixture<LinearDigitalFilter> getSinglePoleIIROutputFixture() {
|
||||
return new FilterOutputFixture<LinearDigitalFilter>(kSinglePoleIIRExpectedOutput) {
|
||||
@Override
|
||||
protected LinearDigitalFilter giveFilter(PIDSource source) {
|
||||
return LinearDigitalFilter.singlePoleIIR(source,
|
||||
kSinglePoleIIRTimeConstant,
|
||||
kFilterStep);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new set of objects representing a high-pass filter with a
|
||||
* repeatable data source
|
||||
*
|
||||
* @return a high-pass filter with a repeatable data source
|
||||
*/
|
||||
public FilterOutputFixture<LinearDigitalFilter> getHighPassOutputFixture() {
|
||||
return new FilterOutputFixture<LinearDigitalFilter>(kHighPassExpectedOutput) {
|
||||
@Override
|
||||
protected LinearDigitalFilter giveFilter(PIDSource source) {
|
||||
return LinearDigitalFilter.highPass(source, kHighPassTimeConstant,
|
||||
kFilterStep);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 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> getMovAvgOutputFixture() {
|
||||
return new FilterOutputFixture<LinearDigitalFilter>(kMovAvgExpectedOutput) {
|
||||
@Override
|
||||
protected LinearDigitalFilter giveFilter(PIDSource source) {
|
||||
return LinearDigitalFilter.movingAverage(source, kMovAvgTaps);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the singleton of the TestBench. If the TestBench is not already
|
||||
* allocated in constructs an new instance of it. Otherwise it returns the
|
||||
|
||||
Reference in New Issue
Block a user