Memory/CPU performance improvement and Audio/Mixer source restructuring

Converted to use float primitives and to keep samples in buffer format until the sample rate is decimated to 48kHz.  
Added new non-recursive, CIC decimation filter -- untested

Updated sound card/mixer input source management.  Now testing for mono/stereo support for each card and limiting channel choices accordingly.  Separated the mixersource into a ComplexMixer and and SimplexMixerSource.  The ComplexMixer is used for sound card based tuners like the funcube.  The SimplexMixerSource is used for demodulated sound card inputs.

Interfaces for sample handling - standardized on using 'Complex' to indicate I/Q samples and 'Simplex' to indicate demodulated float samples.  The processing chain now accepts either a decimated (48k) list of complex samples from a tuner channel source, or a 48k simplex buffer (array of demodulated float samples).

Resolved issue with HackRF tuner not applying tuner configuration information.
This commit is contained in:
dennis.sheirer
2014-10-26 10:11:30 +00:00
parent 0ad710fea2
commit e61c3a891c
71 changed files with 1754 additions and 619 deletions

View File

@@ -1,12 +1,12 @@
package dsp.afc;
import sample.Listener;
import sample.simplex.SimplexSampleListener;
import source.tuner.FrequencyChangeEvent;
import source.tuner.FrequencyChangeListener;
import source.tuner.FrequencyChangeEvent.Attribute;
import source.tuner.FrequencyChangeListener;
import buffer.FloatAveragingBuffer;
public class AutomaticFrequencyControl implements Listener<Float>,
public class AutomaticFrequencyControl implements SimplexSampleListener,
FrequencyChangeListener
{
private static float LARGE_ERROR = 10000.0f;
@@ -65,11 +65,11 @@ public class AutomaticFrequencyControl implements Listener<Float>,
}
@Override
public void receive( Float sample )
public void receive( float sample )
{
if( mMode == Mode.FAST )
{
float average = mBuffer.get( sample.floatValue() );
float average = mBuffer.get( sample );
mSampleCounter++;
@@ -88,7 +88,7 @@ public class AutomaticFrequencyControl implements Listener<Float>,
{
mSkipCounter = 0;
float average = mBuffer.get( sample.floatValue() );
float average = mBuffer.get( sample );
mSampleCounter++;

View File

@@ -1,15 +1,16 @@
package dsp.am;
import sample.Broadcaster;
import sample.Listener;
import sample.complex.ComplexSample;
import sample.simplex.SimplexSampleBroadcaster;
import sample.simplex.SimplexSampleListener;
import dsp.filter.ComplexFIRFilter;
import dsp.filter.FilterFactory;
import dsp.filter.Window.WindowType;
public class AMDemodulator implements Listener<ComplexSample>
{
private Broadcaster<Float> mBroadcaster = new Broadcaster<Float>();
private SimplexSampleBroadcaster mBroadcaster = new SimplexSampleBroadcaster();
private ComplexFIRFilter mIQFilter;
public AMDemodulator()
@@ -34,7 +35,7 @@ public class AMDemodulator implements Listener<ComplexSample>
/**
* Adds a listener to receive demodulated samples
*/
public void addListener( Listener<Float> listener )
public void addListener( SimplexSampleListener listener )
{
mBroadcaster.addListener( listener );
}
@@ -42,7 +43,7 @@ public class AMDemodulator implements Listener<ComplexSample>
/**
* Removes a listener from receiving demodulated samples
*/
public void removeListener( Listener<Float> listener )
public void removeListener( SimplexSampleListener listener )
{
mBroadcaster.removeListener( listener );
}

View File

@@ -0,0 +1,368 @@
package dsp.filter;
import java.util.ArrayList;
import java.util.List;
import sample.Broadcaster;
import sample.Listener;
import sample.complex.ComplexBuffer;
import sample.complex.ComplexSample;
import sample.complex.ComplexSampleListener;
import sample.decimator.ComplexDecimator;
import dsp.filter.Window.WindowType;
public class ComplexPrimeCICDecimate implements Listener<ComplexBuffer>
{
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;
public Broadcaster<ComplexSample> mBroadcaster =
new Broadcaster<ComplexSample>();
public Output mOutput;
/**
* Non-Recursive Prime-Factor CIC Filter with float sample array inputs and
* decimated, single paired i/q 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 ComplexPrimeCICDecimate( int decimation, int order )
{
assert( decimation <= 700 );
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 ).addListener( stage );
}
}
mOutput = new Output( 48000, stageSizes.size(), decimation );
mDecimatingStages.get( mDecimatingStages.size() - 1 )
.addListener( mOutput );
mOutput.setListener( mBroadcaster );
}
public void dispose()
{
for( DecimatingStage stage: mDecimatingStages )
{
stage.dispose();
}
mDecimatingStages.clear();
mBroadcaster.dispose();
mBroadcaster = null;
}
/**
* Adds a listener to receive the output of this CIC decimation filter
*/
public void addListener( Listener<ComplexSample> listener )
{
mBroadcaster.addListener( listener );
}
/**
* Removes listener from output of this CIC decimation filter
*/
public void removeListener( Listener<ComplexSample> listener )
{
mBroadcaster.removeListener( listener );
}
/**
* 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 sample arrays composed as I,Q,I,Q, etc.
*/
@Override
public void receive( ComplexBuffer sampleArray )
{
float[] samples = sampleArray.getSamples();
for( int x = 0; x < samples.length; x += 2 )
{
mFirstDecimatingStage.receive( samples[ x ], samples[ x + 1 ] );
}
samples = null;
}
/**
* 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 ComplexSampleListener
{
private ArrayList<Stage> mStages = new ArrayList<Stage>();
private Stage mFirstStage;
private ComplexDecimator mDecimator;
public DecimatingStage( int size, int order )
{
for( int x = 0; x < order; x++ )
{
Stage stage = new Stage( size );
mStages.add( stage );
if( x == 0 )
{
mFirstStage = stage;
}
else
{
mStages.get( x - 1 ).addListener( stage );
}
}
mDecimator = new ComplexDecimator( size );
mStages.get( mStages.size() - 1 ).addListener( 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 i, float q )
{
mFirstStage.receive( i, q );
}
public void addListener( ComplexSampleListener listener )
{
mDecimator.setListener( listener );
}
}
/**
* Single non-decimating CIC stage component. Uses a circular buffer
* internally to implement the stage so that stage size has essentially
* no impact on the performance of the stage
*/
public class Stage implements ComplexSampleListener
{
private ComplexSampleListener mListener;
private float[] mISamples;
private float[] mQSamples;
private float mISum;
private float mQSum;
private int mSamplePointer;
private int mSize;
public Stage( int size )
{
mSize = size;
mISamples = new float[ size ];
mQSamples = new float[ size ];
}
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;
/* Overwrite the oldest sample with the newest */
mISamples[ mSamplePointer ] = i;
mQSamples[ mSamplePointer ] = q;
mSamplePointer++;
if( mSamplePointer >= mSize )
{
mSamplePointer = 0;
}
if( mListener != null )
{
mListener.receive( mISum, mQSum );
}
}
public void addListener( ComplexSampleListener listener )
{
mListener = 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 ComplexSampleListener
{
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 ComplexFIRFilter( FilterFactory
.getCICCleanupFilter( outputSampleRate,
stageCount,
WindowType.BLACKMAN ), 1.0d );
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( float left, float right )
{
mCleanupFilter.receive(
new ComplexSample( (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<ComplexSample> listener )
{
mCleanupFilter.setListener( listener );
}
/**
* Removes the listener from receiving output samples
*/
public void removeListener( Listener<ComplexSample> listener )
{
mCleanupFilter.setListener( null );
}
public Listener<ComplexSample> 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;
}
}
}

View File

@@ -18,16 +18,17 @@
package dsp.filter;
import sample.Listener;
import sample.simplex.SimplexSampleListener;
/**
* Implements the real-time general DC removal algorithm described on page
* 762, figure 13-62d, in Understanding Digital Signal Processing 3e, Lyons.
*/
public class DCRemovalFilter implements Listener<Float>
public class DCRemovalFilter implements SimplexSampleListener
{
private double mAlpha;
private float mPrevious;
private Listener<Float> mListener;
private SimplexSampleListener mListener;
public DCRemovalFilter( double alpha )
{
@@ -40,7 +41,7 @@ public class DCRemovalFilter implements Listener<Float>
}
@Override
public void receive( Float sample )
public void receive( float sample )
{
send( (float)( sample + ( mPrevious * mAlpha ) - mPrevious ) );
@@ -58,7 +59,7 @@ public class DCRemovalFilter implements Listener<Float>
}
}
public void setListener( Listener<Float> listener )
public void setListener( SimplexSampleListener listener )
{
mListener = listener;
}

View File

@@ -18,12 +18,13 @@
package dsp.filter;
import sample.Listener;
import sample.simplex.SimplexSampleListener;
public class DCRemovalFilter2 implements Listener<Float>
public class DCRemovalFilter2 implements SimplexSampleListener
{
private double mAverage;
private double mRatio;
private Listener<Float> mListener;
private SimplexSampleListener mListener;
public DCRemovalFilter2( double ratio )
{
@@ -31,7 +32,7 @@ public class DCRemovalFilter2 implements Listener<Float>
}
@Override
public void receive( Float sample )
public void receive( float sample )
{
mAverage += mRatio * ( (double)sample - mAverage );
@@ -49,7 +50,7 @@ public class DCRemovalFilter2 implements Listener<Float>
}
}
public void setListener( Listener<Float> listener )
public void setListener( SimplexSampleListener listener )
{
mListener = listener;
}

View File

@@ -1,14 +1,14 @@
package dsp.filter;
import sample.Listener;
import sample.simplex.SimplexSampleListener;
public class DCRemovalFilter3 implements Listener<Float>
public class DCRemovalFilter3 implements SimplexSampleListener
{
private float mAlpha;
private float mPreviousInput = 0.0f;
private float mPreviousOutput = 0.0f;
private Listener<Float> mListener;
private SimplexSampleListener mListener;
/**
* IIR single-pole DC removal filter, as described by J M de Freitas in
@@ -24,7 +24,7 @@ public class DCRemovalFilter3 implements Listener<Float>
mAlpha = alpha;
}
@Override
public void receive( Float currentInput )
public void receive( float currentInput )
{
float currentOutput = ( currentInput - mPreviousInput ) +
( mAlpha * mPreviousOutput );
@@ -38,7 +38,7 @@ public class DCRemovalFilter3 implements Listener<Float>
mPreviousOutput = currentOutput;
}
public void setListener( Listener<Float> listener )
public void setListener( SimplexSampleListener listener )
{
mListener = listener;
}

View File

@@ -17,8 +17,6 @@
******************************************************************************/
package dsp.filter;
import gui.SDRTrunk;
import java.util.Arrays;
import java.util.Set;
import java.util.TreeSet;

View File

@@ -19,11 +19,11 @@ package dsp.filter;
import java.util.ArrayList;
import sample.Listener;
import sample.simplex.SimplexSampleListener;
public class FloatFIRFilter implements Listener<Float>
public class FloatFIRFilter implements SimplexSampleListener
{
private Listener<Float> mListener;
private SimplexSampleListener mListener;
private ArrayList<Float> mBuffer;
private int mBufferSize = 1; //Temporary initial value
private int mBufferPointer = 0;
@@ -61,12 +61,12 @@ public class FloatFIRFilter implements Listener<Float>
return mCoefficients.length;
}
public void setListener( Listener<Float> listener )
public void setListener( SimplexSampleListener listener )
{
mListener = listener;
}
public void receive( Float newSample )
public void receive( float newSample )
{
send( get( newSample ) );
}

View File

@@ -19,11 +19,11 @@ package dsp.filter;
import java.util.ArrayList;
import sample.Listener;
import sample.simplex.SimplexSampleListener;
public class FloatHalfBandFilter implements Listener<Float>
public class FloatHalfBandFilter implements SimplexSampleListener
{
private Listener<Float> mListener;
private SimplexSampleListener mListener;
private ArrayList<Float> mBuffer;
private int mBufferSize = 1; //Temporary initial value
private int mBufferPointer = 0;
@@ -75,7 +75,7 @@ public class FloatHalfBandFilter implements Listener<Float>
* Calculate the filtered value by applying the coefficients against
* the complex samples in mBuffer
*/
public void receive( Float newSample )
public void receive( float newSample )
{
//Add the new sample to the buffer
mBuffer.set( mBufferPointer, newSample );
@@ -188,7 +188,7 @@ public class FloatHalfBandFilter implements Listener<Float>
/**
* Registers a listener for filtered samples
*/
public void setListener( Listener<Float> listener )
public void setListener( SimplexSampleListener listener )
{
mListener = listener;
}

View File

@@ -19,11 +19,11 @@ package dsp.filter;
import java.util.ArrayList;
import sample.Listener;
import sample.simplex.SimplexSampleListener;
public class FloatHalfBandNoDecimateFilter implements Listener<Float>
public class FloatHalfBandNoDecimateFilter implements SimplexSampleListener
{
private Listener<Float> mListener;
private SimplexSampleListener mListener;
private ArrayList<Float> mBuffer;
private int mBufferSize = 1; //Temporary initial value
private int mBufferPointer = 0;
@@ -73,7 +73,7 @@ public class FloatHalfBandNoDecimateFilter implements Listener<Float>
* Calculate the filtered value by applying the coefficients against
* the complex samples in mBuffer
*/
public void receive( Float newSample )
public void receive( float newSample )
{
//Add the new sample to the buffer
mBuffer.set( mBufferPointer, newSample );
@@ -178,7 +178,7 @@ public class FloatHalfBandNoDecimateFilter implements Listener<Float>
/**
* Registers a listener for filtered samples
*/
public void setListener( Listener<Float> listener )
public void setListener( SimplexSampleListener listener )
{
mListener = listener;
}

View File

@@ -18,8 +18,9 @@
package dsp.filter;
import sample.Listener;
import sample.simplex.SimplexSampleListener;
public class SquaringFilter implements Listener<Float>
public class SquaringFilter implements SimplexSampleListener
{
private Listener<Boolean> mListener;
@@ -31,7 +32,7 @@ public class SquaringFilter implements Listener<Float>
}
@Override
public void receive( Float sample )
public void receive( float sample )
{
if( mListener != null )
{

View File

@@ -7,9 +7,9 @@ import instrument.tap.stream.FloatTap;
import java.util.ArrayList;
import java.util.List;
import sample.Listener;
import sample.simplex.SimplexSampleListener;
public class C4FMDecoder implements Listener<Float>, Instrumentable
public class C4FMDecoder implements SimplexSampleListener, Instrumentable
{
private final String TAP_C4FM_INPUT = "C4FM Input";
private ArrayList<Tap> mTaps;
@@ -56,7 +56,7 @@ public class C4FMDecoder implements Listener<Float>, Instrumentable
}
@Override
public void receive( Float t )
public void receive( float t )
{
if( mInputTap != null )
{

View File

@@ -26,6 +26,7 @@ import java.util.BitSet;
import java.util.List;
import sample.Listener;
import sample.simplex.SimplexSampleListener;
import buffer.BooleanAveragingBuffer;
import dsp.fsk.SymbolEvent.Shift;
@@ -49,7 +50,7 @@ import dsp.fsk.SymbolEvent.Shift;
* Implements instrumentable interface, so that slice events can be received
* externally to analyze decoder performance.
*/
public class FSK2Decoder implements Instrumentable, Listener<Float>
public class FSK2Decoder implements Instrumentable, SimplexSampleListener
{
public enum Output{ NORMAL, INVERTED };
@@ -93,7 +94,7 @@ public class FSK2Decoder implements Instrumentable, Listener<Float>
* Primary sample input
*/
@Override
public void receive( Float floatSample )
public void receive( float floatSample )
{
/* Square the sample. Greater than zero is a 1 (true) and less than
* zero is a 0 (false) */

View File

@@ -27,6 +27,7 @@ import java.util.ArrayList;
import java.util.List;
import sample.Listener;
import sample.simplex.SimplexSampleListener;
import dsp.Slicer;
import dsp.Slicer.Output;
import dsp.filter.DCRemovalFilter3;
@@ -36,7 +37,7 @@ import dsp.filter.FloatHalfBandNoDecimateFilter;
import dsp.filter.LTRPulseShapingFilter;
import dsp.filter.SquaringFilter;
public class LTRFSKDecoder implements Listener<Float>, Instrumentable
public class LTRFSKDecoder implements SimplexSampleListener, Instrumentable
{
private FloatHalfBandFilter mHBFilter1;
private FloatHalfBandFilter mHBFilter2;
@@ -104,7 +105,7 @@ public class LTRFSKDecoder implements Listener<Float>, Instrumentable
}
@Override
public void receive( Float sample )
public void receive( float sample )
{
mHBFilter1.receive( sample );
}

View File

@@ -19,10 +19,11 @@ package dsp.nbfm;
import sample.Listener;
import sample.complex.ComplexSample;
import sample.simplex.SimplexSampleListener;
public class FMDiscriminator implements Listener<ComplexSample>
{
private Listener<Float> mListener;
private SimplexSampleListener mListener;
private ComplexSample mPreviousSample = new ComplexSample( 0.0f, 0.0f );
private double mGain;
@@ -93,7 +94,7 @@ public class FMDiscriminator implements Listener<ComplexSample>
mPreviousSample = currentSample;
}
public void setListener( Listener<Float> listener )
public void setListener( SimplexSampleListener listener )
{
mListener = listener;
}

View File

@@ -17,9 +17,10 @@
******************************************************************************/
package dsp.nbfm;
import sample.Broadcaster;
import sample.Listener;
import sample.complex.ComplexSample;
import sample.simplex.SimplexSampleBroadcaster;
import sample.simplex.SimplexSampleListener;
import dsp.filter.ComplexFIRFilter;
import dsp.filter.FilterFactory;
import dsp.filter.FloatFIRFilter;
@@ -27,7 +28,7 @@ import dsp.filter.Window.WindowType;
public class FilteringNBFMDemodulator implements Listener<ComplexSample>
{
private Broadcaster<Float> mBroadcaster = new Broadcaster<Float>();
private SimplexSampleBroadcaster mBroadcaster = new SimplexSampleBroadcaster();
private ComplexFIRFilter mIQFilter;
private FloatFIRFilter mAudioFilter;
private FMDiscriminator mDiscriminator;
@@ -77,7 +78,7 @@ public class FilteringNBFMDemodulator implements Listener<ComplexSample>
/**
* Adds a listener to receive demodulated samples
*/
public void addListener( Listener<Float> listener )
public void addListener( SimplexSampleListener listener )
{
mBroadcaster.addListener( listener );
}
@@ -85,7 +86,7 @@ public class FilteringNBFMDemodulator implements Listener<ComplexSample>
/**
* Removes a listener from receiving demodulated samples
*/
public void removeListener( Listener<Float> listener )
public void removeListener( SimplexSampleListener listener )
{
mBroadcaster.removeListener( listener );
}

View File

@@ -17,16 +17,17 @@
******************************************************************************/
package dsp.nbfm;
import sample.Broadcaster;
import sample.Listener;
import sample.complex.ComplexSample;
import sample.simplex.SimplexSampleBroadcaster;
import sample.simplex.SimplexSampleListener;
import dsp.filter.ComplexFIRFilter;
import dsp.filter.FilterFactory;
import dsp.filter.Window.WindowType;
public class NBFMDemodulator implements Listener<ComplexSample>
{
private Broadcaster<Float> mBroadcaster = new Broadcaster<Float>();
private SimplexSampleBroadcaster mBroadcaster = new SimplexSampleBroadcaster();
private ComplexFIRFilter mIQFilter;
private FMDiscriminator mDiscriminator;
@@ -77,7 +78,7 @@ public class NBFMDemodulator implements Listener<ComplexSample>
/**
* Adds a listener to receive demodulated samples
*/
public void addListener( Listener<Float> listener )
public void addListener( SimplexSampleListener listener )
{
mBroadcaster.addListener( listener );
}
@@ -85,7 +86,7 @@ public class NBFMDemodulator implements Listener<ComplexSample>
/**
* Removes a listener from receiving demodulated samples
*/
public void removeListener( Listener<Float> listener )
public void removeListener( SimplexSampleListener listener )
{
mBroadcaster.removeListener( listener );
}