Refactoring to use COMPLEX and REAL sample terms throughout

This commit is contained in:
dennis.sheirer
2014-10-31 13:58:20 +00:00
parent 34268f32b3
commit d34abdd2fa
50 changed files with 530 additions and 239 deletions

View File

@@ -1,12 +1,12 @@
package dsp.afc;
import sample.simplex.SimplexSampleListener;
import sample.real.RealSampleListener;
import source.tuner.FrequencyChangeEvent;
import source.tuner.FrequencyChangeEvent.Attribute;
import source.tuner.FrequencyChangeListener;
import buffer.FloatAveragingBuffer;
public class AutomaticFrequencyControl implements SimplexSampleListener,
public class AutomaticFrequencyControl implements RealSampleListener,
FrequencyChangeListener
{
private static float LARGE_ERROR = 10000.0f;

View File

@@ -0,0 +1,198 @@
package dsp.agc;
import java.util.concurrent.atomic.AtomicBoolean;
import sample.Listener;
import sample.Provider;
import sample.complex.ComplexSample;
import buffer.ComplexCircularBuffer;
import buffer.DoubleCircularBuffer;
/**
* Automatic Gain Control (AGC) for complex samples at 48000 sample rate.
*/
public class ComplexAutomaticGainControl implements Listener<ComplexSample>,
Provider<ComplexSample>
{
private static final double SAMPLE_RATE = 48000;
/* Signal delay line - time delay in seconds */
private static final double DELAY_TIME_CONSTANT = 0.015;
/* Peak detector window - time delay in seconds */
private static final double WINDOW_TIME_CONSTANT = 0.018;
/* Attack time constants in seconds */
private static final double ATTACK_RISE_TIME_CONSTANT = 0.002;
private static final double ATTACK_FALL_TIME_CONSTANT = 0.005;
private static final double ATTACK_RISE_ALPHA = 1.0 -
Math.exp( -1.0 / SAMPLE_RATE * ATTACK_RISE_TIME_CONSTANT );
private static final double ATTACK_FALL_ALPHA = 1.0 -
Math.exp( -1.0 / SAMPLE_RATE * ATTACK_FALL_TIME_CONSTANT );
/* AGC decay value in milliseconds (20 to 5000) */
private static final double DECAY = 200;
/* Ratio between rise and fall times of decay time constants - adjust for
* best action with SSB */
private static final double DECAY_RISEFALL_RATIO = 0.3;
private static final double DECAY_RISE_ALPHA = 1.0 -
Math.exp( -1.0 / ( SAMPLE_RATE * DECAY * .001 * DECAY_RISEFALL_RATIO ) );
private static final double DECAY_FALL_ALPHA = 1.0 -
Math.exp( -1.0 / ( SAMPLE_RATE * DECAY * .001 ) );
/* Hang timer release decay time constant in seconds */
private static final double RELEASE_TIME_CONSTANT = 0.05;
/* Specifies the AGC Knee in dB if AGC is active (nominal range -160 to 0 dB) */
private static final double THRESHOLD = -100;
/* Limit output to about 3db of maximum */
private static final double AGC_OUT_SCALE = 0.7;
/* Keep max input and output the same */
private static final double MAX_AMPLITUDE = 32767.0; //1.0;
private static final double MAX_MANUAL_AMPLITUDE = 32767.0; //1.0;
/* Specifies AGC manual gain in dB if AGC is not active ( 0 to 100 dB) */
private static final double MANUAL_GAIN = 0.0;
private static final double MANUAL_AGC_GAIN = MAX_MANUAL_AMPLITUDE *
Math.pow( 10.0, MANUAL_GAIN / 20.0 );
/* Specifies dB reduction in output at knee from max output level (0 - 10dB) */
private static final double SLOPE_FACTOR = 2.0;
private static final double KNEE = THRESHOLD / 20.0;
private static final double GAIN_SLOPE = SLOPE_FACTOR / 100.0;
private static final double FIXED_GAIN = AGC_OUT_SCALE *
Math.pow( 10.0, KNEE * ( GAIN_SLOPE - 1.0 ) );
/* Constant for calc log() so that a value of 0 magnitude = -8 */
private static final double MIN_CONSTANT = 3.2767E-4;
private AtomicBoolean mAGCEnabled = new AtomicBoolean( true );
private double mPeakMagnitude = 0.0;
private double mAttackAverage = 0.0;
private double mDecayAverage = 0.0;
private ComplexCircularBuffer mDelayBuffer =
new ComplexCircularBuffer( (int)( SAMPLE_RATE * DELAY_TIME_CONSTANT ) );
private DoubleCircularBuffer mMagnitudeBuffer =
new DoubleCircularBuffer( (int)( SAMPLE_RATE * WINDOW_TIME_CONSTANT ) );
private Listener<ComplexSample> mListener;
public ComplexAutomaticGainControl()
{
}
@Override
public void receive( ComplexSample currentSample )
{
ComplexSample delayedSample = mDelayBuffer.get( currentSample );
double gain = MANUAL_AGC_GAIN;
if( mAGCEnabled.get() )
{
float max = currentSample.maximumAbsolute();
double currentMagnitude = Math.log10( max + MIN_CONSTANT ) -
Math.log10( MAX_AMPLITUDE );
double delayedMagnitude = mMagnitudeBuffer.get( currentMagnitude );
if( currentMagnitude > mPeakMagnitude )
{
/* Use current magnitude as peak if it's larger */
mPeakMagnitude = currentMagnitude;
}
else if( delayedMagnitude == mPeakMagnitude )
{
/* If delayed magnitude is the current peak, then find a new peak */
mPeakMagnitude = mMagnitudeBuffer.max();
}
/* Exponential decay mode */
if( mPeakMagnitude > mAttackAverage )
{
mAttackAverage = ( ( 1.0 - ATTACK_RISE_ALPHA ) * mAttackAverage ) +
( ATTACK_RISE_ALPHA * mPeakMagnitude );
}
else
{
mAttackAverage = ( ( 1.0 - ATTACK_FALL_ALPHA ) * mAttackAverage ) +
( ATTACK_FALL_ALPHA * mPeakMagnitude );
}
if( mPeakMagnitude > mDecayAverage )
{
mDecayAverage = ( ( 1.0 - DECAY_RISE_ALPHA ) * mDecayAverage ) +
( DECAY_RISE_ALPHA * mPeakMagnitude );
}
else
{
mDecayAverage = ( ( 1.0 - DECAY_FALL_ALPHA ) * mDecayAverage ) +
( DECAY_RISE_ALPHA * mPeakMagnitude );
}
double magnitude = ( mAttackAverage > mDecayAverage ) ?
mAttackAverage : mDecayAverage;
if( magnitude < KNEE )
{
gain = FIXED_GAIN;
}
else
{
gain = AGC_OUT_SCALE * Math.pow( 10.0,
magnitude * ( GAIN_SLOPE - 1.0 ) );
}
}
delayedSample.multiply( (float)gain );
if( mListener != null )
{
mListener.receive( delayedSample );
}
}
/**
* Enables or disables Automatic Gain Control (AGC).
*/
public void setAGCEnabled( boolean enabled )
{
mAGCEnabled.set( enabled );
}
/**
* Indicates if AGC is enabled
* @return true=AGC, false=MANUAL GAIN
*/
public boolean isAGCEnabled()
{
return mAGCEnabled.get();
}
@Override
public void setListener( Listener<ComplexSample> listener )
{
mListener = listener;
}
@Override
public void removeListener( Listener<ComplexSample> listener )
{
mListener = null;
}
}

View File

@@ -18,17 +18,17 @@
package dsp.filter;
import sample.Listener;
import sample.simplex.SimplexSampleListener;
import sample.real.RealSampleListener;
/**
* 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 SimplexSampleListener
public class DCRemovalFilter implements RealSampleListener
{
private double mAlpha;
private float mPrevious;
private SimplexSampleListener mListener;
private RealSampleListener mListener;
public DCRemovalFilter( double alpha )
{
@@ -59,7 +59,7 @@ public class DCRemovalFilter implements SimplexSampleListener
}
}
public void setListener( SimplexSampleListener listener )
public void setListener( RealSampleListener listener )
{
mListener = listener;
}

View File

@@ -18,13 +18,13 @@
package dsp.filter;
import sample.Listener;
import sample.simplex.SimplexSampleListener;
import sample.real.RealSampleListener;
public class DCRemovalFilter2 implements SimplexSampleListener
public class DCRemovalFilter2 implements RealSampleListener
{
private double mAverage;
private double mRatio;
private SimplexSampleListener mListener;
private RealSampleListener mListener;
public DCRemovalFilter2( double ratio )
{
@@ -50,7 +50,7 @@ public class DCRemovalFilter2 implements SimplexSampleListener
}
}
public void setListener( SimplexSampleListener listener )
public void setListener( RealSampleListener listener )
{
mListener = listener;
}

View File

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

View File

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

View File

@@ -19,11 +19,11 @@ package dsp.filter;
import java.util.ArrayList;
import sample.simplex.SimplexSampleListener;
import sample.real.RealSampleListener;
public class FloatHalfBandFilter implements SimplexSampleListener
public class FloatHalfBandFilter implements RealSampleListener
{
private SimplexSampleListener mListener;
private RealSampleListener mListener;
private ArrayList<Float> mBuffer;
private int mBufferSize = 1; //Temporary initial value
private int mBufferPointer = 0;
@@ -188,7 +188,7 @@ public class FloatHalfBandFilter implements SimplexSampleListener
/**
* Registers a listener for filtered samples
*/
public void setListener( SimplexSampleListener listener )
public void setListener( RealSampleListener listener )
{
mListener = listener;
}

View File

@@ -19,11 +19,11 @@ package dsp.filter;
import java.util.ArrayList;
import sample.simplex.SimplexSampleListener;
import sample.real.RealSampleListener;
public class FloatHalfBandNoDecimateFilter implements SimplexSampleListener
public class FloatHalfBandNoDecimateFilter implements RealSampleListener
{
private SimplexSampleListener mListener;
private RealSampleListener mListener;
private ArrayList<Float> mBuffer;
private int mBufferSize = 1; //Temporary initial value
private int mBufferPointer = 0;
@@ -178,7 +178,7 @@ public class FloatHalfBandNoDecimateFilter implements SimplexSampleListener
/**
* Registers a listener for filtered samples
*/
public void setListener( SimplexSampleListener listener )
public void setListener( RealSampleListener listener )
{
mListener = listener;
}

View File

@@ -18,9 +18,9 @@
package dsp.filter;
import sample.Listener;
import sample.simplex.SimplexSampleListener;
import sample.real.RealSampleListener;
public class SquaringFilter implements SimplexSampleListener
public class SquaringFilter implements RealSampleListener
{
private Listener<Boolean> mListener;

View File

@@ -7,9 +7,9 @@ import instrument.tap.stream.FloatTap;
import java.util.ArrayList;
import java.util.List;
import sample.simplex.SimplexSampleListener;
import sample.real.RealSampleListener;
public class C4FMDecoder implements SimplexSampleListener, Instrumentable
public class C4FMDecoder implements RealSampleListener, Instrumentable
{
private final String TAP_C4FM_INPUT = "C4FM Input";
private ArrayList<Tap> mTaps;

View File

@@ -26,7 +26,7 @@ import java.util.BitSet;
import java.util.List;
import sample.Listener;
import sample.simplex.SimplexSampleListener;
import sample.real.RealSampleListener;
import buffer.BooleanAveragingBuffer;
import dsp.fsk.SymbolEvent.Shift;
@@ -50,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, SimplexSampleListener
public class FSK2Decoder implements Instrumentable, RealSampleListener
{
public enum Output{ NORMAL, INVERTED };

View File

@@ -27,7 +27,7 @@ import java.util.ArrayList;
import java.util.List;
import sample.Listener;
import sample.simplex.SimplexSampleListener;
import sample.real.RealSampleListener;
import dsp.Slicer;
import dsp.Slicer.Output;
import dsp.filter.DCRemovalFilter3;
@@ -37,7 +37,7 @@ import dsp.filter.FloatHalfBandNoDecimateFilter;
import dsp.filter.LTRPulseShapingFilter;
import dsp.filter.SquaringFilter;
public class LTRFSKDecoder implements SimplexSampleListener, Instrumentable
public class LTRFSKDecoder implements RealSampleListener, Instrumentable
{
private FloatHalfBandFilter mHBFilter1;
private FloatHalfBandFilter mHBFilter2;

View File

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

View File

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

View File

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