Adding real cic decimator for testing in LTR FSK demodulator

This commit is contained in:
Denny Sheirer
2015-09-18 18:24:58 +00:00
parent ba41022853
commit f070510acc
5 changed files with 464 additions and 26 deletions

View File

@@ -0,0 +1,414 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2015 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.cic;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import dsp.filter.FilterFactory;
import dsp.filter.Filters;
import dsp.filter.Window.WindowType;
import dsp.filter.fir.real.RealFIRFilter_RB_RB;
import dsp.filter.halfband.real.HalfBandFilter_RB_RB;
import sample.Listener;
import sample.decimator.RealDecimator;
import sample.real.RealBuffer;
import sample.real.RealSampleListener;
import sample.real.RealToRealBufferAssembler;
public class RealPrimeCICDecimate
{
private final static Logger mLog =
LoggerFactory.getLogger( RealPrimeCICDecimate.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,
257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,
367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,
467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,
599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691 };
private ArrayList<DecimatingStage> mDecimatingStages =
new ArrayList<DecimatingStage>();
private DecimatingStage mFirstDecimatingStage;
private Output mOutput;
private int mOutputBufferSize = 2048;
/**
* Non-Recursive Prime-Factor CIC Filter with float sample array inputs and
* decimated float sample output.
*
* Implements the CIC filter described in Understanding Digital Signal
* Processing, 3e, Lyons, on page 769. This filter is comprised of multiple
* decimating stages each with a prime factor decimation rate. Multiple
* stages are cascaded to achieve the overall decimation rate.
*
* This filter supports a maximum decimation rate of 700. This filter can
* be adapted to higher decimation rates by adding additional prime factors
* to the PRIMES array.
*
* @param decimation - overall decimation rate
* @param order - filter order
*/
public RealPrimeCICDecimate( int decimation, int order, int passFrequency,
int attenuation, WindowType windowType )
{
this( decimation, order, passFrequency, attenuation, windowType, 2048 );
}
public RealPrimeCICDecimate( int decimation, int order, int passFrequency,
int attenuation, WindowType windowType, int outputBufferSize )
{
assert( decimation <= 700 );
mOutputBufferSize = outputBufferSize;
List<Integer> stageSizes = getPrimeFactors( decimation );
for( int x = 0; x < stageSizes.size(); x++ )
{
DecimatingStage stage = new DecimatingStage( stageSizes.get( x ), order );
mDecimatingStages.add( stage );
if( x == 0 )
{
/* Reference to first stage -- will receive all samples */
mFirstDecimatingStage = stage;
}
else
{
/* Wire the current stage to the previous stage */
mDecimatingStages.get( x - 1 ).setListener( stage );
}
}
mOutput = new Output( 48000, passFrequency, attenuation, windowType,
outputBufferSize );
mDecimatingStages.get( mDecimatingStages.size() - 1 )
.setListener( mOutput );
}
public void dispose()
{
for( DecimatingStage stage: mDecimatingStages )
{
stage.dispose();
}
mDecimatingStages.clear();
mDecimatingStages = null;
mFirstDecimatingStage = null;
mOutput.dispose();
mOutput = null;
}
/**
* Adds a listener to receive the output of this CIC decimation filter
*/
public void setListener( Listener<RealBuffer> listener )
{
mOutput.setListener( listener );
}
/**
* Removes listener from output of this CIC decimation filter
*/
public void removeListener()
{
mOutput.removeListener();
}
/**
* Calculates the prime factors of the decimation rate up to a maximum
* decimation rate of 700. If you wish to have decimation rates higher
* than 700, then add additional prime factors to the PRIMES array.
*
* @param decimation - integral decimation rate
* @return - ordered list (smallest to largest) of prime factors
*/
public static List<Integer> getPrimeFactors( int decimation )
{
ArrayList<Integer> stages = new ArrayList<Integer>();
int pointer = 0;
while( decimation > 0 && pointer < PRIMES.length )
{
int prime = PRIMES[ pointer ];
if( decimation % prime == 0 )
{
stages.add( prime );
decimation /= prime;
}
else
{
pointer++;
}
}
return stages;
}
/**
* Primary input method for receiving real buffers.
*/
public void receive( RealBuffer buffer )
{
if( mFirstDecimatingStage != null )
{
for( float sample: buffer.getSamples() )
{
mFirstDecimatingStage.receive( sample );
}
}
}
/**
* Decimating stage combines multiple CIC stages with a decimator. The
* number of stages is indicated by the order value and the size indicates
* the decimation rate of this stage.
*/
public class DecimatingStage implements RealSampleListener
{
private ArrayList<Stage> mStages = new ArrayList<Stage>();
private Stage mFirstStage;
private RealDecimator mDecimator;
public DecimatingStage( int size, int order )
{
for( int x = 0; x < order; x++ )
{
Stage stage;
if( size == 2 )
{
stage = new TwoStage();
}
else
{
stage = new Stage( size );
}
mStages.add( stage );
if( x == 0 )
{
mFirstStage = stage;
}
else
{
mStages.get( x - 1 ).setListener( stage );
}
}
mDecimator = new RealDecimator( size );
mStages.get( mStages.size() - 1 ).setListener( mDecimator );
}
public void dispose()
{
for( Stage stage: mStages )
{
stage.dispose();
}
mStages.clear();
mDecimator.dispose();
mDecimator = null;
mFirstStage = null;
mStages = null;
}
@Override
public void receive( float sample )
{
mFirstStage.receive( sample );
}
public void setListener( RealSampleListener listener )
{
mDecimator.setListener( listener );
}
}
/**
* Single non-decimating CIC stage component. Uses a circular buffer and a
* running average internally to implement the stage so that stage size has
* essentially no impact on the computational requirements of the stage
*/
public class Stage implements RealSampleListener
{
protected RealSampleListener mListener;
private float[] mSamples;
protected float mSum;
private int mSamplePointer = 0;
private int mSize;
protected float mGain;
public Stage()
{
}
public Stage( int size )
{
mSize = size - 1;
mSamples = new float[ mSize ];
mGain = 1.0f / (float)size;
}
public void dispose()
{
mListener = null;
}
public void receive( float sample )
{
/* Subtract the oldest sample and add back in the newest */
mSum = mSum - mSamples[ mSamplePointer ] + sample;
/* Overwrite the oldest sample with the newest */
mSamples[ mSamplePointer ] = sample;
mSamplePointer++;
if( mSamplePointer >= mSize )
{
mSamplePointer = 0;
}
if( mListener != null )
{
mListener.receive( mSum * mGain );
}
}
public void setListener( RealSampleListener listener )
{
mListener = listener;
}
}
/**
* Size 2 stage that removes the unnecessary circular buffer management for
* a two-stage.
*/
public class TwoStage extends Stage
{
public TwoStage()
{
super();
mGain = 0.5f;
}
public void receive( float sample )
{
float sum = mSum + sample;
mSum = sample;
if( mListener != null )
{
mListener.receive( sum * mGain );
}
}
}
/**
* 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 RealSampleListener
{
/* Decimated output buffers will contain 1024 samples */
private RealToRealBufferAssembler mAssembler;
private RealFIRFilter_RB_RB mCleanupFilter;
private HalfBandFilter_RB_RB mHalfBandFilter = new HalfBandFilter_RB_RB(
Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO.getCoefficients(), 0.4f, false );
public Output( int outputSampleRate, int passFrequency, int attenuation,
WindowType windowType, int outputBufferSize )
{
mCleanupFilter = new RealFIRFilter_RB_RB( FilterFactory
.getCICCleanupFilter( outputSampleRate,
passFrequency,
attenuation,
windowType ), 0.4f );
mAssembler = new RealToRealBufferAssembler( outputBufferSize );
mAssembler.setListener( mCleanupFilter );
mCleanupFilter.setListener( mHalfBandFilter );
}
public void dispose()
{
mAssembler.dispose();
mCleanupFilter.dispose();
mHalfBandFilter.dispose();
}
/**
* 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( float sample )
{
mAssembler.receive( sample * 32.0f );
}
/**
* Adds a listener to receive output samples
*/
public void setListener( Listener<RealBuffer> listener )
{
mHalfBandFilter.setListener( listener );
}
/**
* Removes the listener from receiving output samples
*/
public void removeListener()
{
mHalfBandFilter.removeListener();
}
}
}

