diff --git a/src/decode/p25/P25Decoder.java b/src/decode/p25/P25Decoder.java index 89ebb57f..7c738b51 100644 --- a/src/decode/p25/P25Decoder.java +++ b/src/decode/p25/P25Decoder.java @@ -114,16 +114,12 @@ public class P25Decoder extends Decoder FrameSync.P25_PHASE1.getSync(), 64, false, mAliasList ); mNormalFramer.setListener( mMessageProcessor ); -// mInvertedFramer = new P25MessageFramer( -// FrameSync.P25_PHASE1_INVERTED.getSync(), 64, true, mAliasList ); -// mInvertedFramer.setListener( mMessageProcessor ); - /* Setup demodulation chains based on sample type (real or complex) and * modulation (C4FM or CQPSK) */ if( mSourceSampleType == SampleType.COMPLEX ) { mBasebandFilter = new ComplexFIRFilter( FilterFactory.getLowPass( - 48000, 6500, 7500, 48, WindowType.HANNING, true ), 4.0 ); + 48000, 7000, 7500, 60, WindowType.HAMMING, true ), 1.0 ); if( modulation == Modulation.CQPSK ) { @@ -139,7 +135,6 @@ public class P25Decoder extends Decoder mCQPSKDemodulator.setListener( mCQPSKSlicer ); mCQPSKSlicer.addListener( mNormalFramer ); -// mCQPSKSlicer.addListener( mInvertedFramer ); } else /* C4FM */ { @@ -172,7 +167,6 @@ public class P25Decoder extends Decoder mSymbolFilter.setListener( mC4FMSlicer ); mC4FMSlicer.addListener( mNormalFramer ); -// mC4FMSlicer.addListener( mInvertedFramer ); } mAudioOutput = new P25AudioOutput( resourceManager ); diff --git a/src/dsp/filter/ComplexCICDecimate.java b/src/dsp/filter/ComplexCICDecimate.java index 7367f87b..8461d0e5 100644 --- a/src/dsp/filter/ComplexCICDecimate.java +++ b/src/dsp/filter/ComplexCICDecimate.java @@ -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 diff --git a/src/dsp/filter/ComplexPrimeCICDecimate.java b/src/dsp/filter/ComplexPrimeCICDecimate.java index b6c08f33..b57a2710 100644 --- a/src/dsp/filter/ComplexPrimeCICDecimate.java +++ b/src/dsp/filter/ComplexPrimeCICDecimate.java @@ -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 +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 new ArrayList(); private DecimatingStage mFirstDecimatingStage; - - public Broadcaster mBroadcaster = - new Broadcaster(); - public Output mOutput; + private Output mOutput; + + private Listener 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 * @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 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 } mDecimatingStages.clear(); - - mBroadcaster.dispose(); - - mBroadcaster = null; } /** * Adds a listener to receive the output of this CIC decimation filter */ - public void addListener( Listener listener ) + public void setListener( Listener listener ) { - mBroadcaster.addListener( listener ); + mOutput.setListener( listener ); } - + /** * Removes listener from output of this CIC decimation filter */ public void removeListener( Listener listener ) { - mBroadcaster.removeListener( listener ); + mOutput.removeListener( listener ); } /** @@ -144,11 +145,8 @@ public class ComplexPrimeCICDecimate implements Listener /** * 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 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 */ 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 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; } } - } diff --git a/src/dsp/filter/FilterFactory.java b/src/dsp/filter/FilterFactory.java index f3d1f10b..edaf62b2 100644 --- a/src/dsp/filter/FilterFactory.java +++ b/src/dsp/filter/FilterFactory.java @@ -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 ) ); + } } diff --git a/src/dsp/nbfm/FilteringNBFMDemodulator.java b/src/dsp/nbfm/FilteringNBFMDemodulator.java index e20063ed..5644c1be 100644 --- a/src/dsp/nbfm/FilteringNBFMDemodulator.java +++ b/src/dsp/nbfm/FilteringNBFMDemodulator.java @@ -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 { + private final static Logger mLog = + LoggerFactory.getLogger( FilteringNBFMDemodulator.class ); + private RealSampleBroadcaster mBroadcaster = new RealSampleBroadcaster(); private ComplexFIRFilter mIQFilter; private FloatFIRFilter mAudioFilter; diff --git a/src/source/tuner/TunerChannelSource.java b/src/source/tuner/TunerChannelSource.java index 9261b976..03019355 100644 --- a/src/source/tuner/TunerChannelSource.java +++ b/src/source/tuner/TunerChannelSource.java @@ -37,8 +37,7 @@ import source.tuner.FrequencyChangeEvent.Attribute; import util.Oscillator; import controller.ThreadPoolManager; import controller.ThreadPoolManager.ThreadType; -import decode.p25.P25Decoder; -import dsp.filter.ComplexFilter; +import dsp.filter.ComplexPrimeCICDecimate; import dsp.filter.FilterFactory; import dsp.filter.Window.WindowType; @@ -49,15 +48,15 @@ public class TunerChannelSource extends ComplexSource private final static Logger mLog = LoggerFactory.getLogger( TunerChannelSource.class ); - private static int sCHANNEL_RATE = 48000; - private static int sCHANNEL_PASS_FREQUENCY = 12500; + private static int CHANNEL_RATE = 48000; + private static int CHANNEL_PASS_FREQUENCY = 12000; private LinkedTransferQueue mBuffer = new LinkedTransferQueue(); private Tuner mTuner; private TunerChannel mTunerChannel; private Oscillator mSineWaveGenerator; - private ComplexFilter[] mDecimationFilters; + private ComplexPrimeCICDecimate mDecimationFilter; private ComplexSampleAssembler mBufferAssembler = new ComplexSampleAssembler( 3000 ); @@ -71,7 +70,7 @@ public class TunerChannelSource extends ComplexSource public TunerChannelSource( ThreadPoolManager threadPoolManager, Tuner tuner, TunerChannel tunerChannel ) - throws RejectedExecutionException, SourceException + throws RejectedExecutionException, SourceException { super( "Tuner Channel Source" ); @@ -118,11 +117,8 @@ public class TunerChannelSource extends ComplexSource mBuffer.clear(); mBuffer = null; - - for( ComplexFilter filter: mDecimationFilters ) - { - filter.dispose(); - } + + mDecimationFilter.dispose(); mBufferAssembler.dispose(); @@ -160,8 +156,6 @@ public class TunerChannelSource extends ComplexSource public void removeListener( Listener> listener ) { mBufferAssembler.removeListener( listener ); - mDecimationFilters[ mDecimationFilters.length - 1 ] - .setListener( null ); } @Override @@ -184,28 +178,18 @@ public class TunerChannelSource extends ComplexSource { /* Set the oscillator to the new frequency */ mSineWaveGenerator.setSampleRate( sampleRate ); - - /* Get new decimation filters */ - mDecimationFilters = FilterFactory - .getDecimationFilters( sampleRate, - sCHANNEL_RATE, - sCHANNEL_PASS_FREQUENCY, - 48, //dB attenuation - WindowType.HAMMING ); - - /* wire the filters together */ - if( mDecimationFilters.length > 1 ) - { - for( int x = 1; x < mDecimationFilters.length; x++ ) - { - mDecimationFilters[ x - 1 ] - .setListener( mDecimationFilters[ x ] ); - } - } + + /* Get new decimation filter */ + mDecimationFilter = FilterFactory + .getDecimationFilter( sampleRate, + CHANNEL_RATE, + 2, //Order + CHANNEL_PASS_FREQUENCY, + 60, //dB attenuation + WindowType.HAMMING ); /* re-add the original output listener */ - mDecimationFilters[ mDecimationFilters.length - 1 ] - .setListener( mBufferAssembler ); + mDecimationFilter.setListener( mBufferAssembler ); mTunerSampleRate = sampleRate; } @@ -265,28 +249,25 @@ public class TunerChannelSource extends ComplexSource * and process them, causing delays and buffer refill */ mBuffer.drainTo( sampleBuffers, 4 ); - for( ComplexBuffer sampleArray: sampleBuffers ) + for( ComplexBuffer buffer: sampleBuffers ) { - float[] samples = sampleArray.getSamples(); - + float[] samples = buffer.getSamples(); + float[] translated = new float[ samples.length ]; + + /* Perform frequency translation */ for( int x = 0; x < samples.length; x += 2 ) { - Float left = samples[ x ]; - Float right = samples[ x + 1 ]; + ComplexSample multiplier = + mSineWaveGenerator.nextComplex(); - if( left != null && right != null ) - { - /* perform frequency translation */ - ComplexSample translated = ComplexSample.multiply( - mSineWaveGenerator.nextComplex(), left, right ); - - /* decimate the sample rate */ - if( mDecimationFilters[ 0 ] != null ) - { - mDecimationFilters[ 0 ].receive( translated ); - } - } + translated[ x ] = ( samples[ x ] * multiplier.inphase() ) - + ( samples[ x + 1 ] * multiplier.quadrature() ); + + translated[ x + 1 ] = ( samples[ x + 1 ] * multiplier.inphase() ) + + ( samples[ x ] * multiplier.quadrature() ); } + + mDecimationFilter.receive( translated ); } sampleBuffers.clear();