diff --git a/src/controller/channel/Channel.java b/src/controller/channel/Channel.java index 1927dfd1..3cc95f81 100644 --- a/src/controller/channel/Channel.java +++ b/src/controller/channel/Channel.java @@ -59,7 +59,7 @@ import controller.system.System; import decode.config.AuxDecodeConfiguration; import decode.config.DecodeConfigFactory; import decode.config.DecodeConfiguration; -import dsp.afc.AutomaticFrequencyControl; +import dsp.frequency.AutomaticFrequencyControl; import eventlog.config.EventLogConfiguration; @XmlSeeAlso( { Configuration.class } ) diff --git a/src/controller/channel/ProcessingChain.java b/src/controller/channel/ProcessingChain.java index 8e15a605..ee577053 100644 --- a/src/controller/channel/ProcessingChain.java +++ b/src/controller/channel/ProcessingChain.java @@ -62,7 +62,7 @@ import controller.state.ChannelState; import controller.state.ChannelState.State; import decode.Decoder; import decode.DecoderFactory; -import dsp.afc.AutomaticFrequencyControl; +import dsp.frequency.AutomaticFrequencyControl; import eventlog.EventLogType; import eventlog.EventLogger; diff --git a/src/decode/DecoderType.java b/src/decode/DecoderType.java index 92a69710..98217436 100644 --- a/src/decode/DecoderType.java +++ b/src/decode/DecoderType.java @@ -68,7 +68,7 @@ public enum DecoderType DecoderType.LTR_STANDARD, DecoderType.MPT1327, DecoderType.NBFM, -// DecoderType.P25_PHASE1, + DecoderType.P25_PHASE1, DecoderType.PASSPORT ); } diff --git a/src/decode/am/AMDecoder.java b/src/decode/am/AMDecoder.java index 15ac40c3..f329d9ca 100644 --- a/src/decode/am/AMDecoder.java +++ b/src/decode/am/AMDecoder.java @@ -4,14 +4,14 @@ import sample.real.RealSampleListener; import source.Source.SampleType; import decode.Decoder; import decode.DecoderType; -import dsp.agc.ComplexAutomaticGainControl; -import dsp.agc.RealAutomaticGainControl; import dsp.am.AMDemodulator; import dsp.filter.ComplexFIRFilter; import dsp.filter.DCRemovalFilter2; import dsp.filter.FilterFactory; import dsp.filter.FloatFIRFilter; import dsp.filter.Window.WindowType; +import dsp.gain.ComplexAutomaticGainControl; +import dsp.gain.RealAutomaticGainControl; public class AMDecoder extends Decoder { diff --git a/src/decode/p25/P25Decoder.java b/src/decode/p25/P25Decoder.java index 1277b7cb..df883f86 100644 --- a/src/decode/p25/P25Decoder.java +++ b/src/decode/p25/P25Decoder.java @@ -35,13 +35,13 @@ import source.tuner.FrequencyChangeListener; import alias.AliasList; import decode.Decoder; import decode.DecoderType; -import dsp.agc.RealAutomaticGainControl; import dsp.filter.ComplexFIRFilter; import dsp.filter.FilterFactory; import dsp.filter.Window.WindowType; import dsp.fsk.C4FMSlicer; import dsp.fsk.C4FMSymbolFilter; import dsp.fsk.P25MessageFramer; +import dsp.gain.DirectGainControl; import dsp.nbfm.FMDiscriminator; public class P25Decoder extends Decoder @@ -60,8 +60,9 @@ public class P25Decoder extends Decoder FilterFactory.getLowPass( 48000, 5000, 31, WindowType.HAMMING ), 1.0 ); private FMDiscriminator mDemodulator = new FMDiscriminator( 1 ); - private RealAutomaticGainControl mAGC = new RealAutomaticGainControl(); - private C4FMSymbolFilter mSymbolFilter = new C4FMSymbolFilter(); + private DirectGainControl mGainControl = + new DirectGainControl( 15.0f, 0.1f, 25.0f, 0.3f ); + private C4FMSymbolFilter mSymbolFilter; private C4FMSlicer mSlicer = new C4FMSlicer(); private P25MessageFramer mNormalFramer; private P25MessageFramer mInvertedFramer; @@ -74,6 +75,8 @@ public class P25Decoder extends Decoder super( sampleType ); mAliasList = aliasList; + + mSymbolFilter = new C4FMSymbolFilter( mGainControl ); /** * Only setup a demod chain if we're receiving complex samples. If @@ -94,8 +97,8 @@ public class P25Decoder extends Decoder mDemodulator.setListener( getRealReceiver() ); } - addRealSampleListener( mAGC ); - mAGC.setListener( mSymbolFilter ); + addRealSampleListener( mGainControl ); + mGainControl.setListener( mSymbolFilter ); mSymbolFilter.setListener( mSlicer ); @@ -147,13 +150,13 @@ public class P25Decoder extends Decoder { case INSTRUMENT_INPUT_TO_AGC: FloatTap inputAGC = (FloatTap)tap; - removeRealListener( mAGC ); + removeRealListener( mGainControl ); addRealSampleListener( inputAGC ); - inputAGC.setListener( mAGC ); + inputAGC.setListener( mGainControl ); break; case INSTRUMENT_AGC_TO_SYMBOL_FILTER: FloatTap agcSymbol = (FloatTap)tap; - mAGC.setListener( agcSymbol ); + mGainControl.setListener( agcSymbol ); agcSymbol.setListener( mSymbolFilter ); break; case INSTRUMENT_SYMBOL_FILTER_TO_SLICER: @@ -178,10 +181,10 @@ public class P25Decoder extends Decoder case INSTRUMENT_INPUT_TO_AGC: FloatTap inputAGC = (FloatTap)tap; removeRealListener( inputAGC ); - addRealSampleListener( mAGC ); + addRealSampleListener( mGainControl ); break; case INSTRUMENT_AGC_TO_SYMBOL_FILTER: - mAGC.setListener( mSymbolFilter ); + mGainControl.setListener( mSymbolFilter ); break; case INSTRUMENT_SYMBOL_FILTER_TO_SLICER: mSymbolFilter.setListener( mSlicer ); diff --git a/src/dsp/filter/Gain.java b/src/dsp/filter/Gain.java deleted file mode 100644 index e73c17eb..00000000 --- a/src/dsp/filter/Gain.java +++ /dev/null @@ -1,45 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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; - -import sample.Listener; - -public class Gain implements Listener -{ - private double mGain; - private Listener mListener; - - public Gain( double gain ) - { - mGain = gain; - } - - @Override - public void receive( Float sample ) - { - if( mListener != null ) - { - mListener.receive( (float)( sample * mGain ) ); - } - } - - public void setListener( Listener listener ) - { - mListener = listener; - } -} diff --git a/src/dsp/afc/AutomaticFrequencyControl.java b/src/dsp/frequency/AutomaticFrequencyControl.java similarity index 99% rename from src/dsp/afc/AutomaticFrequencyControl.java rename to src/dsp/frequency/AutomaticFrequencyControl.java index 5f57544a..ee634c8a 100644 --- a/src/dsp/afc/AutomaticFrequencyControl.java +++ b/src/dsp/frequency/AutomaticFrequencyControl.java @@ -1,4 +1,4 @@ -package dsp.afc; +package dsp.frequency; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/dsp/fsk/C4FMDecoder.java b/src/dsp/fsk/C4FMDecoder.java deleted file mode 100644 index e7a11af3..00000000 --- a/src/dsp/fsk/C4FMDecoder.java +++ /dev/null @@ -1,85 +0,0 @@ -package dsp.fsk; - -import instrument.Instrumentable; -import instrument.tap.Tap; -import instrument.tap.stream.FloatTap; - -import java.util.ArrayList; -import java.util.List; - -import sample.real.RealSampleListener; - -public class C4FMDecoder implements RealSampleListener, Instrumentable -{ - private static final String TAP_C4FM_INPUT = "C4FM Decoder Input"; - private static final String TAP_SYMBOL_FILTER_OUTPUT = "C4FM Symbol Filter Output"; - - private ArrayList mTaps; - private FloatTap mInputTap; - private FloatTap mSymbolFilterOutputTap; - - private C4FMSymbolFilter mSymbolFilter = new C4FMSymbolFilter(); - - private RealSampleListener mInput; - - public C4FMDecoder() - { - mInput = mSymbolFilter; - } - - @Override - public void receive( float sample ) - { - mInput.receive( sample ); - } - - @Override - public List getTaps() - { - if( mTaps == null ) - { - mTaps = new ArrayList(); - - FloatTap floatTap = new FloatTap( TAP_C4FM_INPUT, 0, 1.0f ); - mTaps.add( floatTap ); - - FloatTap symbolTap = new FloatTap( TAP_SYMBOL_FILTER_OUTPUT, 10, 0.1f ); - mTaps.add( symbolTap ); - } - - return mTaps; - } - - @Override - public void addTap( Tap tap ) - { - switch( tap.getName() ) - { - case TAP_C4FM_INPUT: - mInputTap = (FloatTap)tap; - mInput = mInputTap; - mInputTap.setListener( mSymbolFilter ); - break; - case TAP_SYMBOL_FILTER_OUTPUT: - mSymbolFilterOutputTap = (FloatTap)tap; - mSymbolFilter.setListener( mSymbolFilterOutputTap ); - break; - } - } - - @Override - public void removeTap( Tap tap ) - { - switch( tap.getName() ) - { - case TAP_C4FM_INPUT: - mInput = mSymbolFilter; - mInputTap = null; - break; - case TAP_SYMBOL_FILTER_OUTPUT: - mSymbolFilter.removeListener( mSymbolFilterOutputTap ); - mSymbolFilterOutputTap = null; - break; - } - } -} diff --git a/src/dsp/fsk/C4FMSymbolFilter.java b/src/dsp/fsk/C4FMSymbolFilter.java index c9078212..af6e4072 100644 --- a/src/dsp/fsk/C4FMSymbolFilter.java +++ b/src/dsp/fsk/C4FMSymbolFilter.java @@ -17,6 +17,7 @@ import source.tuner.FrequencyChangeBroadcaster; import source.tuner.FrequencyChangeEvent; import source.tuner.FrequencyChangeEvent.Attribute; import source.tuner.FrequencyChangeListener; +import dsp.gain.GainController; public class C4FMSymbolFilter implements FrequencyChangeBroadcaster, RealSampleListener, @@ -201,10 +202,11 @@ public class C4FMSymbolFilter implements FrequencyChangeBroadcaster, private RealSampleListener mListener; + public static final long MAX_FREQUENCY_CORRECTION = 7000; private FrequencyChangeListener mFrequencyChangeListener; private long mFrequencyCorrection; - private float mGain = 15.4f; + private GainController mGainController; /** * C4FM Symbol Filter @@ -219,13 +221,9 @@ public class C4FMSymbolFilter implements FrequencyChangeBroadcaster, * setting for this filter is 15.4 and that yields a symbol spread that * is centered on 2.0, ranging 1.92 to 2.08. */ - public C4FMSymbolFilter( float gain ) - { - mGain = gain; - } - - public C4FMSymbolFilter() + public C4FMSymbolFilter( GainController gainController ) { + mGainController = gainController; } public long getFrequencyCorrection() @@ -238,7 +236,8 @@ public class C4FMSymbolFilter implements FrequencyChangeBroadcaster, { mSymbolClock += mSymbolTime; - mHistory[ mHistoryLast++ ] = sample * mGain; + mHistory[ mHistoryLast++ ] = sample; + mHistoryLast %= NUMBER_FILTER_TAPS; if( mSymbolClock > 1.0f ) @@ -317,9 +316,19 @@ public class C4FMSymbolFilter implements FrequencyChangeBroadcaster, mSymbolClock -= symbolError * K_SYMBOL_TIMING; } - mSymbolSpread = Math.max( mSymbolSpread, SYMBOL_SPREAD_MIN ); - mSymbolSpread = Math.min( mSymbolSpread, SYMBOL_SPREAD_MAX ); - + if( mSymbolSpread < SYMBOL_SPREAD_MIN ) + { + mGainController.increase(); + + mSymbolSpread = SYMBOL_SPREAD_MIN; + } + else if( mSymbolSpread > SYMBOL_SPREAD_MAX ) + { + mGainController.decrease(); + + mSymbolSpread = SYMBOL_SPREAD_MAX; + } + mCoarseFrequencyCorrection += ( ( mFineFrequencyCorrection - mCoarseFrequencyCorrection ) * K_COARSE_FREQUENCY ); @@ -333,14 +342,21 @@ public class C4FMSymbolFilter implements FrequencyChangeBroadcaster, mFrequencyCorrection += ( 500 * ( mCoarseFrequencyCorrection > 0 ? 1 : -1 ) ); + if( mFrequencyCorrection > MAX_FREQUENCY_CORRECTION ) + { + mFrequencyCorrection = MAX_FREQUENCY_CORRECTION; + } + else if( -mFrequencyCorrection > MAX_FREQUENCY_CORRECTION ) + { + mFrequencyCorrection = -MAX_FREQUENCY_CORRECTION; + } + mFrequencyChangeListener.frequencyChanged( new FrequencyChangeEvent( Attribute.FREQUENCY_ERROR, mFrequencyCorrection ) ); /* reset internal frequency error tracker */ mCoarseFrequencyCorrection = 0; - - mLog.debug( "Frequency Corrected: " + mFrequencyCorrection ); } } diff --git a/src/dsp/agc/ComplexAutomaticGainControl.java b/src/dsp/gain/ComplexAutomaticGainControl.java similarity index 99% rename from src/dsp/agc/ComplexAutomaticGainControl.java rename to src/dsp/gain/ComplexAutomaticGainControl.java index 566d01a1..9fef2ace 100644 --- a/src/dsp/agc/ComplexAutomaticGainControl.java +++ b/src/dsp/gain/ComplexAutomaticGainControl.java @@ -1,4 +1,4 @@ -package dsp.agc; +package dsp.gain; import java.util.concurrent.atomic.AtomicBoolean; diff --git a/src/dsp/gain/DirectGainControl.java b/src/dsp/gain/DirectGainControl.java new file mode 100644 index 00000000..1ee5df38 --- /dev/null +++ b/src/dsp/gain/DirectGainControl.java @@ -0,0 +1,95 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014 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.gain; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sample.real.RealSampleListener; + +public class DirectGainControl implements GainController, RealSampleListener +{ + private final static Logger mLog = + LoggerFactory.getLogger( DirectGainControl.class ); + + private float mGain; + private float mMinimum; + private float mMaximum; + private float mIncrement; + + private RealSampleListener mListener; + + /** + * Gain Control. + * + * Note: does not perform null checking on output listener in order to + * streamline sample processing. + * + * @param gain - initial value + * @param minimum - minimum value greater than 0 + * @param maximum - maximum permitted value + * @param increment - amount gain is adjusted when increase() or decrease() + * is invoked + */ + public DirectGainControl( float gain, float minimum, float maximum, float increment ) + { + assert( minimum > 0 ); + + mGain = gain; + mMinimum = minimum; + mMaximum = maximum; + mIncrement = increment; + } + + @Override + public void receive( float sample ) + { + mListener.receive( (float)( sample * mGain ) ); + } + + @Override + public void increase() + { + mGain += mIncrement; + + if( mGain > mMaximum ) + { + mGain = mMaximum; + } + + mLog.debug( "Gain Increased: " + mGain ); + } + + @Override + public void decrease() + { + mGain -= mIncrement; + + if( mGain < mMinimum ) + { + mGain = mMinimum; + } + + mLog.debug( "Gain Decreased: " + mGain ); + } + + public void setListener( RealSampleListener listener ) + { + mListener = listener; + } +} diff --git a/src/dsp/gain/GainController.java b/src/dsp/gain/GainController.java new file mode 100644 index 00000000..b1f15e8a --- /dev/null +++ b/src/dsp/gain/GainController.java @@ -0,0 +1,8 @@ +package dsp.gain; + +public interface GainController +{ + public abstract void increase(); + + public abstract void decrease(); +} \ No newline at end of file diff --git a/src/dsp/agc/RealAutomaticGainControl.java b/src/dsp/gain/RealAutomaticGainControl.java similarity index 99% rename from src/dsp/agc/RealAutomaticGainControl.java rename to src/dsp/gain/RealAutomaticGainControl.java index 84512135..53e98d90 100644 --- a/src/dsp/agc/RealAutomaticGainControl.java +++ b/src/dsp/gain/RealAutomaticGainControl.java @@ -1,4 +1,4 @@ -package dsp.agc; +package dsp.gain; import java.util.concurrent.atomic.AtomicBoolean; diff --git a/src/source/mixer/RealMixerSource.java b/src/source/mixer/RealMixerSource.java index 58937bda..2e9ad4bd 100644 --- a/src/source/mixer/RealMixerSource.java +++ b/src/source/mixer/RealMixerSource.java @@ -29,7 +29,7 @@ import javax.sound.sampled.TargetDataLine; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import dsp.agc.RealAutomaticGainControl; +import dsp.gain.RealAutomaticGainControl; import sample.Listener; import sample.adapter.SampleAdapter; import sample.real.RealBuffer;