Skip to content
This repository was archived by the owner on Jul 15, 2018. It is now read-only.

Commit 7d4b43b

Browse files
authored
Merge pull request #19 from relinc/simple-filter-fix
relinc/SurePulseDataProcessor #22 fixes filtering issues.
2 parents 16980a9 + f27cf5d commit 7d4b43b

3 files changed

Lines changed: 57 additions & 137 deletions

File tree

src/net/relinc/libraries/staticClasses/SPMath.java

Lines changed: 1 addition & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -19,70 +19,12 @@ public static Complex[] fft(double[] data){
1919
for(int i = 0; i < data.length; i++)
2020
padded[i] = data[i];
2121

22-
// for(int i = 0; i < padded.length; i++){
23-
// System.out.println(padded[i] + ",");
24-
// }
25-
2622
FastFourierTransformer transformer = new FastFourierTransformer(DftNormalization.STANDARD);
2723
return transformer.transform(padded, TransformType.FORWARD);
2824
}
2925

3026
public static double[] fourierLowPassFilter(double[] data, double lowPass, double frequency){
31-
//try the butterworth method
32-
//return butterworthFilter(data, frequency, 3, lowPass, 1.0);
33-
return customBuiltLowPassFilter(data, lowPass, frequency);
34-
}
35-
36-
public static double[] customBuiltLowPassFilter(double[] data, double lowPass, double frequency) {
37-
//this is the old lowpass filter. It didn't treat sparse datasets well.
38-
39-
// data: input data, must be spaced equally in time.
40-
// lowPass: The cutoff frequency at which
41-
// frequency: The frequency of the input data.
42-
// The apache Fft (Fast Fourier Transform) accepts arrays that are
43-
// powers of 2.
44-
45-
Complex[] fourierTransform = fft(data);
46-
47-
// for(int i = 0; i < fourierTransform.length; i++){
48-
// System.out.println(fourierTransform[i].getReal() + " + " +
49-
// fourierTransform[i].getImaginary() + "I" + ",");
50-
// }
51-
52-
// build the frequency domain array
53-
double[] frequencyDomain = new double[fourierTransform.length];
54-
for (int i = 0; i < frequencyDomain.length; i++)
55-
frequencyDomain[i] = frequency * i / (double) fourierTransform.length;
56-
57-
// build the classifier array, 2s are kept and 0s do not pass the filter
58-
double[] keepPoints = new double[frequencyDomain.length];
59-
keepPoints[0] = 1;
60-
for (int i = 1; i < frequencyDomain.length; i++) {
61-
if (frequencyDomain[i] < lowPass) {
62-
// System.out.println("Keeping: " + i);
63-
keepPoints[i] = 2;
64-
} else {
65-
// System.out.println("Not Keeping: " + i);
66-
keepPoints[i] = 0;
67-
}
68-
}
69-
70-
// filter the fft
71-
for (int i = 0; i < fourierTransform.length; i++)
72-
fourierTransform[i] = fourierTransform[i].multiply((double) keepPoints[i]);
73-
74-
// invert back to time domain
75-
FastFourierTransformer transformer = new FastFourierTransformer(DftNormalization.STANDARD);
76-
Complex[] reverseFourier = transformer.transform(fourierTransform, TransformType.INVERSE);
77-
78-
// get the real part of the reverse
79-
double[] result = new double[data.length];
80-
for (int i = 0; i < result.length; i++) {
81-
result[i] = reverseFourier[i].getReal();
82-
}
83-
// if it keeps all the data, then don't filter it because it gets
84-
// screwed.
85-
return keepPoints[keepPoints.length - 1] == 2 ? data : result;
27+
return TriangularLowPassFilter.triangularLowPass(data, lowPass/frequency);
8628
}
8729

