Merges #34 Major redesign of Tuner, Channel and Alias management.

Changes to model, view and controller components and updates all gui
editors.  Removes the configuration panel and tree and replaces with 3
new panels.  Updates playlist.xml to a new playlist_v2.xml format.
This commit is contained in:
denny
2016-04-01 08:23:49 -04:00
450 changed files with 19560 additions and 22805 deletions

View File

@@ -1,342 +0,0 @@
/*******************************************************************************
* 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 sample.Listener;
import sample.Provider;
import sample.complex.Complex;
import dsp.filter.Window.WindowType;
/**
* Cascaded Integrator Comb (CIC) filter - anti-aliasing decimating filter for
* reducing a high rate complex sample stream to a lower rate by an integral
* decimation (ie reduction) value.
*
* Note: comb delay is fixed at 1 for this filter.
*/
public class ComplexCICDecimate extends ComplexFilter
implements Provider<Complex>
{
private Integrator[] mIntegrator;
private Comb[] mComb;
private Decimator mDecimator;
private Output mOutput;
/**
* Constructs the complex sample CIC filter
*
* @param decimation - decimation factor
*
* @param stageCount - number of integrator/comb sections to use. The
* higher the number of stages, the sharper dropoff of the filter response
*
* @param outputSampleRate - output sample rate is used to calculate the
* coefficients for the final cleanup filter to adjust for frequency
* response droop induced by this CIC filter
*/
public ComplexCICDecimate( int decimation,
int stageCount,
int outputSampleRate )
{
/* Construct integrator(s) and comb(s) */
mIntegrator = new Integrator[ stageCount ];
mComb = new Comb[ stageCount ];
for( int x = 0; x < stageCount; x++ )
{
mIntegrator[ x ] = new Integrator();
mComb[ x ] = new Comb();
}
/* Connect each stage */
int y = 1;
while( y < stageCount )
{
mIntegrator[ y - 1 ].setListener( mIntegrator[ y ] );
mComb[ y - 1 ].setListener( mComb[ y ] );
y++;
}
/* Connect decimator to integrator output and comb input */
mDecimator = new Decimator( decimation );
mIntegrator[ stageCount - 1 ].setListener( mDecimator );
mDecimator.setListener( mComb[ 0 ] );
/* Connect output adapter to the comb output */
mOutput = new Output( outputSampleRate, stageCount, decimation );
mComb[ stageCount - 1 ].setListener( mOutput );
}
/**
* Sets a new decimation rate for this filter. This method can be invoked
* after filter construction to change the decimation rate dynamically.
*/
public void setDecimationRate( int rate )
{
mDecimator.setDecimationRate( rate );
mOutput.setDecimationRate( rate );
}
/**
* Input method for this filter to receive complex samples.
*/
@Override
public void receive( Complex sample )
{
mIntegrator[ 0 ].receive( (double)sample.left(), (double)sample.right() );
}
/**
* Sets the listener to receive the output of this CIC filter
*/
@Override
public void setListener( Listener<Complex> listener )
{
mOutput.setListener( listener );
}
/**
* Removes the listener from receiving the output of this CIC filter
*/
@Override
public void removeListener( Listener<Complex> listener )
{
mOutput.removeListener( listener );
}
@Override
public Listener<Complex> getListener()
{
return mOutput.getListener();
}
/**
* CIC Comb filter section
*/
public class Comb implements InternalListener
{
private double mPreviousLeft;
private double mPreviousRight;
private InternalListener mCombListener;
/**
* Performs comb filtering on the received values, subtracting the
* previous received samples from these samples.
*/
@Override
public void receive( double left, double right )
{
mCombListener.receive( ( left - mPreviousLeft ),
( right - mPreviousRight ) );
mPreviousLeft = left;
mPreviousRight = right;
}
/**
* Sets the comb output listener.
*/
public void setListener( InternalListener listener )
{
mCombListener = listener;
}
}
/**
* CIC Integrator section
*/
public class Integrator implements InternalListener
{
private double mRunningSumLeft = 0.0d;
private double mRunningSumRight = 0.0d;
private InternalListener mIntegratorListener;
/**
* Integrates the received values with the previous running sum and
* sends the new running sum to the registered listener
*/
@Override
public void receive( double left, double right )
{
mRunningSumLeft += left;
mRunningSumRight += right;
mIntegratorListener.receive( mRunningSumLeft, mRunningSumRight );
}
/**
* Sets the integrator output listener
*/
public void setListener( InternalListener listener )
{
mIntegratorListener = listener;
}
}
/**
* Decimator - passes only 1 of xx samples to a registered listener and
* discards all other samples.
*/
public class Decimator implements InternalListener
{
private InternalListener mDecimatorListener;
private int mPointer = 0;
private int mDecimationRate;
/**
* Constructs a new Decimator object with the specified decimation rate.
*/
public Decimator( int rate )
{
mDecimationRate = rate;
}
/**
* Sets a new decimation rate. Rate can be changed after this object
* is constructed.
*/
public synchronized void setDecimationRate( int rate )
{
mDecimationRate = rate;
}
/**
* Receives samples allowing only 1 of every (rate) sample to go on
* to the registered listener
*/
@Override
public void receive( double left, double right )
{
mPointer++;
if( mPointer >= mDecimationRate )
{
mDecimatorListener.receive( left, right );
mPointer = 0;
}
}
/**
* Sets the decimated output listener
*/
public void setListener( InternalListener listener )
{
mDecimatorListener = listener;
}
}
/**
* Output adapter - applies gain correction, applies cleanup filter, casts
* double-valued samples into float-valued complex sample and sends the
* result to the registered listener.
*/
public class Output implements InternalListener
{
private int mStageCount;
private int mDecimation;
private double mGain = 1.0d;
private ComplexFilter mCleanupFilter;
public Output( int outputSampleRate, int stageCount, int decimation )
{
mStageCount = stageCount;
mDecimation = decimation;
mCleanupFilter = new ComplexFIRFilterOld( FilterFactory
.getCICCleanupFilter( outputSampleRate,
12000,
60,
WindowType.BLACKMAN ), 1.0f );
setGain();
}
/**
* Receiver method for the output adapter to receive a filtered,
* decimated sample, apply gain correction, apply cleanup filtering
* and output the sample values as a complex sample.
*/
@Override
public void receive( double left, double right )
{
mCleanupFilter.receive(
new Complex( (float)( left * mGain ),
(float)( right * mGain ) ) );
}
/**
* Sets a new decimation rate
*/
public void setDecimationRate( int rate )
{
mDecimation = rate;
setGain();
}
/**
* Adds a listener to receive output samples
*/
public void setListener( Listener<Complex> listener )
{
mCleanupFilter.setListener( listener );
}
/**
* Removes the listener from receiving output samples
*/
public void removeListener( Listener<Complex> listener )
{
mCleanupFilter.setListener( null );
}
public Listener<Complex> getListener()
{
return mCleanupFilter.getListener();
}
/**
* Adjusts the gain correction factor for output samples.
*
* The CIC decimation filter induces significant gain as a factor of
* delay, decimation rate and stage count. So, we apply a gain reduction
* to offset the added gain estimate:
*
* gain = ( decimation * delay ) to power of stageCount
*
* Delay is ONE for the Comb used in this filter.
*/
private void setGain()
{
/* decimation is NOT multiplied by delay since delay is 1 */
double gain = Math.pow( (double)mDecimation, (double)mStageCount );
mGain = 10.0d / gain;
}
}
/**
* Internal interface for each of the CIC components to pass samples
*/
private interface InternalListener
{
public void receive( double left, double right );
}
}

