package org.signalml.domain.signal.filter.iir; import org.apache.log4j.Logger; import org.signalml.domain.montage.filter.TimeDomainSampleFilter; import org.signalml.domain.signal.samplesource.RoundBufferSampleSource; import org.signalml.domain.signal.samplesource.SampleSource; import org.signalml.math.iirdesigner.FilterCoefficients; /** * This class represents a Time Domain (IIR or FIR) engine for filtering the samples. * Use this to filter online signals. * * @author Piotr Szachewicz */ public class OnlineIIRSinglechannelSampleFilter extends AbstractIIRSinglechannelSampleFilter { protected static final Logger logger = Logger.getLogger(OnlineIIRSinglechannelSampleFilter.class); public OnlineIIRSinglechannelSampleFilter(SampleSource source, TimeDomainSampleFilter definition) { super(source, definition); } public OnlineIIRSinglechannelSampleFilter(SampleSource source, TimeDomainSampleFilter definition, FilterCoefficients coefficients) { super(source, definition, coefficients); } public OnlineIIRSinglechannelSampleFilter(SampleSource source, FilterCoefficients coefficients) { super(source, coefficients); } /** * Filters the given data using the specified digital filter. * This method uses previously filtered samples cache. * @param bCoefficients feedforward coefficients of the filter * @param aCoefficients feedback coeffcients of the filter * @param input the input signal to be filtered * is assumed. * @return the input signal after filtering */ public static double[] filterUsingCache(double[] bCoefficients, double[] aCoefficients, double[] input) { int filterOrder = Math.max(aCoefficients.length - 1, bCoefficients.length - 1); int cacheSize = filterOrder + input.length; double[] unfilteredSamplesCache = new double[cacheSize]; double[] filteredSamplesCache = new double[cacheSize]; for (int i = 0; i < filterOrder; i++) { unfilteredSamplesCache[i] = 0.0; } for (int i = filterOrder; i < unfilteredSamplesCache.length; i++) { unfilteredSamplesCache[i] = input[i - filterOrder]; } double[] filteredSamples = calculateNewFilteredSamples(bCoefficients, aCoefficients, filterOrder, unfilteredSamplesCache, filteredSamplesCache, input.length); return filteredSamples; } /** * Returns an array containing newly added unfiltered samples which can be used to * calculate a specified number of new filtered samples using the filters definition. * @param newSamples number of samples which were added to the signal since * the last call of this method * @return an array containing enough unfiltered samples to filter newSamples of new samples * (size of the array = newSamples+ order of the filter). */ protected double[] getUnfilteredSamplesCache(int newSamples) { int unfilteredSamplesNeeded = newSamples + filterOrder; int zeroPaddingSize = 0; double[] unfilteredSamplesCache = new double[unfilteredSamplesNeeded]; if (unfilteredSamplesNeeded > source.getSampleCount()) { zeroPaddingSize = unfilteredSamplesNeeded - source.getSampleCount(); } for (int i = 0; i < zeroPaddingSize; i++) { unfilteredSamplesCache[i] = 0.0; } source.getSamples(unfilteredSamplesCache, source.getSampleCount() - unfilteredSamplesNeeded + zeroPaddingSize, unfilteredSamplesNeeded - zeroPaddingSize, zeroPaddingSize); return unfilteredSamplesCache; } /** * Returns an array with filtered samples from the filter engine's * cache which can be used to calculate a specified number of new filtered * samples using the filters definition. * * @param newSamples the number of samples which were added to the signal * since the last call of this method. * @return an array containing enough filtered samples to filter newSamples of new samples * (size of the array = newSamples+ order of the filter. Last newSamples cells in * the array is filled with zeros). */ protected double[] getFilteredSamplesCache(int newSamples) { int filteredCacheSize = newSamples + filterOrder; int zeroPaddingSize = 0; double[] filteredSamplesCache = new double[filteredCacheSize]; if (filtered == null) { filtered = new RoundBufferSampleSource(source.getSampleCount()); for (int i = 0; i < source.getSampleCount(); i++) { filtered.addSamples(new double[] {0.0}); } } if (filteredCacheSize > filtered.getSampleCount()) { zeroPaddingSize = filteredCacheSize - filtered.getSampleCount(); } for (int i = 0; i < zeroPaddingSize; i++) { filteredSamplesCache[i] = 0.0; } filtered.getSamples(filteredSamplesCache, filtered.getSampleCount() - filterOrder, filterOrder, zeroPaddingSize); return filteredSamplesCache; } /** * Calculates newSamples of new filtered samples using a part of the unfiltered signal and * part of the filtered signal stored in the cache. * @param unfilteredSamplesCache an array containing unfiltered samples needed to calculate * newSamples of new filtered samples. * @param filteredSamplesCache an array containg filtered samples needed to calculate * newSamples of new filtered samples. * @param newSamples number of samples which were added to the signal since the last call * of this method * @return an array containing new filtered samples of the signal (size of the array = newSamples) */ protected double[] calculateNewFilteredSamples(double[] unfilteredSamplesCache, double[] filteredSamplesCache, int newSamples) { return calculateNewFilteredSamples(bCoefficients, aCoefficients, filterOrder, unfilteredSamplesCache, filteredSamplesCache, newSamples); } /** * Calculates newSamples of new filtered samples using a part of the unfiltered signal and * part of the filtered signal stored in the cache. * @param bCoefficients b Coefficients of the filter * @param aCoefficients a Coefficients of the filter * @param filterOrder the order of the filter * @param unfilteredSamplesCache an array containing unfiltered samples needed to calculate * newSamples of new filtered samples. * @param filteredSamplesCache an array containg filtered samples needed to calculate * newSamples of new filtered samples. * @param newSamples number of samples which were added to the signal since the last call * of this method * @return an array containing new filtered samples of the signal (size of the array = newSamples) */ protected static double[] calculateNewFilteredSamples(double[] bCoefficients, double[] aCoefficients, int filterOrder, double[] unfilteredSamplesCache, double[] filteredSamplesCache, int newSamples) { for (int i = filterOrder; i < filteredSamplesCache.length; i++) { for (int j = i - filterOrder; j <= i; j++) { if (i - j < bCoefficients.length) { filteredSamplesCache[i] += unfilteredSamplesCache[j] * bCoefficients[i - j]; } if (j < i && i - j < aCoefficients.length) { filteredSamplesCache[i] -= filteredSamplesCache[j] * aCoefficients[i - j]; } } filteredSamplesCache[i] /= aCoefficients[0]; } double[] newFilteredSamples = new double[newSamples]; for (int i = 0; i < newSamples; i++) { newFilteredSamples[i] = filteredSamplesCache[filterOrder + i]; } return newFilteredSamples; } /** * Updates the cache used to store filtered samples of the signal. * @param newSamples number of samples which were added to the signal since the last call * of this method */ public synchronized void updateCache(int newSamples) { double[] unfilteredSamplesCache = getUnfilteredSamplesCache(newSamples); double[] filteredSamplesCache = getFilteredSamplesCache(newSamples); double[] newFilteredSamples = calculateNewFilteredSamples(bCoefficients, aCoefficients, filterOrder, unfilteredSamplesCache, filteredSamplesCache, newSamples); filtered.addSamples(newFilteredSamples); } /** * Returns the given number of the filtered samples starting from * the given position in time. {@link OnlineIIRSinglechannelSampleFilter#updateCache(int)} * must be run before running this method if new samples were added or * cache was never updated before. * * @param target the array to which results will be written starting * from position <code>arrayOffset</code> * @param signalOffset the position (in time) in the signal starting * from which samples will be returned * @param count the number of samples to be returned * @param arrayOffset the offset in <code>target</code> array starting * from which samples will be written */ @Override public synchronized void getSamples(double[] target, int signalOffset, int count, int arrayOffset) { filtered.getSamples(target, signalOffset, count, arrayOffset); } }