/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package dsp.filter;
import java.util.ArrayList;
import org.apache.commons.lang3.Validate;
import sample.real.RealSampleListener;
public class FloatFIRFilter implements RealSampleListener
{
private RealSampleListener mListener;
private ArrayList<Float> mBuffer;
private int mBufferSize = 1; //Temporary initial value
private int mBufferPointer = 0;
private float[] mCoefficients;
private int[][] mIndexMap;
private int mCenterCoefficient;
private int mCenterCoefficientMapIndex;
private float mGain;
public FloatFIRFilter( float[] coefficients, float gain )
{
mCoefficients = coefficients;
mBuffer = new ArrayList<Float>();
mBufferSize = mCoefficients.length;
mGain = gain;
//Fill the buffer with zero valued samples, so we don't have to check for null
for( int x = 0; x < mCoefficients.length; x++ )
{
mBuffer.add( 0.0f );
}
generateIndexMap( mCoefficients.length );
}
public void dispose()
{
mListener = null;
mBuffer.clear();
}
public int getTapCount()
{
return mCoefficients.length;
}
public void setListener( RealSampleListener listener )
{
mListener = listener;
}
public void receive( float newSample )
{
send( get( newSample ) );
}
public float get( float newSample )
{
//Add the new sample to the buffer
mBuffer.set( mBufferPointer, newSample );
//Increment & Adjust the buffer pointer for circular wrap around
mBufferPointer++;
if( mBufferPointer >= mBufferSize )
{
mBufferPointer = 0;
}
//Convolution - multiply filter coefficients by the circular buffer
//samples to calculate a new filtered value
float accumulator = 0.0f;
//Start with the center tap value
accumulator += mCoefficients[ mCenterCoefficient ] *
mBuffer.get( mIndexMap[ mBufferPointer ][ mCenterCoefficientMapIndex ] );
//For the remaining coefficients, add the symmetric samples, oldest and newest
//first, then multiply by the single coefficient
for( int x = 0; x < mCenterCoefficient; x++ )
{
accumulator += mCoefficients[ x ] *
( mBuffer.get( mIndexMap[ mBufferPointer ][ x ] ) +
mBuffer.get( mIndexMap[ mBufferPointer ][ x + mCenterCoefficient ] ) );
}
//We're almost finished ... apply gain, cast the doubles to shorts and
//return the value
return accumulator * mGain;
}
/**
* Dispatch the filtered sample to all registered listeners
*/
private void send( float sample )
{
if( mListener != null )
{
mListener.receive( sample );
}
}
public String printIndexMap()
{
StringBuilder sb = new StringBuilder();
for( int x = 0; x < mCoefficients.length; x++ )
{
for( int y = 0; y < mCoefficients.length; y++ )
{
sb.append( mIndexMap[ x ][ y ] + " " );
}
sb.append( "\n" );
}
return sb.toString();
}
/**
* @param odd-sized number of filter taps (ie coefficients) and buffer
*/
private void generateIndexMap( int size )
{
//Ensure we have an odd size
Validate.isTrue(size % 2 == 1);
mIndexMap = new int[ size ][ size ];
//Last column will be the center coefficient index value for each row
mCenterCoefficientMapIndex = size - 1;
mCenterCoefficient = (int)( size / 2 );
//Setup the first row. Offset is the first row's center coefficient value
//and will become the index value we place in the last column
mIndexMap[ 0 ][ mCenterCoefficient ] = mCenterCoefficient;
//Indexes 0 to 1/2 are their index value, and indexes 1/2 to end are
//an offset of the first half of the values
for( int x = 0; x < mCenterCoefficient; x++ )
{
mIndexMap[ 0 ][ x ] = x;
mIndexMap[ 0 ][ x + mCenterCoefficient ] = size - 1 - x;
}
//For each subsequent map row, increment the value of the preceding row
//same column by 1, wrapping to zero when we exceed the size value
for( int x = 1; x < size; x++ )
{
for( int y = 0; y < size; y++ )
{
mIndexMap[ x ][ y ] = mIndexMap[ x - 1 ][ y ] + 1;
if( mIndexMap[ x ][ y ] >= size )
{
mIndexMap[ x ][ y ] = 0;
}
}
}
}
}