View File

@@ -17,29 +17,33 @@
******************************************************************************/
package dsp.fsk;
import instrument.Instrumentable;
import instrument.tap.Tap;
import instrument.tap.TapGroup;
import instrument.tap.stream.BinaryTap;
import instrument.tap.stream.FloatTap;
import instrument.tap.stream.SymbolEventTap;
import java.util.ArrayList;
import java.util.List;
import sample.Listener;
import sample.real.RealBuffer;
import dsp.filter.Filters;
import dsp.filter.FloatHalfBandFilter;
import dsp.filter.FloatHalfBandNoDecimateFilter;
import dsp.filter.LTRPulseShapingFilter;
import dsp.filter.SquaringFilter;
import dsp.filter.Window.WindowType;
import dsp.filter.cic.RealPrimeCICDecimate;
import dsp.filter.dc.IIRSinglePoleDCRemovalFilter;
import dsp.symbol.Slicer;
import dsp.symbol.Slicer.Output;
import instrument.Instrumentable;
import instrument.tap.Tap;
import instrument.tap.TapGroup;
import instrument.tap.stream.BinaryTap;
import instrument.tap.stream.FloatBufferTap;
import instrument.tap.stream.FloatTap;
import instrument.tap.stream.SymbolEventTap;
import sample.Listener;
import sample.real.RealBuffer;
public class LTRFSKDecoder implements Listener<RealBuffer>, Instrumentable
{
private RealPrimeCICDecimate mCICDecimator;
private FloatHalfBandFilter mHBFilter1;
private FloatHalfBandFilter mHBFilter2;
private FloatHalfBandFilter mHBFilter3;
@@ -58,6 +62,7 @@ public class LTRFSKDecoder implements Listener<RealBuffer>, Instrumentable
private final String TAP_F4_F5 = "LTR FSK Demod Filter4 >< Filter5";
private final String TAP_F5_F6 = "LTR FSK Demod Filter5 >< Filter6";
private final String TAP_F6_DT = "LTR FSK Demod Filter6 >< DC Filter";
private final String TAP_CIC = "LTR FSK Demod CIC Decimator";
private final String TAP_DT_SF1 = "LTR FSK Demod DC Filter >< Squaring Filter";
private final String TAP_SF1_PS = "LTR FSK Demod Squaring Filter >< Pulse Shaper";
private final String TAP_PS_SLICER = "LTR FSK Demod Pulse Shaper >< Slicer";
@@ -69,6 +74,8 @@ public class LTRFSKDecoder implements Listener<RealBuffer>, Instrumentable
*/
public LTRFSKDecoder()
{
mCICDecimator = new RealPrimeCICDecimate( 32, 1, 375, 60, WindowType.HAMMING, 2 );
mHBFilter1 = new FloatHalfBandFilter(
Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.1002f );
@@ -112,6 +119,8 @@ public class LTRFSKDecoder implements Listener<RealBuffer>, Instrumentable
{
mHBFilter1.receive( sample );
}
mCICDecimator.receive(buffer);
}
public void addListener( Listener<Boolean> listener )
@@ -139,6 +148,7 @@ public class LTRFSKDecoder implements Listener<RealBuffer>, Instrumentable
group.add( new FloatTap( TAP_F4_F5, 14, 0.125f ) );
group.add( new FloatTap( TAP_F5_F6, 15, 0.0625f ) );
group.add( new FloatTap( TAP_F6_DT, 31, 0.0625f ) );
group.add( new FloatBufferTap( TAP_CIC, 31, 0.0625f ) );
group.add( new FloatTap( TAP_DT_SF1, 31, 0.0625f ) );
group.add( new BinaryTap( TAP_SF1_PS, 33, 0.0625f ) );
group.add( new BinaryTap( TAP_PS_SLICER, 33, 0.0625f ) );
@@ -155,6 +165,9 @@ public class LTRFSKDecoder implements Listener<RealBuffer>, Instrumentable
{
switch( tap.getName() )
{
case TAP_CIC:
mCICDecimator.setListener( (FloatBufferTap)tap );
break;
case TAP_F1_F2:
mHBFilter1.setListener( (FloatTap)tap );
((FloatTap)tap).setListener( mHBFilter2 );
@@ -202,6 +215,9 @@ public class LTRFSKDecoder implements Listener<RealBuffer>, Instrumentable
{
switch( tap.getName() )
{
case TAP_CIC:
mCICDecimator.removeListener();
break;
case TAP_F1_F2:
mHBFilter1.setListener( mHBFilter2 );
break;

View File

@@ -1,11 +1,17 @@
package instrument.tap.stream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Listener;
import sample.real.RealBuffer;
public class FloatBufferTap extends FloatTap implements Listener<RealBuffer>
{
private Listener<RealBuffer> mListener;
private final static Logger mLog =
LoggerFactory.getLogger( FloatBufferTap.class );
private Listener<RealBuffer> mRealBufferListener;
public FloatBufferTap( String name, int delay, float sampleRateRatio )
{
@@ -15,24 +21,26 @@ public class FloatBufferTap extends FloatTap implements Listener<RealBuffer>
@Override
public void receive( RealBuffer buffer )
{
if( mListener != null )
mLog.debug("Got a buffer!");
if( mRealBufferListener != null )
{
mListener.receive( buffer );
mRealBufferListener.receive( buffer );
}
for( float sample: buffer.getSamples() )
{
receive( sample );
mLog.debug("Sending a sample to the tap");
super.receive( sample );
}
}
public void setListener( Listener<RealBuffer> listener )
{
mListener = listener;
mRealBufferListener = listener;
}
public void removeListener( Listener<RealBuffer> listener )
{
mListener = null;
mRealBufferListener = null;
}
}

View File

@@ -23,7 +23,7 @@ import sample.real.RealSampleListener;
public class FloatTap extends StreamTap implements RealSampleListener
{
private RealSampleListener mListener;
private RealSampleListener mFloatTapListener;
public FloatTap( String name,
int delay,
@@ -35,9 +35,9 @@ public class FloatTap extends StreamTap implements RealSampleListener
@Override
public void receive( float sample )
{
if( mListener != null )
if( mFloatTapListener != null )
{
mListener.receive( sample );
mFloatTapListener.receive( sample );
}
for( TapListener listener: mListeners )
@@ -48,11 +48,11 @@ public class FloatTap extends StreamTap implements RealSampleListener
public void setListener( RealSampleListener listener )
{
mListener = listener;
mFloatTapListener = listener;
}
public void removeListener( RealSampleListener listener )
{
mListener = null;
mFloatTapListener = null;
}
}

View File

@@ -1,11 +1,11 @@
package sample.decimator;
import sample.complex.ComplexSampleListener;
import sample.real.RealSampleListener;
public class RealDecimator implements ComplexSampleListener
public class RealDecimator implements RealSampleListener
{
private ComplexSampleListener mListener;
private RealSampleListener mListener;
private int mCounter = 0;
private int mDecimationRate;
@@ -36,13 +36,13 @@ public class RealDecimator implements ComplexSampleListener
* to the registered listener
*/
@Override
public void receive( float i, float q )
public void receive( float sample )
{
mCounter++;
if( mCounter >= mDecimationRate )
{
mListener.receive( i, q );
mListener.receive( sample );
mCounter = 0;
}
@@ -51,7 +51,7 @@ public class RealDecimator implements ComplexSampleListener
/**
* Sets the decimated output listener
*/
public void setListener( ComplexSampleListener listener )
public void setListener( RealSampleListener listener )
{
mListener = listener;
}