[wpimath] Add LinearFilter::BackwardFiniteDifference() (#3528)

This is an alternative to #2344 that handles arbitrary order derivatives
of arbitrary precision. The downside is that since it's part of
LinearFilter, it can't utilize the units type system in the same way to
make Calculate()'s input type different from its output type.
This commit is contained in:
Tyler Veness
2021-08-28 20:50:18 -07:00
committed by GitHub
parent c8fc715fe2
commit 3b5d0d141a
4 changed files with 387 additions and 6 deletions

View File

@@ -108,4 +108,113 @@ class LinearFilterTest {
(DoubleFunction<Double>) LinearFilterTest::getPulseData,
0.0));
}
/** Test backward finite difference. */
@Test
void backwardFiniteDifferenceTest() {
double h = 0.005;
assertResults(
1,
2,
// f(x) = x²
(double x) -> x * x,
// df/dx = 2x
(double x) -> 2.0 * x,
h,
-20.0,
20.0);
assertResults(
1,
2,
// f(x) = sin(x)
(double x) -> Math.sin(x),
// df/dx = cos(x)
(double x) -> Math.cos(x),
h,
-20.0,
20.0);
assertResults(
1,
2,
// f(x) = ln(x)
(double x) -> Math.log(x),
// df/dx = 1 / x
(double x) -> 1.0 / x,
h,
1.0,
20.0);
assertResults(
2,
4,
// f(x) = x²
(double x) -> x * x,
// d²f/dx² = 2
(double x) -> 2.0,
h,
-20.0,
20.0);
assertResults(
2,
4,
// f(x) = sin(x)
(double x) -> Math.sin(x),
// d²f/dx² = -sin(x)
(double x) -> -Math.sin(x),
h,
-20.0,
20.0);
assertResults(
2,
4,
// f(x) = ln(x)
(double x) -> Math.log(x),
// d²f/dx² = -1 / x²
(double x) -> -1.0 / (x * x),
h,
1.0,
20.0);
}
/**
* Helper for checking results of backward finite difference.
*
* @param derivative The order of the derivative.
* @param samples The number of sample points.
* @param f Function of which to take derivative.
* @param dfdx Derivative of f.
* @param h Sample period in seconds.
* @param min Minimum of f's domain to test.
* @param max Maximum of f's domain to test.
*/
void assertResults(
int derivative,
int samples,
DoubleFunction<Double> f,
DoubleFunction<Double> dfdx,
double h,
double min,
double max) {
var filter = LinearFilter.backwardFiniteDifference(derivative, samples, h);
for (int i = (int) (min / h); i < (int) (max / h); ++i) {
// Let filter initialize
if (i < (int) (min / h) + samples) {
filter.calculate(f.apply(i * h));
continue;
}
// The order of accuracy is O(h^(N - d)) where N is number of stencil
// points and d is order of derivative
assertEquals(
dfdx.apply(i * h),
filter.calculate(f.apply(i * h)),
10.0 * Math.pow(h, samples - derivative));
}
}
}