View File

@@ -1,160 +0,0 @@
/*******************************************************************************
* 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 sample.complex.Complex;
public class ComplexFIRFilterOld extends ComplexFilter
{
private ArrayList<Complex> 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 ComplexFIRFilterOld( float[] coefficients, float gain )
{
mCoefficients = coefficients;
mBuffer = new ArrayList<Complex>();
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( new Complex( (float)0.0, (float)0.0 ) );
}
generateIndexMap( mCoefficients.length );
}
public void dispose()
{
mBuffer.clear();
mBuffer = null;
mCoefficients = null;
mIndexMap = null;
}
public void receive( Complex 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
double leftAccumulator = 0;
double rightAccumulator = 0;
//Start with the center tap value
leftAccumulator += mCoefficients[ mCenterCoefficient ] *
mBuffer.get( mIndexMap[ mBufferPointer ][ mCenterCoefficientMapIndex ] ).left();
rightAccumulator += mCoefficients[ mCenterCoefficient ] *
mBuffer.get( mIndexMap[ mBufferPointer ][ mCenterCoefficientMapIndex ] ).right();
//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++ )
{
leftAccumulator += mCoefficients[ x ] *
( mBuffer.get( mIndexMap[ mBufferPointer ][ x ] ).left() +
mBuffer.get( mIndexMap[ mBufferPointer ][ x + mCenterCoefficient ] ).left() );
rightAccumulator += mCoefficients[ x ] *
( mBuffer.get( mIndexMap[ mBufferPointer ][ x ] ).right() +
mBuffer.get( mIndexMap[ mBufferPointer ][ x + mCenterCoefficient ] ).right() );
}
//We're almost finished ... apply gain, cast the doubles to floats and
//send it on it's merry way
send( new Complex( (float)( leftAccumulator * mGain ),
(float)( rightAccumulator * mGain ) ) );
}
/**
* @param odd-sized number of filter taps (ie coefficients) and buffer
*/
private void generateIndexMap( int size )
{
//Ensure we have an odd size
assert( 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 ][ mCenterCoefficientMapIndex ] = 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;
}
}
}
}
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();
}
}

