Tuner Channel - uses new CIC prime decimation filter for decimating channel to 48k sample rate.

This commit is contained in:
dennis.sheirer
2015-04-07 01:10:10 +00:00
parent 90149fe600
commit d55d7609cb
6 changed files with 112 additions and 209 deletions

View File

@@ -262,12 +262,14 @@ public class ComplexCICDecimate extends ComplexFilter
mDecimation = decimation;
mCleanupFilter = new ComplexFIRFilter( FilterFactory
.getCICCleanupFilter( outputSampleRate,
stageCount,
1,
12000,
60,
WindowType.BLACKMAN ), 1.0d );
setGain();
}
/**
* Receiver method for the output adapter to receive a filtered,
* decimated sample, apply gain correction, apply cleanup filtering

View File

@@ -3,7 +3,9 @@ package dsp.filter;
import java.util.ArrayList;
import java.util.List;
import sample.Broadcaster;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Listener;
import sample.complex.ComplexBuffer;
import sample.complex.ComplexSample;
@@ -11,8 +13,11 @@ import sample.complex.ComplexSampleListener;
import sample.decimator.ComplexDecimator;
import dsp.filter.Window.WindowType;
public class ComplexPrimeCICDecimate implements Listener<ComplexBuffer>
public class ComplexPrimeCICDecimate
{
private final static Logger mLog =
LoggerFactory.getLogger( ComplexPrimeCICDecimate.class );
public static int[] PRIMES = { 2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,
59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,
157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,
@@ -25,11 +30,11 @@ public class ComplexPrimeCICDecimate implements Listener<ComplexBuffer>
new ArrayList<DecimatingStage>();
private DecimatingStage mFirstDecimatingStage;
public Broadcaster<ComplexSample> mBroadcaster =
new Broadcaster<ComplexSample>();
public Output mOutput;
private Output mOutput;
private Listener<ComplexSample> mListener;
/**
* Non-Recursive Prime-Factor CIC Filter with float sample array inputs and
* decimated, single paired i/q float sample output.
@@ -46,7 +51,8 @@ public class ComplexPrimeCICDecimate implements Listener<ComplexBuffer>
* @param decimation - overall decimation rate
* @param order - filter order
*/
public ComplexPrimeCICDecimate( int decimation, int order )
public ComplexPrimeCICDecimate( int decimation, int order,
int passFrequency, int attenuation, WindowType windowType )
{
assert( decimation <= 700 );
@@ -69,13 +75,12 @@ public class ComplexPrimeCICDecimate implements Listener<ComplexBuffer>
mDecimatingStages.get( x - 1 ).addListener( stage );
}
}
mOutput = new Output( 48000, stageSizes.size(), decimation );
mOutput = new Output( 48000, order, decimation, passFrequency,
attenuation, windowType );
mDecimatingStages.get( mDecimatingStages.size() - 1 )
.addListener( mOutput );
mOutput.setListener( mBroadcaster );
}
public void dispose()
@@ -86,26 +91,22 @@ public class ComplexPrimeCICDecimate implements Listener<ComplexBuffer>
}
mDecimatingStages.clear();
mBroadcaster.dispose();
mBroadcaster = null;
}
/**
* Adds a listener to receive the output of this CIC decimation filter
*/
public void addListener( Listener<ComplexSample> listener )
public void setListener( Listener<ComplexSample> listener )
{
mBroadcaster.addListener( listener );
mOutput.setListener( listener );
}
/**
* Removes listener from output of this CIC decimation filter
*/
public void removeListener( Listener<ComplexSample> listener )
{
mBroadcaster.removeListener( listener );
mOutput.removeListener( listener );
}
/**
@@ -144,11 +145,8 @@ public class ComplexPrimeCICDecimate implements Listener<ComplexBuffer>
/**
* Primary input method for receiving sample arrays composed as I,Q,I,Q, etc.
*/
@Override
public void receive( ComplexBuffer sampleArray )
public void receive( float[] samples )
{
float[] samples = sampleArray.getSamples();
for( int x = 0; x < samples.length; x += 2 )
{
mFirstDecimatingStage.receive( samples[ x ], samples[ x + 1 ] );
@@ -232,28 +230,27 @@ public class ComplexPrimeCICDecimate implements Listener<ComplexBuffer>
private float mISum;
private float mQSum;
private int mSamplePointer;
private int mSamplePointer = 0;
private int mSize;
public Stage( int size )
{
mSize = size;
mISamples = new float[ size ];
mQSamples = new float[ size ];
mSize = size - 1;
mISamples = new float[ mSize ];
mQSamples = new float[ mSize ];
}
public void dispose()
{
mListener = null;
mISamples = null;
mQSamples = null;
}
public void receive( float i, float q )
{
/* Subtract the oldest sample and add back in the newest */
mISum = mISum - mISamples[ mSamplePointer ] + i;
mQSum = mQSum - mISamples[ mSamplePointer ] + q;
mQSum = mQSum - mQSamples[ mSamplePointer ] + q;
/* Overwrite the oldest sample with the newest */
mISamples[ mSamplePointer ] = i;
@@ -285,19 +282,22 @@ public class ComplexPrimeCICDecimate implements Listener<ComplexBuffer>
*/
public class Output implements ComplexSampleListener
{
private int mStageCount;
private int mDecimation;
private int mOrder;
private double mGain = 1.0d;
private ComplexFilter mCleanupFilter;
public Output( int outputSampleRate, int stageCount, int decimation )
public Output( int outputSampleRate, int order, int decimation,
int passFrequency, int attenuation, WindowType windowType )
{
mStageCount = stageCount;
mOrder = order;
mDecimation = decimation;
mCleanupFilter = new ComplexFIRFilter( FilterFactory
.getCICCleanupFilter( outputSampleRate,
stageCount,
WindowType.BLACKMAN ), 1.0d );
mOrder,
passFrequency,
attenuation,
windowType ), 1.0d );
setGain();
}
@@ -359,10 +359,9 @@ public class ComplexPrimeCICDecimate implements Listener<ComplexBuffer>
private void setGain()
{
/* decimation is NOT multiplied by delay since delay is 1 */
double gain = Math.pow( (double)mDecimation, (double)mStageCount );
double gain = Math.pow( (double)mDecimation, (double)mOrder );
mGain = 10.0d / gain;
mGain = 1.0d / gain;
}
}
}

