diff --git a/src/dsp/filter/cic/RealPrimeCICDecimate.java b/src/dsp/filter/cic/RealPrimeCICDecimate.java
new file mode 100644
index 00000000..8e359cea
--- /dev/null
+++ b/src/dsp/filter/cic/RealPrimeCICDecimate.java
@@ -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
+ ******************************************************************************/
+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 mDecimatingStages =
+ new ArrayList();
+
+ 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 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 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 getPrimeFactors( int decimation )
+ {
+ ArrayList stages = new ArrayList();
+
+ 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 mStages = new ArrayList();
+ 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 listener )
+ {
+ mHalfBandFilter.setListener( listener );
+ }
+
+ /**
+ * Removes the listener from receiving output samples
+ */
+ public void removeListener()
+ {
+ mHalfBandFilter.removeListener();
+ }
+ }
+}
diff --git a/src/dsp/fsk/LTRFSKDecoder.java b/src/dsp/fsk/LTRFSKDecoder.java
index d7745bf0..118ea13f 100644
--- a/src/dsp/fsk/LTRFSKDecoder.java
+++ b/src/dsp/fsk/LTRFSKDecoder.java
@@ -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, Instrumentable
{
+ private RealPrimeCICDecimate mCICDecimator;
+
private FloatHalfBandFilter mHBFilter1;
private FloatHalfBandFilter mHBFilter2;
private FloatHalfBandFilter mHBFilter3;
@@ -58,6 +62,7 @@ public class LTRFSKDecoder implements Listener, 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, 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, Instrumentable
{
mHBFilter1.receive( sample );
}
+
+ mCICDecimator.receive(buffer);
}
public void addListener( Listener listener )
@@ -139,6 +148,7 @@ public class LTRFSKDecoder implements Listener, 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, 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, Instrumentable
{
switch( tap.getName() )
{
+ case TAP_CIC:
+ mCICDecimator.removeListener();
+ break;
case TAP_F1_F2:
mHBFilter1.setListener( mHBFilter2 );
break;
diff --git a/src/instrument/tap/stream/FloatBufferTap.java b/src/instrument/tap/stream/FloatBufferTap.java
index 55bbfd93..5b1508f5 100644
--- a/src/instrument/tap/stream/FloatBufferTap.java
+++ b/src/instrument/tap/stream/FloatBufferTap.java
@@ -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
{
- private Listener mListener;
+ private final static Logger mLog =
+ LoggerFactory.getLogger( FloatBufferTap.class );
+
+ private Listener mRealBufferListener;
public FloatBufferTap( String name, int delay, float sampleRateRatio )
{
@@ -15,24 +21,26 @@ public class FloatBufferTap extends FloatTap implements Listener
@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 listener )
{
- mListener = listener;
+ mRealBufferListener = listener;
}
public void removeListener( Listener listener )
{
- mListener = null;
+ mRealBufferListener = null;
}
}
diff --git a/src/instrument/tap/stream/FloatTap.java b/src/instrument/tap/stream/FloatTap.java
index 1beebfa5..1d94daff 100644
--- a/src/instrument/tap/stream/FloatTap.java
+++ b/src/instrument/tap/stream/FloatTap.java
@@ -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;
}
}
diff --git a/src/sample/decimator/RealDecimator.java b/src/sample/decimator/RealDecimator.java
index 3a01249f..d8d34e31 100644
--- a/src/sample/decimator/RealDecimator.java
+++ b/src/sample/decimator/RealDecimator.java
@@ -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;
}