View File

@@ -1,186 +0,0 @@
/*******************************************************************************
* 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 sample.complex.Complex;
public class ComplexHalfBandFilter extends ComplexFilter
{
private ArrayList<Complex> mBuffer;
private int mBufferSize = 1; //Temporary initial value
private int mBufferPointer = 0;
private float mGain;
private float[] mCoefficients;
private int[][] mIndexMap;
private int mCenterCoefficient;
private int mCenterCoefficientMapIndex;
private boolean mDispatchFlag = false;
/**
* Half-Band filter with decimation by 2 against complex samples composed of
* real valued floats.
*
* Takes advantage of the symmetrical nature of FIR filter coefficients by
* adding oldest and newest sample first, then multiplying once by the
* corresponding coefficient
*
* Also, takes advantage of the 0-valued FIR half-band coefficents inherent
* in the half-band filter, and does not calculate those coefficients.
*
* This reduces the workload to (tap-size - 1) / 4 + 1 calculations per sample.
*
* @param filter - filter coefficients
* @param gain - gain multiplier. Use 1.0 for unity/no gain
*/
public ComplexHalfBandFilter( Filters filter, float gain )
{
mCoefficients = filter.getCoefficients();
mBuffer = new ArrayList<Complex>();
mBufferSize = mCoefficients.length;
//Fill the buffer with zero valued samples
for( int x = 0; x < mCoefficients.length; x++ )
{
mBuffer.add( new Complex( (float)0.0, (float)0.0 ) );
}
generateIndexMap( mCoefficients.length );
mGain = gain;
}
/**
* Calculate the filtered value by applying the coefficients against
* the complex samples in mBuffer
*/
public void receive( Complex 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;
}
//Toggle the flag every time, so that we can calculate and dispatch a
//sample every other time, when the flag is true (ie decimate by 2)
mDispatchFlag = !mDispatchFlag;
//Calculate a filtered sample when the flag is true
if( mDispatchFlag )
{
//Convolution - multiply filter coefficients by the circular buffer
//samples to calculate a new filtered value
double leftAccumulator = 0;
double rightAccumulator = 0;
//Start with the center tap value
leftAccumulator += mCoefficients[ mCenterCoefficient ] *
mBuffer.get( mIndexMap[ mBufferPointer ][ mCenterCoefficientMapIndex ] ).left();
rightAccumulator += mCoefficients[ mCenterCoefficient ] *
mBuffer.get( mIndexMap[ mBufferPointer ][ mCenterCoefficientMapIndex ] ).right();
//For the remaining coefficients, add the symmetric samples, oldest and newest
//first, then multiply by the single coefficient
for( int x = 0; x < mCenterCoefficientMapIndex; x += 2 )
{
leftAccumulator += mCoefficients[ x ] *
( mBuffer.get( mIndexMap[ mBufferPointer ][ x ] ).left() +
mBuffer.get( mIndexMap[ mBufferPointer ][ x + 1 ] ).left() );
rightAccumulator += mCoefficients[ x ] *
( mBuffer.get( mIndexMap[ mBufferPointer ][ x ] ).right() +
mBuffer.get( mIndexMap[ mBufferPointer ][ x + 1 ] ).right() );
}
//We're almost finished ... apply gain, cast the doubles to floats and
//send it on it's merry way
send( new Complex( (float)( leftAccumulator * mGain ),
(float)( rightAccumulator * mGain ) ) );
}
}
/**
* Creates an n X (n + 1 / 2) index map enabling quick access to the
* circular buffer samples.
*
* As the buffer shifts right with each subsequent sample, we have to move
* the index pointers with it, for efficient access of the samples.
*
* The first array index value in the index map corresponds to the current
* buffer pointer location.
*
* The second array index value points to the samples that should be
* multiplied by the coefficients as follows:
*
* 0 = center tap sample, to be multiplied by center coefficient
*
* 0 = sample( 1 )
* 1 = sample( size - 1 )
*
* Indexes 0 and 1 will be multiplied by coefficient( 0 ).
*
* Subsequent indexes 3, 4, etc, point to the oldest and newest samples that
* correspond to the matching ( 3 ) coefficient index.
*
* @param odd-sized number of filter taps (ie coefficients) and buffer
*/
private void generateIndexMap( int size )
{
//Ensure we have an odd size
assert( size % 2 == 1 );
int mapWidth = ( ( size + 1 ) / 2 ) + 1;
//Set center tap index for coefficients array
mCenterCoefficient = ( size - 1 ) / 2;
mCenterCoefficientMapIndex = mCenterCoefficient + 1;
mIndexMap = new int[ size ][ mapWidth ];
//Setup the first row, buffer pointer index 0, as a starting point
for( int x = 0; x < mapWidth - 2; x += 2 )
{
mIndexMap[ 0 ][ x ] = x;
mIndexMap[ 0 ][ x + 1 ] = size - 1 - x;
}
//Place center tap index in last element
mIndexMap[ 0 ][ mCenterCoefficientMapIndex ] = mCenterCoefficient;
//For each subsequent row, increment the previous row's value by 1,
//subtracting size as needed, to keep the values between 0 and size - 1
for( int x = 1; x < size; x++ )
{
for( int y = 0; y < mapWidth; y++ )
{
mIndexMap[ x ][ y ] = mIndexMap[ x - 1 ][ y ] + 1;
if( mIndexMap[ x ][ y ] >= size )
{
mIndexMap[ x ][ y ] -= size;
}
}
}
}
}