View File

@@ -34,8 +34,8 @@ import dsp.filter.Window.WindowType;
public class FilterFactory
{
private final static Logger mLog =
LoggerFactory.getLogger( FilterFactory.class );
// private final static Logger mLog =
// LoggerFactory.getLogger( FilterFactory.class );
/**
* Generates coefficients for a unity-gain, windowed low-pass filter
@@ -381,99 +381,14 @@ public class FilterFactory
return (int)( Math.round( (double)attenuation / ( 22.0d * frequency ) ) );
}
public static ComplexFilter[] getDecimationFilters( int sampleRate,
int decimatedRate,
long passFrequency,
int attenuation,
WindowType windowType )
public static ComplexPrimeCICDecimate getDecimationFilter( int sampleRate,
int decimatedRate, int order, int passFrequency, int attenuation,
WindowType windowType )
{
ComplexFilter[] filters;
if( sampleRate == 96000 && decimatedRate == 48000 )
{
filters = new ComplexFilter[ 1 ];
filters[ 0 ] = new ComplexHalfBandFilter(
Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002 );
}
else if( sampleRate == 192000 && decimatedRate == 48000 )
{
filters = new ComplexFilter[ 2 ];
filters[ 0 ] = new ComplexHalfBandFilter(
Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002 );
filters[ 1 ] = new ComplexHalfBandFilter(
Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002 );
}
else
{
filters = new ComplexFilter[ 1 ];
int decimationRate = (int)( sampleRate / decimatedRate );
int decimate = (int)( sampleRate / decimatedRate );
// if( decimate < 25 )
// {
filters[ 0 ] = new ComplexCICDecimate( decimate, 1, decimatedRate );
// }
// else
// {
// filters[ 0 ] = new ComplexCICDecimate( decimate, 3, decimatedRate );
// }
// int finalStopFrequency = decimatedRate - passFrequency;
//
// int[] rates = getPolyphaseDecimationRates( sampleRate,
// decimatedRate,
// passFrequency,
// finalStopFrequency );
//
// filters = new ComplexFilter[ rates.length ];
//
// int counter = 0;
// int currentSampleRate = sampleRate;
// int currentDecimatedRate = sampleRate;
//
// while( counter < rates.length )
// {
// currentDecimatedRate /= rates[ counter ];
//
// int stopFrequency = currentDecimatedRate- passFrequency;
//
// int proposedFilterLength = getTapCount( currentSampleRate,
// passFrequency,
// stopFrequency,
// attenuation );
//
// /* check filter length is multiple of decimation rate */
// int multiplier = (int)( proposedFilterLength / rates[ counter ] );
//
// /* increase filter length to the next multiple, if needed */
// if( multiplier * rates[ counter ] < proposedFilterLength )
// {
// multiplier++;
// }
//
// double[] filter = getLowPass( currentSampleRate,
// passFrequency,
// rates[counter] * multiplier,
// windowType );
//
// filters[ counter ] =
// new ComplexPolyphaseDecimationFilter( filter, rates[ counter ] );
//
// Log.info( "FilterFactory - PFD filter [" + ( counter + 1 ) + "/" + rates.length +
// "] overall rate [" + sampleRate +
// "] decimate [" + rates[ counter ] +
// "] filter taps [" + filter.length +
// "] rate [" + currentSampleRate +
// "] pass [" + passFrequency +
// "]" );
//
// currentSampleRate /= rates[ counter ];
// counter++;
// }
}
return filters;
return new ComplexPrimeCICDecimate( decimationRate, order,
passFrequency, attenuation, windowType );
}
/**
@@ -516,10 +431,10 @@ public class FilterFactory
int stage1 = findClosest( optimalStage1, factors );
mLog.info( "Decimation rate [" + decimation +
"] stage1 optimal [" + optimalStage1 +
"] stage1 actual [" + stage1 +
"]");
// mLog.info( "Decimation rate [" + decimation +
// "] stage1 optimal [" + optimalStage1 +
// "] stage1 actual [" + stage1 +
// "]");
if( stage1 == decimation || stage1 == 1 )
{
@@ -627,11 +542,11 @@ public class FilterFactory
int retVal = (int)( 2.0d * decimation * ( numerator / denominator ) );
mLog.info( "Optimal Stage 1 Decimation - rate [" + sampleRate +
"] pass [" + passFrequency +
"] bw ratio [" + ratio +
"] optimal [" + retVal +
"]" );
// mLog.info( "Optimal Stage 1 Decimation - rate [" + sampleRate +
// "] pass [" + passFrequency +
// "] bw ratio [" + ratio +
// "] optimal [" + retVal +
// "]" );
return retVal;
}
@@ -660,15 +575,14 @@ public class FilterFactory
* @return
*/
public static double[] getCICCleanupFilter( int outputSampleRate,
int stageCount,
int order,
int passFrequency,
int attenuation,
WindowType window )
{
int pass = (int)( outputSampleRate / 4 );
int stop = pass + (int)( (double)pass * 0.25d );
int attenuation = 60; //db
int taps = getTapCount( outputSampleRate, passFrequency, passFrequency + 1500,
attenuation );
int taps = getTapCount( outputSampleRate, pass, stop, attenuation );
/* Make tap count odd */
if( taps% 2 == 0 )
{
@@ -676,7 +590,7 @@ public class FilterFactory
}
double[] frequencyResponse =
getCICResponseArray( outputSampleRate, pass, taps, stageCount );
getCICResponseArray( outputSampleRate, passFrequency, taps, order );
//Apply Inverse DFT against frequency response unity values, leaving the
//IDFT bin results in the frequency response array
@@ -707,7 +621,7 @@ public class FilterFactory
public static double[] getCICResponseArray( int sampleRate,
int frequency,
int length,
int stageCount )
int order )
{
double[] cicArray = new double[ length * 2 ];
@@ -717,14 +631,14 @@ public class FilterFactory
cicArray[ 0 ] = 1.0d;
double unityResponse = Math.pow( Math.sin( 1.0d / (double)length ) /
( 1.0d / (double)length ), stageCount );
( 1.0d / (double)length ), order );
for( int x = 1; x <= binCount; x++ )
{
/* Calculate unity response plus amplification for droop */
double compensated = 1.0d + ( unityResponse -
Math.pow( ( Math.sin( (double)x / (double)length ) /
( (double)x / (double)length ) ), stageCount ) );
( (double)x / (double)length ) ), order ) );
cicArray[ x ] = compensated;
cicArray[ length - x ] = compensated;
@@ -821,4 +735,11 @@ public class FilterFactory
return coefficients;
}
public static void main( String[] args )
{
double[] coefficients = getCICCleanupFilter( 48000, 2, 60, 24000, WindowType.BLACKMAN );
System.out.println( Arrays.toString( coefficients ) );
}
}

View File

@@ -17,6 +17,9 @@
******************************************************************************/
package dsp.nbfm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Listener;
import sample.complex.ComplexSample;
import sample.real.RealSampleBroadcaster;
@@ -28,6 +31,9 @@ import dsp.filter.Window.WindowType;
public class FilteringNBFMDemodulator implements Listener<ComplexSample>
{
private final static Logger mLog =
LoggerFactory.getLogger( FilteringNBFMDemodulator.class );
private RealSampleBroadcaster mBroadcaster = new RealSampleBroadcaster();
private ComplexFIRFilter mIQFilter;
private FloatFIRFilter mAudioFilter;