package org.signalml.domain.signal.filter.iir; import static org.junit.Assert.assertEquals; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.signalml.domain.montage.filter.TimeDomainSampleFilter; import org.signalml.domain.signal.filter.iir.AbstractIIRSinglechannelSampleFilter; import org.signalml.domain.signal.filter.iir.OnlineIIRSinglechannelSampleFilter; import org.signalml.domain.signal.samplesource.MultichannelSampleSource; import org.signalml.domain.signal.samplesource.RoundBufferMultichannelSampleSource; import org.signalml.math.iirdesigner.FilterCoefficients; /** * This is an abstract class for testing subclasses of * {@link AbstractIIRSinglechannelSampleFilter}. * * @author Piotr Szachewicz */ public abstract class AbstractIIRSinglechannelSampleFilterTest { /** * the number of samples used for the tests */ public static final int TEST_SAMPLE_COUNT = 20; /** * the {@link FilterCoefficients coefficients} of the filter used for testing * {@link TimeDomainSampleFilter TimeDomainSampleFilter} class. */ protected FilterCoefficients coefficients; /** * the source of samples which will be filtered by the engine */ protected RoundBufferMultichannelSampleSource source; /** * the engine used to filter the samples taken from the source */ private AbstractIIRSinglechannelSampleFilter engine; /** * Sets everything up for the tests. */ @Before public void setUp() { source = new RoundBufferMultichannelSampleSource(1, TEST_SAMPLE_COUNT); } /** * Cleans up after the test. */ @After public void tearDown() { source = null; coefficients = null; } protected abstract AbstractIIRSinglechannelSampleFilter getEngine(MultichannelSampleSource source, FilterCoefficients coefficients); /** * Test method for {@link org.signalml.domain.signal.filter.iir.AbstractIIRSinglechannelSampleFilter#getSamples(double[], int, int, int)}. * Uses a filter that multiplies every sample by one and compares filtered * samples with unfiltered ones. */ @Test public void testGetSamplesAllPassFilter() { coefficients = new FilterCoefficients(new double[] {1.0, 0.0}, new double[] {1.0, 0.0}); engine = getEngine(source, coefficients); double[] target1 = new double[TEST_SAMPLE_COUNT]; double[] target2 = new double[TEST_SAMPLE_COUNT]; int i; for (i = 0; i < TEST_SAMPLE_COUNT; i++) source.addSamples(new float[] {(float) Math.random()}); source.getSamples(0, target1, 0, TEST_SAMPLE_COUNT, 0); updateCacheIfNecessary(); engine.getSamples(target2, 0, TEST_SAMPLE_COUNT, 0); for (i = 0; i < TEST_SAMPLE_COUNT; i++) assertEquals(target1[i], target2[i], 0.00001); } protected void updateCacheIfNecessary() { if (engine instanceof OnlineIIRSinglechannelSampleFilter) ((OnlineIIRSinglechannelSampleFilter)engine).updateCache(source.getSampleCount(0)); } /** * Test method for {@link org.signalml.domain.signal.filter.iir.AbstractIIRSinglechannelSampleFilter#getSamples(double[], int, int, int)}. * Uses a filter that applies gain to a signal. */ @Test public void testGetSamplesWithGain() { coefficients = new FilterCoefficients(new double[] {0.7}, new double[] {1.0}); engine = getEngine(source, coefficients); double[] target1 = new double[TEST_SAMPLE_COUNT]; double[] target2 = new double[TEST_SAMPLE_COUNT]; int i; for (i = 0; i < TEST_SAMPLE_COUNT; i++) source.addSamples(new float[] {(float) Math.random()}); source.getSamples(0, target1, 0, TEST_SAMPLE_COUNT, 0); updateCacheIfNecessary(); engine.getSamples(target2, 0, TEST_SAMPLE_COUNT, 0); for (i = 0; i < TEST_SAMPLE_COUNT; i++) assertEquals(0.7 * target1[i], target2[i], 0.00001); } /** * Test method for {@link org.signalml.domain.signal.filter.iir.AbstractIIRSinglechannelSampleFilter#getSamples(double[], int, int, int)}. * Uses high pass filter to filter out a constant from the signal and pass only high * frequency component. */ @Test public void testGetSamplesHighPassFilter() { /*this filter was generated in python and is a highpass filter: b,a=signal.iirdesign(wp=0.6,ws=0.2,gstop=30, gpass=3,ftype='butter')*/ coefficients = new FilterCoefficients(new double[] {1.0, 0.04940057, 0.33397875, 0.00449333}, new double[] {0.16001061, -0.48003182, 0.48003182, -0.16001061} ); engine = getEngine(source, coefficients); double[] target1 = new double[TEST_SAMPLE_COUNT]; double[] target2 = new double[TEST_SAMPLE_COUNT]; int i; /* generating a test signal: 1, 2, 1, 2, 1, 2, 1, 2, .... * which is a sum of two signals: a s1(t)=1.5 and an high frequency * component with an amplitude=0.5 and frequency=2*sampling frequency: * 1+0, 1+1, 1+0, 1+1, ... * * After high pass filtering the constant signal should be filtered out * and we should only have the high frequency component: * -0.5, 0.5, -0.5, 0.5, ... */ for (i = 0; i < TEST_SAMPLE_COUNT; i++) source.addSamples(new float[] {(float) (1.0 + (i % 2))}); source.getSamples(0, target1, 0, TEST_SAMPLE_COUNT, 0); updateCacheIfNecessary(); engine.getSamples(target2, 0, TEST_SAMPLE_COUNT, 0); // first few samples are distorted by the rectangular window function for (i = 60; i < TEST_SAMPLE_COUNT; i++) assertEquals(target1[i] - 1.5, target2[i], 0.00001); } /** * Test method for {@link org.signalml.domain.signal.filter.iir.AbstractIIRSinglechannelSampleFilter#getSamples(double[], int, int, int)}. * Uses low pass filter to filter out a high frequency component from the * signal and pass only constant component. */ @Test public void testGetSamplesLowPassFilter() { /*this filter was generated in python and is a lowpass filter: b,a=signal.iirdesign(wp=0.2,ws=0.6,gstop=30, gpass=3,ftype='butter')*/ coefficients = new FilterCoefficients(new double[] {1.0, -1.39105118, 0.85664657, -0.18260807}, new double[] {0.03537341, 0.10612024, 0.10612024, 0.03537341} ); engine = getEngine(source, coefficients); double[] target2 = new double[TEST_SAMPLE_COUNT]; int i; /* generating a test signal: 1, 2, 1, 2, 1, 2, 1, 2, .... * which is a sum of two signals: a s1(t)=1.5 and an high frequency * component with an amplitude=0.5 and frequency=2*sampling frequency: * 1+0, 1+1, 1+0, 1+1, ... * * After low pass filtering the high frequency component should be filtered out * and we should only have the constant component: * 1.5,1.5,1.5,1.5,... */ for (i = 0; i < TEST_SAMPLE_COUNT; i++) source.addSamples(new float[] {(float) (1.0 + (i % 2))}); updateCacheIfNecessary(); engine.getSamples(target2, 0, TEST_SAMPLE_COUNT, 0); // first few samples are distorted by the rectangular window function for (i = 60; i < TEST_SAMPLE_COUNT; i++) assertEquals(1.5, target2[i], 0.0001); } }