View File

@@ -1,194 +0,0 @@
/*******************************************************************************
* 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 sample.Listener;
import sample.complex.Complex;
public class ComplexHalfBandNoDecimateFilter extends ComplexFilter
{
private Listener<Complex> mListener;
private ArrayList<Complex> mBuffer;
private int mBufferSize = 1; //Temporary initial value
private int mBufferPointer = 0;
private float mGain;
private float[] mCoefficients;
private int[][] mIndexMap;
private int mCenterCoefficient;
private int mCenterCoefficientMapIndex;
/**
* Half-Band filter with no decimation against complex samples composed of
* real valued floats.
*
* Takes advantage of the symmetrical nature of FIR filter coefficients by
* adding oldest and newest sample first, then multiplying once by the
* corresponding coefficient
*
* Also, takes advantage of the 0-valued FIR half-band coefficents inherent
* in the half-band filter, and does not calculate those coefficients.
*
* This reduces the workload to (tap-size - 1) / 4 + 1 calculations per sample.
*
* @param filter - filter coefficients
* @param gain - gain multiplier. Use 1.0 for unity/no gain
*/
public ComplexHalfBandNoDecimateFilter( Filters filter, float gain )
{
mCoefficients = filter.getCoefficients();
mBuffer = new ArrayList<Complex>();
mBufferSize = mCoefficients.length;
//Fill the buffer with zero valued samples
for( int x = 0; x < mCoefficients.length; x++ )
{
mBuffer.add( new Complex( (float)0.0, (float)0.0 ) );
}
generateIndexMap( mCoefficients.length );
mGain = gain;
}
/**
* Calculate the filtered value by applying the coefficients against
* the complex samples in mBuffer
*/
public void receive( Complex 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 leftAccumulator = 0;
float rightAccumulator = 0;
//Start with the center tap value
leftAccumulator += mCoefficients[ mCenterCoefficient ] *
mBuffer.get( mIndexMap[ mBufferPointer ][ mCenterCoefficientMapIndex ] ).left();
rightAccumulator += mCoefficients[ mCenterCoefficient ] *
mBuffer.get( mIndexMap[ mBufferPointer ][ mCenterCoefficientMapIndex ] ).right();
//For the remaining coefficients, add the symmetric samples, oldest and newest
//first, then multiply by the single coefficient
for( int x = 0; x < mCenterCoefficientMapIndex; x += 2 )
{
leftAccumulator += mCoefficients[ x ] *
( mBuffer.get( mIndexMap[ mBufferPointer ][ x ] ).left() +
mBuffer.get( mIndexMap[ mBufferPointer ][ x + 1 ] ).left() );
rightAccumulator += mCoefficients[ x ] *
( mBuffer.get( mIndexMap[ mBufferPointer ][ x ] ).right() +
mBuffer.get( mIndexMap[ mBufferPointer ][ x + 1 ] ).right() );
}
//We're almost finished ... apply gain, cast the floats to floats and
//send it on it's merry way
send( new Complex( ( leftAccumulator * mGain ),
( rightAccumulator * mGain ) ) );
}
/**
* Creates an n X (n + 1 / 2) index map enabling quick access to the
* circular buffer samples.
*
* As the buffer shifts right with each subsequent sample, we have to move
* the index pointers with it, for efficient access of the samples.
*
* The first array index value in the index map corresponds to the current
* buffer pointer location.
*
* The second array index value points to the samples that should be
* multiplied by the coefficients as follows:
*
* 0 = center tap sample, to be multiplied by center coefficient
*
* 0 = sample( 1 )
* 1 = sample( size - 1 )
*
* Indexes 0 and 1 will be multiplied by coefficient( 0 ).
*
* Subsequent indexes 3, 4, etc, point to the oldest and newest samples that
* correspond to the matching ( 3 ) coefficient index.
*
* @param odd-sized number of filter taps (ie coefficients) and buffer
*/
private void generateIndexMap( int size )
{
//Ensure we have an odd size
assert( size % 2 == 1 );
int mapWidth = ( ( size + 1 ) / 2 ) + 1;
//Set center tap index for coefficients array
mCenterCoefficient = ( size - 1 ) / 2;
mCenterCoefficientMapIndex = mCenterCoefficient + 1;
mIndexMap = new int[ size ][ mapWidth ];
//Setup the first row, buffer pointer index 0, as a starting point
for( int x = 0; x < mapWidth - 2; x += 2 )
{
mIndexMap[ 0 ][ x ] = x;
mIndexMap[ 0 ][ x + 1 ] = size - 1 - x;
}
//Place center tap index in last element
mIndexMap[ 0 ][ mCenterCoefficientMapIndex ] = mCenterCoefficient;
//For each subsequent row, increment the previous row's value by 1,
//subtracting size as needed, to keep the values between 0 and size - 1
for( int x = 1; x < size; x++ )
{
for( int y = 0; y < mapWidth; y++ )
{
mIndexMap[ x ][ y ] = mIndexMap[ x - 1 ][ y ] + 1;
if( mIndexMap[ x ][ y ] >= size )
{
mIndexMap[ x ][ y ] -= size;
}
}
}
}
public static void main( String[] args )
{
float[] c = Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO.mCoefficients;
float sum = 0;
for( int x = 0; x < c.length; x++ )
{
sum += c[ x ];
}
System.out.println( sum );
}
}