8830
public static ArrayList<double[]> diluteData(double[] input, int keepPoints){
@@ -123,82 +65,6 @@ public static double[] getEngStrainFromLagrangianStrain(double[] langStrain) {
12365
return strain;
12466
}
12567

126-
/*
127-
* time smoothing constant for low-pass filter
128-
* 0 ≤ α ≤ 1 ; a smaller value basically means more smoothing
129-
* See: http://en.wikipedia.org/wiki/Low-pass_filter#Discrete-time_realization
130-
*/
131-
static final float ALPHA = 0.2f;
132-
133-
protected float[] accelVals;
134-
135-
public void onSensorChanged(float[] val) {
136-
accelVals = lowPass( val, accelVals );
137-
138-
// use smoothed accelVals here; see this link for a simple compass example:
139-
// http://www.codingforandroid.com/2011/01/using-orientation-sensors-simple.html
140-
}
141-
142-
/**
143-
* @see http://en.wikipedia.org/wiki/Low-pass_filter#Algorithmic_implementation
144-
* @see http://en.wikipedia.org/wiki/Low-pass_filter#Simple_infinite_impulse_response_filter
145-
*/
146-
protected float[] lowPass( float[] input, float[] output ) {
147-
if ( output == null ) return input;
148-
149-
for ( int i=0; i<input.length; i++ ) {
150-
output[i] = output[i] + ALPHA * (input[i] - output[i]);
151-
}
152-
return output;
153-
}
154-
155-
public static double[] butterworthFilter(double[] signal, double sampleFrequency, int order, double f0, double DCGain) {
156-
157-
int N = signal.length;
158-
159-
// Apply forward FFT
160-
Complex[] signalFFT = fft(signal);
161-
162-
if (f0 > 0) {
163-
164-
//int numBins = N / 2; // Half the length of the FFT by symmetry
165-
double binWidth = sampleFrequency / N; // Hz
166-
167-
// Filter
168-
// System.Threading.Tasks.Parallel.For( 1, N / 2, i =>
169-
// {
170-
// var binFreq = binWidth * i;
171-
// var gain = DCGain / ( Math.Sqrt( ( 1 +
172-
// Math.Pow( binFreq / f0, 2.0 * order ) ) ) );
173-
// signalFFT[i] *= gain;
174-
// signalFFT[N - i] *= gain;
175-
// } );
176-
177-
for (int i = 1; i <= N / 2; i++) {
178-
double binFreq = binWidth * i;
179-
double gain = DCGain / (Math.sqrt((1 + Math.pow(binFreq / f0, 2.0 * order))));
180-
signalFFT[i] = signalFFT[i].multiply(new Complex(gain));
181-
signalFFT[N - i] = signalFFT[N - i].multiply(new Complex(gain));
182-
}
183-
184-
}
185-
FastFourierTransformer transformer = new FastFourierTransformer(DftNormalization.STANDARD);
186-
Complex[] reverseFourier = transformer.transform(signalFFT, TransformType.INVERSE);
187-
188-
// get the real part of the reverse
189-
double[] result = new double[signal.length];
190-
for (int i = 0; i < result.length; i++) {
191-
result[i] = reverseFourier[i].getReal();
192-
}
193-
return result;
194-
// Reverse filtered signal
195-
// var ifft = new DoubleComplexBackward1DFFT( N );
196-
// ifft.SetScaleFactorByLength(); // Needed to get the correct amplitude
197-
// signal = ifft.FFT( signalFFT );
198-
//
199-
// return signal;
200-
}
201-
20268
public static double getPicoArrowIncrease(double currentVal, boolean up)
20369
{
20470
if(currentVal <= 0.0)
@@ -223,6 +89,4 @@ public static double getPicoArrowIncrease(double currentVal, boolean up)
22389
}
22490
return Math.pow(10, count);
22591
}
226-
227-
22892
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package net.relinc.libraries.staticClasses;
2+
3+
public final class TriangularLowPassFilter {
4+
5+
private static double findTriangularMovingAverage(double[] data, double f_t, int idx) {
6+
int range = (int) (.25 / f_t);
7+
double value = 0;
8+
for (int idxFilter = -range; idxFilter < range; idxFilter++) {
9+
if (idx + idxFilter <= 0) {
10+
value += data[0] * (range - Math.abs(idxFilter));
11+
} else if (idx + idxFilter >= data.length - 1) {
12+
value += data[data.length - 1] * (range - Math.abs(idxFilter));
13+
} else {
14+
value += data[idx + idxFilter] * (range - Math.abs(idxFilter));
15+
}
16+
}
17+
return value;
18+
}
19+
20+
public static double[] triangularLowPass(double[] data, double f_t) {
21+
22+
double[] filtered_data = new double[data.length];
23+
int range = (int) (.25 / f_t);
24+
25+
double total = 0;
26+
27+
for (int idxFilter = -range; idxFilter < range; idxFilter++) {
28+
total += range - Math.abs(idxFilter);
29+
}
30+
31+
for (int idx = 0; idx < data.length; idx++) {
32+
filtered_data[idx] = findTriangularMovingAverage(data, f_t, idx) / total;
33+
}
34+
35+
return filtered_data;
36+
}
37+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package net.relinc.libraries.unitTests;
2+
3+
import static org.junit.Assert.*;
4+
import org.junit.Test;
5+
6+
import net.relinc.libraries.staticClasses.TriangularLowPassFilter;
7+
8+
public class TriangularFilterTest extends BaseTest {
9+
@Test
10+
public void runFilter() {
11+
double[] testArray = new double[20];
12+
for (int idx = 0; idx < 20; idx++)
13+
testArray[idx] = Math.sin(idx / 4);
14+
double[] result = TriangularLowPassFilter.triangularLowPass(testArray, .2);
15+
assertTrue(Math.abs(result[0]) < .01);
16+
assertTrue(Math.abs(result[10] - .909) < .01);
17+
assertTrue(Math.abs(result[19] + .756) < .01);
18+
}
19+
}

0 commit comments

Comments
 (0)