View File

@@ -1,177 +0,0 @@
/*******************************************************************************
* 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 sample.Listener;
import sample.complex.Complex;
import buffer.ComplexTappedCircularBuffer;
public class ComplexPolyphaseDecimationFilter extends ComplexFilter
{
private FilterPhase[] mFilterPhases;
private int mPhasePointer = 0;
private int mPhaseCount;
/**
* Implements a decimating poly-phase filter bank for complex samples, as
* described in Understanding Digital Signal Processing, 3e, Lyons, in
* section 10.7, figure 10-14.
*
* Constraints:
* - Low-pass filter coefficient length must be an integer multiple
* of the decimation rate:
*
* Consideration:
* - Low pass filter cutoff frequency must be less than:
* ( input sample rate / 2 / decimation )
*
* @param coefficients - low pass filter coefficients.
* @param decimationRate - integer decimation rate
*/
public ComplexPolyphaseDecimationFilter( double[] coefficients,
int decimationRate )
{
/* Verify that the filter length is an integer multiple of the
* decimation rate */
assert( coefficients.length % decimationRate == 0 );
mPhaseCount = decimationRate;
/* Construct a number of filter phases equal to the decimation rate */
mFilterPhases = new FilterPhase[ mPhaseCount ];
for( int phaseNumber = 0; phaseNumber < mPhaseCount; phaseNumber++ )
{
double[] phaseCoefficients =
getPhaseCoefficients( coefficients, decimationRate, phaseNumber );
mFilterPhases[ phaseNumber ] = new FilterPhase( phaseCoefficients );
}
}
/**
* Inputs a new sample into the filter at the non-decimated rate
*/
@Override
public void receive( Complex sample )
{
mFilterPhases[ mPhasePointer++ ].receive( sample );
if( mPhasePointer >= mPhaseCount )
{
/* Broadcast a new output sample */
calculate();
/* Reset the pointer */
mPhasePointer = 0;
}
}
/**
* Outputs a complex sample at the decimated rate, comprised of the sum of
* the outputs of the individual filter stages, trigger by a decimation rate
* counter on the input side.
*/
private void calculate()
{
if( hasListener() )
{
/* Accumulators */
float left = 0.0f;
float right = 0.0f;
for( int x = 0; x < mPhaseCount; x++ )
{
Complex phaseSample = mFilterPhases[ x ].calculate();
left += phaseSample.left();
right += phaseSample.right();
}
send( new Complex( left, right ) );
}
}
/**
* Returns the subset of filter coefficients that belong to the filter bank
* phase.
*/
private double[] getPhaseCoefficients( double[] coefficients,
int phaseCount,
int phaseNumber )
{
int phaseFilterLength = (int)( coefficients.length / phaseCount );
double[] phaseCoefficients = new double[ phaseFilterLength ];
for( int x = 0; x < phaseFilterLength; x++ )
{
phaseCoefficients[ x ] = coefficients[ x * phaseCount + phaseNumber ];
}
return phaseCoefficients;
}
/**
* Implements a single phase of a poly-phase filter bank
*/
public class FilterPhase implements Listener<Complex>
{
private int mLength;
private double[] mCoefficients;
private ComplexTappedCircularBuffer mBuffer;
/**
* @param coefficients - subset of the filter coefficients for this
* phase of the poly-phase filter bank
*/
public FilterPhase( double[] coefficients )
{
mCoefficients = coefficients;
mLength = coefficients.length;
/* The constructor preloads the buffer with 0-valued samples */
mBuffer = new ComplexTappedCircularBuffer( coefficients.length );
}
@Override
public void receive( Complex sample )
{
mBuffer.add( sample );
}
/**
* Performs convolution by multiplying the buffer samples by their
* corresponding filter coefficients and returns the sum
*/
public Complex calculate()
{
/* Accumulators */
double left = 0.0d;
double right = 0.0d;
for( int x = 0; x < mLength; x++ )
{
left += ( mBuffer.get( x ).left() * mCoefficients[ x ] );
right += ( mBuffer.get( x ).right() * mCoefficients[ x ] );
}
return new Complex( (float)left, (float)right );
}
}
}

View File

@@ -0,0 +1,60 @@
package dsp.filter.dc;
import sample.Listener;
import sample.real.RealBuffer;
public class DCRemovalFilter_RealBuffer implements Listener<RealBuffer>
{
private Listener<RealBuffer> mListener;
/**
* Removes DC bias present across the samples in the buffer by calculating
* the mean DC component and subtracting that component from each of the
* samples.
*/
public DCRemovalFilter_RealBuffer()
{
}
public void dispose()
{
mListener = null;
}
public RealBuffer filter( RealBuffer buffer )
{
double accumulator = 0.0d;
for( float sample: buffer.getSamples() )
{
accumulator += sample;
}
float mean = (float)( accumulator / (double)buffer.getSamples().length );
for( float sample: buffer.getSamples() )
{
sample -= mean;
}
return buffer;
}
@Override
public void receive( RealBuffer buffer )
{
if( mListener != null )
{
mListener.receive( filter( buffer ) );
}
}
public void setListener( Listener<RealBuffer> listener )
{
mListener = listener;
}
public void removeListener()
{
mListener = null;
}
}