From bc5080aa4a8c4c9a06a5a076394e558f6d6b637a Mon Sep 17 00:00:00 2001 From: "dennis.sheirer" Date: Mon, 4 May 2015 00:55:04 +0000 Subject: [PATCH] P25 - updated automatic and direct frequency control to cleanup the code and allow P25 to better track frequency and PPM changes in the tuner and provide a cleaner implementation of the AFC component in the processing chain of all of the other decoders. Changed C4FM symbol filter to reset frequency tracking values when the tuner's frequency or PPM values change. Moved all frequency control pieces into the source.tuner.frequency package. --- .classpath | 2 +- src/controller/channel/Channel.java | 7 +- src/controller/channel/ProcessingChain.java | 68 ++------ src/decode/Decoder.java | 55 ++++++ src/decode/DecoderFactory.java | 10 +- src/decode/ltrnet/LTRNetDecoder.java | 6 +- .../ltrstandard/LTRStandardDecoder.java | 10 +- src/decode/mpt1327/MPT1327Decoder.java | 6 +- src/decode/nbfm/NBFMDecoder.java | 9 +- src/decode/p25/C4FMSymbolFilter.java | 80 ++++----- src/decode/p25/P25ChannelState.java | 9 +- src/decode/p25/P25Decoder.java | 47 ++--- src/decode/p25/audio/P25AudioOutput.java | 5 +- src/decode/passport/PassportDecoder.java | 7 +- src/dsp/psk/CQPSKDemodulator.java | 17 +- src/gui/control/JFrequencyControl.java | 8 +- src/instrument/gui/AudioSourceFrame.java | 12 +- src/sample/complex/ComplexSample.java | 61 +++++-- src/source/recording/Recording.java | 8 +- .../recording/RecordingEditorPanel.java | 2 +- .../tuner/DirectFrequencyController.java | 9 - src/source/tuner/Tuner.java | 10 +- src/source/tuner/TunerChannelSource.java | 10 +- src/source/tuner/TunerController.java | 4 +- src/source/tuner/TunerNode.java | 6 +- src/source/tuner/fcd/FCDTuner.java | 2 +- .../tuner/fcd/proV1/FCD1TunerEditorPanel.java | 10 +- .../fcd/proplusV2/FCD2TunerEditorPanel.java | 10 +- .../frequency/AutomaticFrequencyControl.java | 125 ++++++------- .../FrequencyChangeBroadcaster.java | 3 +- .../{ => frequency}/FrequencyChangeEvent.java | 10 +- .../FrequencyChangeListener.java | 3 +- .../{ => frequency}/FrequencyController.java | 13 +- .../frequency/FrequencyCorrectionControl.java | 164 ++++++++++++++++++ .../FrequencyLimitException.java | 2 +- .../tuner/hackrf/HackRFTunerEditorPanel.java | 10 +- src/source/tuner/rtl/RTL2832Tuner.java | 2 +- .../tuner/rtl/RTL2832TunerController.java | 4 +- .../tuner/rtl/e4k/E4KTunerEditorPanel.java | 8 +- .../rtl/r820t/R820TTunerEditorPanel.java | 8 +- src/spectrum/ChannelSpectrumPanel.java | 4 +- src/spectrum/DFTProcessor.java | 6 +- src/spectrum/OverlayPanel.java | 12 +- src/spectrum/SpectralDisplayPanel.java | 6 +- 44 files changed, 533 insertions(+), 337 deletions(-) delete mode 100644 src/source/tuner/DirectFrequencyController.java rename src/{dsp => source/tuner}/frequency/AutomaticFrequencyControl.java (54%) rename src/source/tuner/{ => frequency}/FrequencyChangeBroadcaster.java (97%) rename src/source/tuner/{ => frequency}/FrequencyChangeEvent.java (68%) rename src/source/tuner/{ => frequency}/FrequencyChangeListener.java (97%) rename src/source/tuner/{ => frequency}/FrequencyController.java (93%) create mode 100644 src/source/tuner/frequency/FrequencyCorrectionControl.java rename src/source/tuner/{ => frequency}/FrequencyLimitException.java (96%) diff --git a/.classpath b/.classpath index 5ac3191b..893c713b 100644 --- a/.classpath +++ b/.classpath @@ -24,6 +24,6 @@ - + diff --git a/src/controller/channel/Channel.java b/src/controller/channel/Channel.java index 1ae34152..106d507e 100644 --- a/src/controller/channel/Channel.java +++ b/src/controller/channel/Channel.java @@ -48,6 +48,7 @@ import source.config.SourceConfigTuner; import source.config.SourceConfiguration; import source.tuner.TunerChannel; import source.tuner.TunerChannel.Type; +import source.tuner.frequency.AutomaticFrequencyControl; import controller.ResourceManager; import controller.activity.ActivitySummaryFrame; import controller.channel.ChannelEvent.Event; @@ -59,7 +60,6 @@ import controller.system.System; import decode.config.AuxDecodeConfiguration; import decode.config.DecodeConfigFactory; import decode.config.DecodeConfiguration; -import dsp.frequency.AutomaticFrequencyControl; import eventlog.config.EventLogConfiguration; @XmlSeeAlso( { Configuration.class } ) @@ -145,14 +145,15 @@ public class Channel extends Configuration */ public boolean hasFrequencyControl() { - return mProcessingChain != null && mProcessingChain.hasFrequencyControl(); + return mProcessingChain != null && + mProcessingChain.getDecoder().hasFrequencyCorrectionControl(); } public long getFrequencyCorrection() { if( mProcessingChain != null ) { - return mProcessingChain.getFrequencyCorrection(); + return mProcessingChain.getDecoder().getFrequencyCorrection(); } return 0; diff --git a/src/controller/channel/ProcessingChain.java b/src/controller/channel/ProcessingChain.java index b7d1ef58..92a0a9c7 100644 --- a/src/controller/channel/ProcessingChain.java +++ b/src/controller/channel/ProcessingChain.java @@ -46,12 +46,11 @@ import source.RealSource; import source.Source; import source.SourceException; import source.config.SourceConfigMixer; -import source.tuner.DirectFrequencyController; -import source.tuner.FrequencyChangeListener; import source.tuner.Tuner; import source.tuner.TunerChannelSource; +import source.tuner.frequency.AutomaticFrequencyControl; +import source.tuner.frequency.FrequencyCorrectionControl; import alias.AliasList; -import audio.AudioOutputImpl; import audio.IAudioOutput; import audio.IAudioTypeListener; import audio.SquelchListener; @@ -63,7 +62,6 @@ import controller.state.ChannelState; import controller.state.ChannelState.State; import decode.Decoder; import decode.DecoderFactory; -import dsp.frequency.AutomaticFrequencyControl; import eventlog.EventLogType; import eventlog.EventLogger; @@ -120,31 +118,6 @@ public class ProcessingChain implements Listener return null; } - /** - * Current frequency correction setting - */ - public long getFrequencyCorrection() - { - if( mAFC != null ) - { - return mAFC.getErrorCorrection(); - } - else if( mDecoder != null && - mDecoder instanceof DirectFrequencyController ) - { - return ( (DirectFrequencyController)mDecoder).getFrequencyCorrection(); - } - - return 0; - } - - public boolean hasFrequencyControl() - { - return mAFC != null || - ( mDecoder != null && - mDecoder instanceof DirectFrequencyController ); - } - public Listener> getComplexReceiver() { return mComplexReceiver; @@ -373,37 +346,20 @@ public class ProcessingChain implements Listener /* Register to receive decoded messages and auxiliary messages */ mDecoder.addMessageListener( ProcessingChain.this ); - /* Automatic frequency control */ - if( mAFC != null ) - { - mAFC.dispose(); - mAFC = null; - } - - if( mSource instanceof TunerChannelSource ) + /* Establish two-way communication between the frequency + * correction controller and the tuner channel source */ + if( mSource instanceof TunerChannelSource && + mDecoder.hasFrequencyCorrectionControl() ) { TunerChannelSource tcs = (TunerChannelSource)mSource; - if( mChannel.getDecodeConfiguration().supportsAFC() && - mChannel.getDecodeConfiguration().isAFCEnabled() ) + FrequencyCorrectionControl fcc = + mDecoder.getFrequencyCorrectionControl(); + + if( fcc != null ) { - - int maximumCorrection = mChannel - .getDecodeConfiguration().getAFCMaximumCorrection(); - - mAFC = new AutomaticFrequencyControl( tcs, - maximumCorrection ); - - /* Register AFC to receive frequency change events */ - tcs.addListener( mAFC ); - - /* Add AFC as listener to the decoder */ - mDecoder.addUnfilteredRealSampleListener( mAFC ); - } - else if( mDecoder instanceof DirectFrequencyController ) - { - ((DirectFrequencyController)mDecoder) - .setListener( (FrequencyChangeListener)tcs ); + tcs.addListener( fcc ); + fcc.setListener( tcs ); } } diff --git a/src/decode/Decoder.java b/src/decode/Decoder.java index 62455e18..7fe365e4 100644 --- a/src/decode/Decoder.java +++ b/src/decode/Decoder.java @@ -27,7 +27,10 @@ import sample.complex.ComplexSample; import sample.real.RealSampleBroadcaster; import sample.real.RealSampleListener; import source.Source.SampleType; +import source.tuner.frequency.AutomaticFrequencyControl; +import source.tuner.frequency.FrequencyCorrectionControl; import audio.IAudioOutput; +import decode.config.DecodeConfiguration; import eventlog.MessageEventLogger; public abstract class Decoder implements Listener @@ -40,6 +43,8 @@ public abstract class Decoder implements Listener protected SampleType mSourceSampleType; protected ArrayList mAuxiliaryDecoders = new ArrayList(); + + protected FrequencyCorrectionControl mFrequencyCorrection; /** * Abstract decoder class. @@ -57,6 +62,51 @@ public abstract class Decoder implements Listener mSourceSampleType = sampleType; } + public Decoder( DecodeConfiguration config, SampleType sampleType ) + { + this( sampleType ); + + if( config.supportsAFC() && config.isAFCEnabled() ) + { + mFrequencyCorrection = + new AutomaticFrequencyControl( config.getAFCMaximumCorrection() ); + + /* Register AFC to receive non-filtered demodulated audio samples */ + addUnfilteredRealSampleListener( + (RealSampleListener)mFrequencyCorrection ); + } + } + + /** + * Optional frequency correction control to provide frequency adjustments + * directed by the decoder. + */ + public FrequencyCorrectionControl getFrequencyCorrectionControl() + { + return mFrequencyCorrection; + } + + /** + * Indicates if a frequency correction controller exists for this decoder + */ + public boolean hasFrequencyCorrectionControl() + { + return mFrequencyCorrection != null; + } + + /** + * Current frequency correction setting or zero if no controller exists + */ + public int getFrequencyCorrection() + { + if( hasFrequencyCorrectionControl() ) + { + return mFrequencyCorrection.getErrorCorrection(); + } + + return 0; + } + /** * Returns a real (ie demodulated) sample listener interface for * connecting this decoder to a real sample stream provider @@ -97,6 +147,11 @@ public abstract class Decoder implements Listener mMessageBroadcaster.clear(); mComplexBroadcaster.clear(); mRealBroadcaster.clear(); + + if( mFrequencyCorrection != null ) + { + mFrequencyCorrection.dispose(); + } } /** diff --git a/src/decode/DecoderFactory.java b/src/decode/DecoderFactory.java index 644cbe44..da7db700 100644 --- a/src/decode/DecoderFactory.java +++ b/src/decode/DecoderFactory.java @@ -74,30 +74,30 @@ public class DecoderFactory retVal = new AMDecoder( sampleType ); break; case NBFM: - retVal = new NBFMDecoder( sampleType ); + retVal = new NBFMDecoder( config, sampleType ); break; case LTR_STANDARD: DecodeConfigLTRStandard ltrStandardConfig = (DecodeConfigLTRStandard)config; - retVal = new LTRStandardDecoder( sampleType, aliasList, + retVal = new LTRStandardDecoder( config, sampleType, aliasList, ltrStandardConfig.getMessageDirection() ); break; case LTR_NET: DecodeConfigLTRNet ltrNetConfig = (DecodeConfigLTRNet)config; - retVal = new LTRNetDecoder( sampleType, aliasList, + retVal = new LTRNetDecoder( config, sampleType, aliasList, ltrNetConfig.getMessageDirection() ); break; case MPT1327: DecodeConfigMPT1327 mptConfig = (DecodeConfigMPT1327)config; - retVal = new MPT1327Decoder( sampleType, aliasList, + retVal = new MPT1327Decoder( config, sampleType, aliasList, mptConfig.getSync() ); break; case PASSPORT: - retVal = new PassportDecoder( sampleType, aliasList ); + retVal = new PassportDecoder( config, sampleType, aliasList ); break; case P25_PHASE1: DecodeConfigP25Phase1 p25Config = (DecodeConfigP25Phase1)config; diff --git a/src/decode/ltrnet/LTRNetDecoder.java b/src/decode/ltrnet/LTRNetDecoder.java index be8da2c5..163573cc 100644 --- a/src/decode/ltrnet/LTRNetDecoder.java +++ b/src/decode/ltrnet/LTRNetDecoder.java @@ -33,6 +33,7 @@ import bits.MessageFramer; import bits.SyncPattern; import decode.Decoder; import decode.DecoderType; +import decode.config.DecodeConfiguration; import dsp.filter.DCRemovalFilter2; import dsp.fsk.LTRFSKDecoder; import dsp.nbfm.FilteringNBFMDemodulator; @@ -55,11 +56,12 @@ public class LTRNetDecoder extends Decoder implements Instrumentable private List mAvailableTaps; - public LTRNetDecoder( SampleType sampleType, + public LTRNetDecoder( DecodeConfiguration config, + SampleType sampleType, AliasList aliasList, MessageDirection direction ) { - super( sampleType ); + super( config, sampleType ); /** * Only setup a demod chain if we're receiving complex samples. If diff --git a/src/decode/ltrstandard/LTRStandardDecoder.java b/src/decode/ltrstandard/LTRStandardDecoder.java index 71b50786..338e7003 100644 --- a/src/decode/ltrstandard/LTRStandardDecoder.java +++ b/src/decode/ltrstandard/LTRStandardDecoder.java @@ -27,6 +27,7 @@ import bits.MessageFramer; import bits.SyncPattern; import decode.Decoder; import decode.DecoderType; +import decode.config.DecodeConfiguration; import dsp.filter.DCRemovalFilter2; import dsp.fsk.LTRFSKDecoder; import dsp.nbfm.FilteringNBFMDemodulator; @@ -47,11 +48,12 @@ public class LTRStandardDecoder extends Decoder private LTRStandardMessageProcessor mLTRMessageProcessor; private AudioOutputImpl mAudioOutput = new AudioOutputImpl( "LTR Standard Decoder Audio Output" ); - public LTRStandardDecoder( SampleType sampleType, - AliasList aliasList, - MessageDirection direction ) + public LTRStandardDecoder( DecodeConfiguration config, + SampleType sampleType, + AliasList aliasList, + MessageDirection direction ) { - super( sampleType ); + super( config, sampleType ); /** * Only setup a demod chain if we're receiving complex samples. If diff --git a/src/decode/mpt1327/MPT1327Decoder.java b/src/decode/mpt1327/MPT1327Decoder.java index c3ebc6ad..7fabfbbe 100644 --- a/src/decode/mpt1327/MPT1327Decoder.java +++ b/src/decode/mpt1327/MPT1327Decoder.java @@ -35,6 +35,7 @@ import bits.MessageFramer; import bits.SyncPattern; import decode.Decoder; import decode.DecoderType; +import decode.config.DecodeConfiguration; import dsp.filter.DCRemovalFilter; import dsp.filter.FilterFactory; import dsp.filter.Filters; @@ -86,11 +87,12 @@ public class MPT1327Decoder extends Decoder implements Instrumentable private MPT1327MessageProcessor mMessageProcessor; private AudioOutputImpl mAudioOutput = new AudioOutputImpl( "MPT1327 Decoder Audio Output" ); - public MPT1327Decoder( SampleType sampleType, + public MPT1327Decoder( DecodeConfiguration config, + SampleType sampleType, AliasList aliasList, Sync sync ) { - super( sampleType ); + super( config, sampleType ); /* If we're receiving complex samples, do FM demodulation and DC removal * and feed the output back to this decoder */ diff --git a/src/decode/nbfm/NBFMDecoder.java b/src/decode/nbfm/NBFMDecoder.java index aa003a3a..5f8c1faa 100644 --- a/src/decode/nbfm/NBFMDecoder.java +++ b/src/decode/nbfm/NBFMDecoder.java @@ -17,12 +17,13 @@ ******************************************************************************/ package decode.nbfm; -import audio.AudioOutputImpl; -import audio.IAudioOutput; import sample.real.RealSampleListener; import source.Source.SampleType; +import audio.AudioOutputImpl; +import audio.IAudioOutput; import decode.Decoder; import decode.DecoderType; +import decode.config.DecodeConfiguration; import dsp.filter.DCRemovalFilter2; import dsp.nbfm.FilteringNBFMDemodulator; @@ -38,9 +39,9 @@ public class NBFMDecoder extends Decoder private DCRemovalFilter2 mDCRemovalFilter; private AudioOutputImpl mAudioOutput = new AudioOutputImpl( "NBFM Decoder Audio Output" ); - public NBFMDecoder( SampleType sampleType ) + public NBFMDecoder( DecodeConfiguration config, SampleType sampleType ) { - super( sampleType ); + super( config, sampleType ); /** * Only setup a demod chain if we're receiving complex samples. If diff --git a/src/decode/p25/C4FMSymbolFilter.java b/src/decode/p25/C4FMSymbolFilter.java index 4ce6445f..65b2c286 100644 --- a/src/decode/p25/C4FMSymbolFilter.java +++ b/src/decode/p25/C4FMSymbolFilter.java @@ -13,13 +13,11 @@ import org.slf4j.LoggerFactory; import sample.real.RealSampleListener; import sample.real.RealSampleProvider; -import source.tuner.FrequencyChangeBroadcaster; -import source.tuner.FrequencyChangeEvent; -import source.tuner.FrequencyChangeEvent.Attribute; -import source.tuner.FrequencyChangeListener; +import source.tuner.frequency.FrequencyCorrectionControl; +import source.tuner.frequency.FrequencyCorrectionControl.FrequencyCorrectionResetListener; import dsp.gain.GainController; -public class C4FMSymbolFilter implements FrequencyChangeBroadcaster, +public class C4FMSymbolFilter implements FrequencyCorrectionResetListener, RealSampleListener, RealSampleProvider, Instrumentable @@ -200,13 +198,12 @@ public class C4FMSymbolFilter implements FrequencyChangeBroadcaster, private float mHistory[] = new float[ NUMBER_FILTER_TAPS ]; private int mHistoryLast = 0; + private boolean mReset = false; + private RealSampleListener mListener; - public static final long MAX_FREQUENCY_CORRECTION = 3000; - private FrequencyChangeListener mFrequencyChangeListener; - private long mFrequencyCorrection; - private GainController mGainController; + private FrequencyCorrectionControl mFrequencyCorrectionControl; /** * C4FM Symbol Filter @@ -221,19 +218,31 @@ 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( GainController gainController ) + public C4FMSymbolFilter( GainController gainController, + FrequencyCorrectionControl frequencyControl ) { mGainController = gainController; - } - - public long getFrequencyCorrection() - { - return mFrequencyCorrection; + + mFrequencyCorrectionControl = frequencyControl; + + /* Register for reset events, so we can reset internal frequency tracking */ + mFrequencyCorrectionControl.setListener( this ); } @Override public void receive( float sample ) { + if( mReset ) + { + mCoarseFrequencyCorrection = 0.0f; + mFineFrequencyCorrection = 0.0f; + mSymbolSpread = 2.0f; + mSymbolClock = 0.0f; + mSymbolTime = (float)SYMBOL_RATE / (float)SAMPLE_RATE; + + mReset = false; + } + mSymbolClock += mSymbolTime; mHistory[ mHistoryLast++ ] = sample; @@ -337,27 +346,11 @@ public class C4FMSymbolFilter implements FrequencyChangeBroadcaster, /* send frequency correction */ if( Math.abs( mCoarseFrequencyCorrection ) > COARSE_FREQUENCY_DEADBAND ) { - if( mFrequencyChangeListener != null ) - { - mFrequencyCorrection += ( + mFrequencyCorrectionControl.adjust( 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; - } } if( mSymbolSpreadTap != null ) @@ -373,6 +366,17 @@ public class C4FMSymbolFilter implements FrequencyChangeBroadcaster, } } + /** + * Listener interface for frequency correction reset events. We reset the + * coarse and fine frequency control values when the tuner channel source + * has changed frequency or frequency offset. + */ + @Override + public void resetFrequencyCorrection() + { + mReset = true; + } + @Override public void setListener( RealSampleListener listener ) { @@ -420,16 +424,4 @@ public class C4FMSymbolFilter implements FrequencyChangeBroadcaster, mSymbolSpreadTap = null; } } - - @Override - public void addListener( FrequencyChangeListener listener ) - { - mFrequencyChangeListener = listener; - } - - @Override - public void removeListener( FrequencyChangeListener listener ) - { - mFrequencyChangeListener = null; - } } diff --git a/src/decode/p25/P25ChannelState.java b/src/decode/p25/P25ChannelState.java index dad5647c..6a6831be 100644 --- a/src/decode/p25/P25ChannelState.java +++ b/src/decode/p25/P25ChannelState.java @@ -56,6 +56,7 @@ import decode.p25.message.pdu.confirmed.SNDCPActivateTDSContextReject; import decode.p25.message.pdu.confirmed.SNDCPActivateTDSContextRequest; import decode.p25.message.pdu.confirmed.SNDCPDeactivateTDSContext; import decode.p25.message.pdu.confirmed.SNDCPUserData; +import decode.p25.message.pdu.osp.control.AdjacentStatusBroadcastExtended; import decode.p25.message.pdu.osp.control.CallAlertExtended; import decode.p25.message.pdu.osp.control.GroupAffiliationQueryExtended; import decode.p25.message.pdu.osp.control.GroupAffiliationResponseExtended; @@ -115,7 +116,6 @@ import decode.p25.message.tsbk.osp.voice.TelephoneInterconnectVoiceChannelGrantU import decode.p25.message.tsbk.osp.voice.UnitToUnitAnswerRequest; import decode.p25.message.tsbk.osp.voice.UnitToUnitVoiceChannelGrant; import decode.p25.message.tsbk.osp.voice.UnitToUnitVoiceChannelGrantUpdate; -import decode.p25.reference.Encryption; import decode.p25.reference.IPProtocol; import decode.p25.reference.Response; import decode.p25.reference.Vendor; @@ -1599,6 +1599,12 @@ public class P25ChannelState extends ChannelState { switch( pdu.getOpcode() ) { + case ADJACENT_STATUS_BROADCAST: + AdjacentStatusBroadcastExtended asbe = + (AdjacentStatusBroadcastExtended)pdu; + + updateSystem( asbe.getSystemID() ); + break; case CALL_ALERT: if( pdu instanceof CallAlertExtended ) { @@ -2064,7 +2070,6 @@ public class P25ChannelState extends ChannelState } break; default: - mLog.debug( "PDU - unrecognized OPCODE message: " + pdu.toString() ); break; } } diff --git a/src/decode/p25/P25Decoder.java b/src/decode/p25/P25Decoder.java index 251f6063..c62cda80 100644 --- a/src/decode/p25/P25Decoder.java +++ b/src/decode/p25/P25Decoder.java @@ -32,8 +32,7 @@ import org.slf4j.LoggerFactory; import sample.real.RealSampleListener; import source.Source.SampleType; -import source.tuner.DirectFrequencyController; -import source.tuner.FrequencyChangeListener; +import source.tuner.frequency.FrequencyCorrectionControl; import alias.AliasList; import controller.ResourceManager; import decode.Decoder; @@ -48,12 +47,12 @@ import dsp.gain.DirectGainControl; import dsp.nbfm.FMDiscriminator; import dsp.psk.CQPSKDemodulator; import dsp.psk.QPSKStarSlicer; -import dsp.symbol.FrameSync; -public class P25Decoder extends Decoder - implements DirectFrequencyController, Instrumentable +public class P25Decoder extends Decoder implements Instrumentable { private final static Logger mLog = LoggerFactory.getLogger( P25Decoder.class ); + + private final static int MAXIMUM_FREQUENCY_CORRECTION = 3000; //Hertz, +/- /* Instrumentation Taps */ private static final String INSTRUMENT_COMPLEX_INPUT = "Tap Point: Complex Input"; @@ -73,7 +72,7 @@ public class P25Decoder extends Decoder /* Demods */ private FMDiscriminator mFMDemodulator; private CQPSKDemodulator mCQPSKDemodulator; - + /* Gain */ private ComplexFeedForwardGainControl mAGC; private DirectGainControl mDGC; @@ -162,6 +161,8 @@ public class P25Decoder extends Decoder * can process the output as if it were coming from any other * real sample source */ mFMDemodulator.setListener( getRealReceiver() ); + + } } @@ -175,7 +176,10 @@ public class P25Decoder extends Decoder mDGC = new DirectGainControl( 15.0f, 0.1f, 35.0f, 0.3f ); mAudioFilter.setListener( mDGC ); - mSymbolFilter = new C4FMSymbolFilter( mDGC ); + mFrequencyCorrection = new FrequencyCorrectionControl( + MAXIMUM_FREQUENCY_CORRECTION ); + + mSymbolFilter = new C4FMSymbolFilter( mDGC, mFrequencyCorrection ); mDGC.setListener( mSymbolFilter ); mC4FMSlicer = new C4FMSlicer(); @@ -371,35 +375,6 @@ public class P25Decoder extends Decoder + "not implemented in P25 Decoder" ); } - /** - * Registers a frequency change listener to receive frequency correction - * events from the symbol filter - */ - @Override - public void setListener( FrequencyChangeListener listener ) - { - if( mSymbolFilter != null ) - { - mSymbolFilter.addListener( listener ); - } - - if( mCQPSKDemodulator != null ) - { - mCQPSKDemodulator.addListener( listener ); - } - } - - @Override - public long getFrequencyCorrection() - { - if( mSymbolFilter != null ) - { - return mSymbolFilter.getFrequencyCorrection(); - } - - return 0; - } - public enum Modulation { CQPSK( "LSM SIMULCAST", "LSM" ), diff --git a/src/decode/p25/audio/P25AudioOutput.java b/src/decode/p25/audio/P25AudioOutput.java index 51033768..7bfcfcd7 100644 --- a/src/decode/p25/audio/P25AudioOutput.java +++ b/src/decode/p25/audio/P25AudioOutput.java @@ -141,7 +141,8 @@ public class P25AudioOutput implements IAudioOutput, IAudioTypeListener, try { - mOutput = AudioSystem.getSourceDataLine( IMBEAudioFormat.PCM_SIGNED_8KHZ_16BITS ); + mOutput = AudioSystem.getSourceDataLine( + IMBEAudioFormat.PCM_SIGNED_8KHZ_16BITS ); if( mOutput != null ) { @@ -242,7 +243,7 @@ public class P25AudioOutput implements IAudioOutput, IAudioTypeListener, /* Insert an 18-byte imbe frame into the stream */ mIMBETargetDataLine.receive( frame ); - /* Read 1920 bytes of converted upsamped audio from output */ + /* Read 1920 bytes of converted audio from output */ byte[] audio = new byte[ PROCESSED_AUDIO_FRAME_SIZE ]; try diff --git a/src/decode/passport/PassportDecoder.java b/src/decode/passport/PassportDecoder.java index 609d1644..8293123e 100644 --- a/src/decode/passport/PassportDecoder.java +++ b/src/decode/passport/PassportDecoder.java @@ -32,6 +32,7 @@ import bits.MessageFramer; import bits.SyncPattern; import decode.Decoder; import decode.DecoderType; +import decode.config.DecodeConfiguration; import dsp.filter.DCRemovalFilter2; import dsp.fsk.LTRFSKDecoder; import dsp.nbfm.FilteringNBFMDemodulator; @@ -55,9 +56,11 @@ public class PassportDecoder extends Decoder implements Instrumentable private List mAvailableTaps; - public PassportDecoder( SampleType sampleType, AliasList aliasList ) + public PassportDecoder( DecodeConfiguration config, + SampleType sampleType, + AliasList aliasList ) { - super( sampleType ); + super( config, sampleType ); /** * Only setup a demod chain if we're receiving complex samples. If diff --git a/src/dsp/psk/CQPSKDemodulator.java b/src/dsp/psk/CQPSKDemodulator.java index 84eed452..91ba9870 100644 --- a/src/dsp/psk/CQPSKDemodulator.java +++ b/src/dsp/psk/CQPSKDemodulator.java @@ -36,6 +36,7 @@ import instrument.tap.stream.EyeDiagramDataTap; import java.text.DecimalFormat; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import org.slf4j.Logger; @@ -44,7 +45,7 @@ import org.slf4j.LoggerFactory; import sample.Listener; import sample.Provider; import sample.complex.ComplexSample; -import source.tuner.FrequencyChangeListener; +import source.tuner.frequency.FrequencyChangeListener; import buffer.FloatAveragingBuffer; import dsp.filter.interpolator.QPSKInterpolator; @@ -297,13 +298,13 @@ public class CQPSKDemodulator implements Instrumentable, middleSymbol.normalize(); currentSymbol.normalize(); -// if( mEyeDiagramDataTap != null ) -// { -// mEyeDiagramDataTap.receive( -// new EyeDiagramData( Arrays.copyOfRange( mDelayLine, -// mDelayLinePointer, mDelayLinePointer + 20 ), -// mMu, (float)half_sps + half_mu ) ); -// } + if( mEyeDiagramDataTap != null ) + { + mEyeDiagramDataTap.receive( + new EyeDiagramData( Arrays.copyOfRange( mDelayLine, + mDelayLinePointer, mDelayLinePointer + 20 ), + mMu, (float)half_sps + half_mu ) ); + } /* Gardner timing error calculations */ float errorInphase = ( mPreviousSymbol.inphase() - diff --git a/src/gui/control/JFrequencyControl.java b/src/gui/control/JFrequencyControl.java index b70ef0d9..66a7d1c1 100644 --- a/src/gui/control/JFrequencyControl.java +++ b/src/gui/control/JFrequencyControl.java @@ -43,9 +43,9 @@ import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import source.tuner.FrequencyChangeEvent; -import source.tuner.FrequencyChangeListener; -import source.tuner.FrequencyChangeEvent.Attribute; +import source.tuner.frequency.FrequencyChangeEvent; +import source.tuner.frequency.FrequencyChangeListener; +import source.tuner.frequency.FrequencyChangeEvent.Attribute; public class JFrequencyControl extends JPanel implements FrequencyChangeListener { @@ -138,7 +138,7 @@ public class JFrequencyControl extends JPanel implements FrequencyChangeListener { if( event.getAttribute() == Attribute.FREQUENCY ) { - setFrequency( (long)event.getValue(), false ); + setFrequency( event.getValue().longValue(), false ); } } diff --git a/src/instrument/gui/AudioSourceFrame.java b/src/instrument/gui/AudioSourceFrame.java index 191e9c9d..ff2c0e74 100644 --- a/src/instrument/gui/AudioSourceFrame.java +++ b/src/instrument/gui/AudioSourceFrame.java @@ -45,6 +45,9 @@ import source.wave.WaveSource.PositionListener; import controller.ResourceManager; import decode.Decoder; import decode.DecoderType; +import decode.config.DecodeConfigLTRNet; +import decode.config.DecodeConfigMPT1327; +import decode.config.DecodeConfigPassport; import decode.fleetsync2.Fleetsync2Decoder; import decode.lj1200.LJ1200Decoder; import decode.ltrnet.LTRNetDecoder; @@ -185,16 +188,19 @@ public class AudioSourceFrame extends JInternalFrame implements PositionListener decoder = new LJ1200Decoder( null ); break; case LTR_NET: - decoder = new LTRNetDecoder( mSource.getSampleType(), null, MessageDirection.OSW ); + decoder = new LTRNetDecoder( new DecodeConfigLTRNet(), + mSource.getSampleType(), null, MessageDirection.OSW ); break; case MDC1200: decoder = new MDCDecoder( null ); break; case MPT1327: - decoder = new MPT1327Decoder( mSource.getSampleType(), null, Sync.NORMAL ); + decoder = new MPT1327Decoder( new DecodeConfigMPT1327(), + mSource.getSampleType(), null, Sync.NORMAL ); break; case PASSPORT: - decoder = new PassportDecoder( mSource.getSampleType(), null ); + decoder = new PassportDecoder( new DecodeConfigPassport(), + mSource.getSampleType(), null ); break; case P25_PHASE1: ResourceManager rm = new ResourceManager(); diff --git a/src/sample/complex/ComplexSample.java b/src/sample/complex/ComplexSample.java index 4c220f47..7b4bd6a9 100644 --- a/src/sample/complex/ComplexSample.java +++ b/src/sample/complex/ComplexSample.java @@ -81,6 +81,12 @@ public class ComplexSample implements Serializable mRight *= scalor; } + public static ComplexSample multiply( ComplexSample sample, float scalor ) + { + return new ComplexSample( sample.left() * scalor, + sample.right() * scalor ); + } + /** * Multiplies this sample by the multiplier sample */ @@ -159,9 +165,17 @@ public class ComplexSample implements Serializable * Magnitude squared of this sample */ public float magnitudeSquared() + { + return norm(); + } + + /** + * Norm of this sample = ( i * i ) + ( q * q ) + */ + public float norm() { return (float)( ( inphase() * inphase() ) + - ( quadrature() * quadrature() ) ); + ( quadrature() * quadrature() ) ); } /** @@ -293,6 +307,32 @@ public class ComplexSample implements Serializable return (float)Math.atan2( y(), x() ); } + /** + * Constrains the i and q quantities to +/- value. + * + * @param value - maximum absolute value + */ + public void clip( float value ) + { + if( mLeft > value ) + { + mLeft = value; + } + else if( mLeft < -value ) + { + mLeft = -value; + } + + if( mRight > value ) + { + mRight = value; + } + else if( mRight < -value ) + { + mRight = -value; + } + } + /** * Angle of this sample in degrees */ @@ -321,18 +361,17 @@ public class ComplexSample implements Serializable public static void main( String[] args ) { - double SYMBOL_FREQUENCY = 2.0d * Math.PI * 4800.0d / 48000.0d; - double PHASE_CORRECTION_90_DEGREES = SYMBOL_FREQUENCY / 4.0d; - double PHASE_CORRECTION_180_DEGREES = SYMBOL_FREQUENCY / 2.0d; - - double angle = Math.PI / 4; - - ComplexSample s1 = new ComplexSample( (float)Math.sin( angle ), (float)Math.cos( angle ) ); - ComplexSample s2 = new ComplexSample( (float)Math.sin( -angle ), (float)Math.cos( -angle ) ); + double angle = Math.PI; //180 degrees - ComplexSample symbol = ComplexSample.multiply( s1, s2.conjugate() ); + ComplexSample s1 = new ComplexSample( (float)Math.sin( angle ), (float)Math.cos( angle ) ); - mLog.debug( "S1:" + s1.polarAngle() + " S2:" + s2.polarAngle() + " Symbol:" + symbol.polarAngle() ); + ComplexSample tap = new ComplexSample( 1.0f, -0.1f ); + + ComplexSample convolved = ComplexSample.multiply( s1, tap ); + + mLog.debug( "s: " + s1.toString() + + " t: " + tap.toString() + + " convolved: " + convolved.toString() ); } } diff --git a/src/source/recording/Recording.java b/src/source/recording/Recording.java index 501793dd..48899f8b 100644 --- a/src/source/recording/Recording.java +++ b/src/source/recording/Recording.java @@ -5,12 +5,12 @@ import java.util.ArrayList; import java.util.concurrent.RejectedExecutionException; import source.SourceException; -import source.tuner.FrequencyChangeEvent; -import source.tuner.FrequencyChangeListener; import source.tuner.TunerChannel; import source.tuner.TunerChannelProvider; import source.tuner.TunerChannelSource; -import source.tuner.FrequencyChangeEvent.Attribute; +import source.tuner.frequency.FrequencyChangeEvent; +import source.tuner.frequency.FrequencyChangeListener; +import source.tuner.frequency.FrequencyChangeEvent.Attribute; import controller.ResourceManager; import controller.ThreadPoolManager; @@ -92,7 +92,7 @@ public class Recording implements Comparable, { if( event.getAttribute() == Attribute.FREQUENCY ) { - int frequency = (int)event.getValue(); + long frequency = event.getValue().longValue(); mConfiguration.setCenterFrequency( frequency ); diff --git a/src/source/recording/RecordingEditorPanel.java b/src/source/recording/RecordingEditorPanel.java index 03c3cb2b..37f95733 100644 --- a/src/source/recording/RecordingEditorPanel.java +++ b/src/source/recording/RecordingEditorPanel.java @@ -15,7 +15,7 @@ import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JTextField; -import source.tuner.FrequencyChangeListener; +import source.tuner.frequency.FrequencyChangeListener; import net.miginfocom.swing.MigLayout; public class RecordingEditorPanel extends JPanel diff --git a/src/source/tuner/DirectFrequencyController.java b/src/source/tuner/DirectFrequencyController.java deleted file mode 100644 index ad6cf9e3..00000000 --- a/src/source/tuner/DirectFrequencyController.java +++ /dev/null @@ -1,9 +0,0 @@ -package source.tuner; - - -public interface DirectFrequencyController -{ - public void setListener( FrequencyChangeListener listener ); - - public long getFrequencyCorrection(); -} diff --git a/src/source/tuner/Tuner.java b/src/source/tuner/Tuner.java index 2076d3ca..c9979267 100644 --- a/src/source/tuner/Tuner.java +++ b/src/source/tuner/Tuner.java @@ -22,10 +22,16 @@ import java.util.concurrent.RejectedExecutionException; import javax.swing.JPanel; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import sample.Listener; import sample.complex.ComplexBuffer; import source.SourceException; -import source.tuner.FrequencyChangeEvent.Attribute; +import source.tuner.frequency.FrequencyChangeBroadcaster; +import source.tuner.frequency.FrequencyChangeEvent; +import source.tuner.frequency.FrequencyChangeListener; +import source.tuner.frequency.FrequencyChangeEvent.Attribute; import controller.ResourceManager; import controller.ThreadPoolManager; @@ -36,6 +42,8 @@ public abstract class Tuner implements FrequencyChangeBroadcaster, FrequencyChangeListener, TunerChannelProvider { + private final static Logger mLog = LoggerFactory.getLogger( Tuner.class ); + private String mName; /** diff --git a/src/source/tuner/TunerChannelSource.java b/src/source/tuner/TunerChannelSource.java index 2f15516d..a22f6ff0 100644 --- a/src/source/tuner/TunerChannelSource.java +++ b/src/source/tuner/TunerChannelSource.java @@ -33,7 +33,9 @@ import sample.complex.ComplexSample; import sample.complex.ComplexSampleAssembler; import source.ComplexSource; import source.SourceException; -import source.tuner.FrequencyChangeEvent.Attribute; +import source.tuner.frequency.FrequencyChangeEvent; +import source.tuner.frequency.FrequencyChangeListener; +import source.tuner.frequency.FrequencyChangeEvent.Attribute; import util.Oscillator; import controller.ThreadPoolManager; import controller.ThreadPoolManager.ThreadType; @@ -173,7 +175,7 @@ public class TunerChannelSource extends ComplexSource case SAMPLE_RATE_ERROR: break; case SAMPLE_RATE: - int sampleRate = (int)event.getValue(); + int sampleRate = event.getValue().intValue(); if( mTunerSampleRate != sampleRate ) { @@ -196,7 +198,7 @@ public class TunerChannelSource extends ComplexSource } break; case FREQUENCY_ERROR: - mTunerFrequencyError = (int)event.getValue(); + mTunerFrequencyError = event.getValue().intValue(); long frequencyErrorOffset = mTunerFrequency - mTunerChannel.getFrequency() - @@ -205,7 +207,7 @@ public class TunerChannelSource extends ComplexSource mSineWaveGenerator.setFrequency( frequencyErrorOffset ); break; case FREQUENCY: - int frequency = (int)event.getValue(); + long frequency = event.getValue().longValue(); /* If the frequency is updated, the AFC will also get reset, so * we don't include the frequency error value here */ diff --git a/src/source/tuner/TunerController.java b/src/source/tuner/TunerController.java index 27621896..c5cf2064 100644 --- a/src/source/tuner/TunerController.java +++ b/src/source/tuner/TunerController.java @@ -26,7 +26,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import source.SourceException; -import source.tuner.FrequencyController.Tunable; +import source.tuner.frequency.FrequencyChangeListener; +import source.tuner.frequency.FrequencyController; +import source.tuner.frequency.FrequencyController.Tunable; import controller.ThreadPoolManager; public abstract class TunerController implements Tunable diff --git a/src/source/tuner/TunerNode.java b/src/source/tuner/TunerNode.java index 20f1dfe4..7e4887b1 100644 --- a/src/source/tuner/TunerNode.java +++ b/src/source/tuner/TunerNode.java @@ -29,6 +29,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import source.SourceException; +import source.tuner.frequency.FrequencyChangeEvent; +import source.tuner.frequency.FrequencyChangeListener; import spectrum.SpectrumFrame; import controller.BaseNode; @@ -127,7 +129,7 @@ public class TunerNode extends BaseNode implements FrequencyChangeListener switch( event.getAttribute() ) { case FREQUENCY: - int frequency = (int)event.getValue(); + long frequency = event.getValue().longValue(); if( mFrequency != frequency ) { @@ -136,7 +138,7 @@ public class TunerNode extends BaseNode implements FrequencyChangeListener } break; case SAMPLE_RATE: - int sampleRate = (int)event.getValue(); + int sampleRate = event.getValue().intValue(); if( mSampleRate != sampleRate ) { diff --git a/src/source/tuner/fcd/FCDTuner.java b/src/source/tuner/fcd/FCDTuner.java index ae697917..1aadbba6 100644 --- a/src/source/tuner/fcd/FCDTuner.java +++ b/src/source/tuner/fcd/FCDTuner.java @@ -25,7 +25,6 @@ import sample.Listener; import sample.adapter.ShortAdapter; import sample.complex.ComplexBuffer; import source.SourceException; -import source.tuner.FrequencyChangeListener; import source.tuner.MixerTuner; import source.tuner.MixerTunerDataLine; import source.tuner.TunerChannel; @@ -33,6 +32,7 @@ import source.tuner.TunerChannelSource; import source.tuner.TunerClass; import source.tuner.TunerConfiguration; import source.tuner.TunerType; +import source.tuner.frequency.FrequencyChangeListener; import controller.ResourceManager; import controller.ThreadPoolManager; diff --git a/src/source/tuner/fcd/proV1/FCD1TunerEditorPanel.java b/src/source/tuner/fcd/proV1/FCD1TunerEditorPanel.java index 12c7b2c5..e82b7a43 100644 --- a/src/source/tuner/fcd/proV1/FCD1TunerEditorPanel.java +++ b/src/source/tuner/fcd/proV1/FCD1TunerEditorPanel.java @@ -30,11 +30,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import source.SourceException; -import source.tuner.FrequencyChangeEvent; -import source.tuner.FrequencyChangeEvent.Attribute; -import source.tuner.FrequencyChangeListener; import source.tuner.fcd.FCDTuner; import source.tuner.fcd.FCDTunerDetailsPanel; +import source.tuner.frequency.FrequencyChangeEvent; +import source.tuner.frequency.FrequencyChangeListener; +import source.tuner.frequency.FrequencyChangeEvent.Attribute; import com.jidesoft.swing.JideTabbedPane; @@ -99,12 +99,12 @@ public class FCD1TunerEditorPanel extends JPanel implements FrequencyChangeListe { try { - mController.setFrequency( event.getValue() ); + mController.setFrequency( event.getValue().longValue() ); } catch ( SourceException e ) { mLog.error( "FCTProController - error setting frequency [" + - event.getValue() + "]", e ); + event.getValue().longValue() + "]", e ); } } } diff --git a/src/source/tuner/fcd/proplusV2/FCD2TunerEditorPanel.java b/src/source/tuner/fcd/proplusV2/FCD2TunerEditorPanel.java index 553f62c1..e7dcba54 100644 --- a/src/source/tuner/fcd/proplusV2/FCD2TunerEditorPanel.java +++ b/src/source/tuner/fcd/proplusV2/FCD2TunerEditorPanel.java @@ -30,11 +30,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import source.SourceException; -import source.tuner.FrequencyChangeEvent; -import source.tuner.FrequencyChangeEvent.Attribute; -import source.tuner.FrequencyChangeListener; import source.tuner.fcd.FCDTuner; import source.tuner.fcd.FCDTunerDetailsPanel; +import source.tuner.frequency.FrequencyChangeEvent; +import source.tuner.frequency.FrequencyChangeListener; +import source.tuner.frequency.FrequencyChangeEvent.Attribute; import com.jidesoft.swing.JideTabbedPane; @@ -99,12 +99,12 @@ public class FCD2TunerEditorPanel extends JPanel implements FrequencyChangeListe //Disregard the bandwidth, which shouldn't be changing try { - mController.setFrequency( event.getValue() ); + mController.setFrequency( event.getValue().longValue() ); } catch ( SourceException e ) { mLog.error( "FCDProController - error setting frequency [" + - event.getValue() + "]", e ); + event.getValue().longValue() + "]", e ); } } } diff --git a/src/dsp/frequency/AutomaticFrequencyControl.java b/src/source/tuner/frequency/AutomaticFrequencyControl.java similarity index 54% rename from src/dsp/frequency/AutomaticFrequencyControl.java rename to src/source/tuner/frequency/AutomaticFrequencyControl.java index ee634c8a..b655a1d5 100644 --- a/src/dsp/frequency/AutomaticFrequencyControl.java +++ b/src/source/tuner/frequency/AutomaticFrequencyControl.java @@ -1,16 +1,32 @@ -package dsp.frequency; +package source.tuner.frequency; + +/******************************************************************************* + * 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 + ******************************************************************************/ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sample.real.RealSampleListener; -import source.tuner.FrequencyChangeEvent; -import source.tuner.FrequencyChangeEvent.Attribute; -import source.tuner.FrequencyChangeListener; +import source.tuner.frequency.FrequencyChangeEvent.Attribute; import buffer.FloatAveragingBuffer; -public class AutomaticFrequencyControl implements RealSampleListener, - FrequencyChangeListener +public class AutomaticFrequencyControl extends FrequencyCorrectionControl + implements RealSampleListener { private final static Logger mLog = LoggerFactory.getLogger( AutomaticFrequencyControl.class ); @@ -35,40 +51,44 @@ public class AutomaticFrequencyControl implements RealSampleListener, private int mSkipCounter = 0; private int mSkipThreshold = 4; private int mErrorCorrection = 0; - private long mCurrentFrequency; - private int mMaximumCorrection; - private FrequencyChangeListener mListener; public enum Mode { NORMAL,FAST; } - public AutomaticFrequencyControl( FrequencyChangeListener listener, - int maximumCorrection ) + /** + * Automatic Frequency Control. Monitors DC bias present in an FM + * demodulated audio stream and issues automatic frequency corrections to + * maintain the DC bias close to zero. + * + * @param maximum - maximum allowable (+/-) frequency correction value. + */ + public AutomaticFrequencyControl( int maximum ) { - mListener = listener; - mMaximumCorrection = maximumCorrection; + super( maximum ); } public void dispose() { - mListener = null; + super.dispose(); + mBuffer = null; } - - public int getErrorCorrection() - { - return mErrorCorrection; - } - + + /** + * Resets the error correction value to zero and broadcasts a change event + */ public void reset() { mErrorCorrection = 0; mMode = Mode.FAST; dispatch(); } - + + /** + * Broadcasts a frequency correction change event to the registered listener + */ private void dispatch() { if( mListener != null ) @@ -119,86 +139,43 @@ public class AutomaticFrequencyControl implements RealSampleListener, private void update( float average ) { - int correction = 0; - + mMode = Mode.NORMAL; + if( average > LARGE_ERROR ) { - correction = -LARGE_FREQUENCY_CORRECTION; + adjust( -LARGE_FREQUENCY_CORRECTION ); mMode = Mode.FAST; } else if( average > MEDIUM_ERROR ) { - correction = -MEDIUM_FREQUENCY_CORRECTION; + adjust( -MEDIUM_FREQUENCY_CORRECTION ); mMode = Mode.FAST; } else if( average > SMALL_ERROR ) { - correction = -SMALL_FREQUENCY_CORRECTION; - mMode = Mode.NORMAL; + adjust( -SMALL_FREQUENCY_CORRECTION ); } else if( average > FINE_ERROR ) { - correction = -FINE_FREQUENCY_CORRECTION; - mMode = Mode.NORMAL; + adjust( -FINE_FREQUENCY_CORRECTION ); } else if( average < -LARGE_ERROR ) { - correction = LARGE_FREQUENCY_CORRECTION; + adjust( LARGE_FREQUENCY_CORRECTION ); mMode = Mode.FAST; } else if( average < -MEDIUM_ERROR ) { - correction = MEDIUM_FREQUENCY_CORRECTION; + adjust( MEDIUM_FREQUENCY_CORRECTION ); mMode = Mode.FAST; } else if( average < -SMALL_ERROR ) { - correction = SMALL_FREQUENCY_CORRECTION; - mMode = Mode.NORMAL; + adjust( SMALL_FREQUENCY_CORRECTION ); } else if( average < -FINE_ERROR ) { - correction = FINE_FREQUENCY_CORRECTION; - mMode = Mode.NORMAL; - } - - if( correction != 0 ) - { - mErrorCorrection -= correction; - - if( Math.abs( mErrorCorrection ) > mMaximumCorrection ) - { - mErrorCorrection = ( mErrorCorrection < 0 ) ? - -mMaximumCorrection : - mMaximumCorrection; - } - - dispatch(); - } - else - { - mMode = Mode.NORMAL; + adjust( FINE_FREQUENCY_CORRECTION ); } } - - @Override - public void frequencyChanged( FrequencyChangeEvent event ) - { - switch( event.getAttribute() ) - { - case FREQUENCY: - int frequency = (int)event.getValue(); - - if( mCurrentFrequency != frequency ) - { - mCurrentFrequency = frequency; - - reset(); - } - case SAMPLE_RATE_ERROR: - break; - default: - break; - } - } } diff --git a/src/source/tuner/FrequencyChangeBroadcaster.java b/src/source/tuner/frequency/FrequencyChangeBroadcaster.java similarity index 97% rename from src/source/tuner/FrequencyChangeBroadcaster.java rename to src/source/tuner/frequency/FrequencyChangeBroadcaster.java index 467f8a32..89c03bf6 100644 --- a/src/source/tuner/FrequencyChangeBroadcaster.java +++ b/src/source/tuner/frequency/FrequencyChangeBroadcaster.java @@ -15,7 +15,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package source.tuner; +package source.tuner.frequency; + public interface FrequencyChangeBroadcaster { diff --git a/src/source/tuner/FrequencyChangeEvent.java b/src/source/tuner/frequency/FrequencyChangeEvent.java similarity index 68% rename from src/source/tuner/FrequencyChangeEvent.java rename to src/source/tuner/frequency/FrequencyChangeEvent.java index b2dad23c..0ab125bc 100644 --- a/src/source/tuner/FrequencyChangeEvent.java +++ b/src/source/tuner/frequency/FrequencyChangeEvent.java @@ -1,4 +1,4 @@ -package source.tuner; +package source.tuner.frequency; public class FrequencyChangeEvent { @@ -12,9 +12,9 @@ public class FrequencyChangeEvent } private Attribute mAttribute; - private long mValue; + private Number mValue; - public FrequencyChangeEvent( Attribute attribute, long value ) + public FrequencyChangeEvent( Attribute attribute, Number value ) { mAttribute = attribute; mValue = value; @@ -25,10 +25,8 @@ public class FrequencyChangeEvent return mAttribute; } - public long getValue() + public Number getValue() { return mValue; } - - } diff --git a/src/source/tuner/FrequencyChangeListener.java b/src/source/tuner/frequency/FrequencyChangeListener.java similarity index 97% rename from src/source/tuner/FrequencyChangeListener.java rename to src/source/tuner/frequency/FrequencyChangeListener.java index 3aee68de..fab9f28a 100644 --- a/src/source/tuner/FrequencyChangeListener.java +++ b/src/source/tuner/frequency/FrequencyChangeListener.java @@ -15,7 +15,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package source.tuner; +package source.tuner.frequency; + /** * Interface for broadcasting changes to frequency, bandwidth, sample rate, diff --git a/src/source/tuner/FrequencyController.java b/src/source/tuner/frequency/FrequencyController.java similarity index 93% rename from src/source/tuner/FrequencyController.java rename to src/source/tuner/frequency/FrequencyController.java index ccd1d2e7..53371832 100644 --- a/src/source/tuner/FrequencyController.java +++ b/src/source/tuner/frequency/FrequencyController.java @@ -15,15 +15,20 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package source.tuner; +package source.tuner.frequency; import java.util.ArrayList; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import source.SourceException; -import source.tuner.FrequencyChangeEvent.Attribute; +import source.tuner.frequency.FrequencyChangeEvent.Attribute; public class FrequencyController { + private final static Logger mLog = LoggerFactory.getLogger( FrequencyController.class ); + private Tunable mTunable; private long mFrequency = 101100000; private long mTunedFrequency = 101100000; @@ -167,6 +172,10 @@ public class FrequencyController /* Reset the frequency, but don't broadcast a change */ setFrequency( mFrequency, false ); } + + broadcastFrequencyChangeEvent( new FrequencyChangeEvent( + Attribute.SAMPLE_RATE_ERROR, mFrequencyCorrection ) ); + } /** diff --git a/src/source/tuner/frequency/FrequencyCorrectionControl.java b/src/source/tuner/frequency/FrequencyCorrectionControl.java new file mode 100644 index 00000000..a2853c67 --- /dev/null +++ b/src/source/tuner/frequency/FrequencyCorrectionControl.java @@ -0,0 +1,164 @@ +package source.tuner.frequency; + +/******************************************************************************* + * 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 + ******************************************************************************/ +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import source.tuner.frequency.FrequencyChangeEvent.Attribute; + +/** + * Frequency control for providing frequency error correction to a tuner channel + */ +public class FrequencyCorrectionControl implements FrequencyChangeListener +{ + private final static Logger mLog = + LoggerFactory.getLogger( FrequencyCorrectionControl.class ); + + protected FrequencyChangeListener mListener; + protected FrequencyCorrectionResetListener mResetListener; + + protected int mErrorCorrection = 0; + private int mMaximumCorrection = 3000; + + /** + * Frequency correction controller. + * + * @param maximum - defines the maximum +/- allowable frequency correction + * value + */ + public FrequencyCorrectionControl( int maximum ) + { + mMaximumCorrection = maximum; + } + + /** + * Cleanup + */ + public void dispose() + { + mListener = null; + mResetListener = null; + } + + /** + * Registers a listener to receive frequency correction events + */ + public void setListener( FrequencyChangeListener listener ) + { + mListener = listener; + } + + /** + * Registers a listener to receive frequency correction resets + */ + public void setListener( FrequencyCorrectionResetListener listener ) + { + mResetListener = listener; + } + + /** + * Listener for source frequency or frequency offset value changes to + * reset this frequency controller value to zero. + */ + @Override + public void frequencyChanged( FrequencyChangeEvent event ) + { + Attribute attribute = event.getAttribute(); + + if( attribute == Attribute.FREQUENCY || + attribute == Attribute.SAMPLE_RATE_ERROR ) + { + reset(); + } + } + + /** + * Resets frequency correction value to zero and notifies any reset + * listeners + */ + protected void reset() + { + setErrorCorrection( 0, false ); + + if( mResetListener != null ) + { + mResetListener.resetFrequencyCorrection(); + } + } + + /** + * Sets frequency correction to the specified value and broadcasts a change + */ + public void setErrorCorrection( int correction ) + { + setErrorCorrection( correction, true ); + } + + /** + * Sets frequency correction to the specified value + */ + public void setErrorCorrection( int correction, boolean broadcast ) + { + mErrorCorrection = correction; + + /* Limit frequency correction to +/- max correction value */ + if( mErrorCorrection > mMaximumCorrection ) + { + mErrorCorrection = mMaximumCorrection; + } + else if( mErrorCorrection < -mMaximumCorrection ) + { + mErrorCorrection = -mMaximumCorrection; + } + + /* Broadcast a change event */ + if( broadcast && mListener != null ) + { + mListener.frequencyChanged( + new FrequencyChangeEvent( Attribute.FREQUENCY_ERROR, + mErrorCorrection ) ); + } + } + + /** + * Increments the frequency error correction value by the specified adjustment + */ + public void adjust( int adjustment ) + { + setErrorCorrection( getErrorCorrection() + adjustment ); + } + + /** + * Current frequency error correction value + * @return + */ + public int getErrorCorrection() + { + return mErrorCorrection; + } + + /** + * Listener interface to be notified when this control is reset to a zero + * frequency correction value + */ + public interface FrequencyCorrectionResetListener + { + public void resetFrequencyCorrection(); + } +} diff --git a/src/source/tuner/FrequencyLimitException.java b/src/source/tuner/frequency/FrequencyLimitException.java similarity index 96% rename from src/source/tuner/FrequencyLimitException.java rename to src/source/tuner/frequency/FrequencyLimitException.java index 63778b5a..be72a74e 100644 --- a/src/source/tuner/FrequencyLimitException.java +++ b/src/source/tuner/frequency/FrequencyLimitException.java @@ -1,4 +1,4 @@ -package source.tuner; +package source.tuner.frequency; public class FrequencyLimitException extends Exception { diff --git a/src/source/tuner/hackrf/HackRFTunerEditorPanel.java b/src/source/tuner/hackrf/HackRFTunerEditorPanel.java index 5f4dec78..a594eab7 100644 --- a/src/source/tuner/hackrf/HackRFTunerEditorPanel.java +++ b/src/source/tuner/hackrf/HackRFTunerEditorPanel.java @@ -31,9 +31,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import source.SourceException; -import source.tuner.FrequencyChangeEvent; -import source.tuner.FrequencyChangeEvent.Attribute; -import source.tuner.FrequencyChangeListener; +import source.tuner.frequency.FrequencyChangeEvent; +import source.tuner.frequency.FrequencyChangeListener; +import source.tuner.frequency.FrequencyChangeEvent.Attribute; import source.tuner.hackrf.HackRFTunerController.BoardID; import com.jidesoft.swing.JideTabbedPane; @@ -114,11 +114,11 @@ public class HackRFTunerEditorPanel extends JPanel { try { - mController.setFrequency( event.getValue() ); + mController.setFrequency( event.getValue().longValue() ); } catch ( SourceException e ) { - mLog.error( "error setting frequency [" + event.getValue() + "]", e ); + mLog.error( "error setting frequency [" + event.getValue().longValue() + "]", e ); } } } diff --git a/src/source/tuner/rtl/RTL2832Tuner.java b/src/source/tuner/rtl/RTL2832Tuner.java index 7f421b7b..2f82e760 100644 --- a/src/source/tuner/rtl/RTL2832Tuner.java +++ b/src/source/tuner/rtl/RTL2832Tuner.java @@ -28,13 +28,13 @@ import org.usb4java.LibUsbException; import sample.Listener; import sample.complex.ComplexBuffer; import source.SourceException; -import source.tuner.FrequencyChangeListener; import source.tuner.Tuner; import source.tuner.TunerChannel; import source.tuner.TunerChannelSource; import source.tuner.TunerClass; import source.tuner.TunerConfiguration; import source.tuner.TunerType; +import source.tuner.frequency.FrequencyChangeListener; import source.tuner.rtl.RTL2832TunerController.SampleRate; import controller.ResourceManager; import controller.ThreadPoolManager; diff --git a/src/source/tuner/rtl/RTL2832TunerController.java b/src/source/tuner/rtl/RTL2832TunerController.java index 91db8979..acffcde3 100644 --- a/src/source/tuner/rtl/RTL2832TunerController.java +++ b/src/source/tuner/rtl/RTL2832TunerController.java @@ -55,8 +55,8 @@ import sample.Listener; import sample.adapter.ByteSampleAdapter; import sample.complex.ComplexBuffer; import source.SourceException; -import source.tuner.FrequencyChangeEvent; -import source.tuner.FrequencyChangeEvent.Attribute; +import source.tuner.frequency.FrequencyChangeEvent; +import source.tuner.frequency.FrequencyChangeEvent.Attribute; import source.tuner.TunerController; import source.tuner.TunerType; import buffer.FloatAveragingBuffer; diff --git a/src/source/tuner/rtl/e4k/E4KTunerEditorPanel.java b/src/source/tuner/rtl/e4k/E4KTunerEditorPanel.java index ed042e5f..fcb6f72c 100644 --- a/src/source/tuner/rtl/e4k/E4KTunerEditorPanel.java +++ b/src/source/tuner/rtl/e4k/E4KTunerEditorPanel.java @@ -31,9 +31,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import source.SourceException; -import source.tuner.FrequencyChangeEvent; -import source.tuner.FrequencyChangeListener; -import source.tuner.FrequencyChangeEvent.Attribute; +import source.tuner.frequency.FrequencyChangeEvent; +import source.tuner.frequency.FrequencyChangeListener; +import source.tuner.frequency.FrequencyChangeEvent.Attribute; import source.tuner.rtl.RTL2832InfoPanel; import com.jidesoft.swing.JideTabbedPane; @@ -100,7 +100,7 @@ public class E4KTunerEditorPanel extends JPanel implements FrequencyChangeListen { if( event.getAttribute() == Attribute.FREQUENCY ) { - final int frequency = (int)event.getValue(); + final long frequency = event.getValue().longValue(); EventQueue.invokeLater( new Runnable() { diff --git a/src/source/tuner/rtl/r820t/R820TTunerEditorPanel.java b/src/source/tuner/rtl/r820t/R820TTunerEditorPanel.java index b4cc9bc7..36bde408 100644 --- a/src/source/tuner/rtl/r820t/R820TTunerEditorPanel.java +++ b/src/source/tuner/rtl/r820t/R820TTunerEditorPanel.java @@ -31,9 +31,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import source.SourceException; -import source.tuner.FrequencyChangeEvent; -import source.tuner.FrequencyChangeListener; -import source.tuner.FrequencyChangeEvent.Attribute; +import source.tuner.frequency.FrequencyChangeEvent; +import source.tuner.frequency.FrequencyChangeListener; +import source.tuner.frequency.FrequencyChangeEvent.Attribute; import source.tuner.rtl.RTL2832InfoPanel; import com.jidesoft.swing.JideTabbedPane; @@ -99,7 +99,7 @@ public class R820TTunerEditorPanel extends JPanel implements FrequencyChangeList { if( event.getAttribute() == Attribute.FREQUENCY ) { - final int frequency = (int)event.getValue(); + final long frequency = event.getValue().longValue(); EventQueue.invokeLater( new Runnable() { diff --git a/src/spectrum/ChannelSpectrumPanel.java b/src/spectrum/ChannelSpectrumPanel.java index eb9e5e98..e2f1cc32 100644 --- a/src/spectrum/ChannelSpectrumPanel.java +++ b/src/spectrum/ChannelSpectrumPanel.java @@ -25,8 +25,8 @@ import settings.Setting; import settings.SettingChangeListener; import settings.SettingsManager; import source.Source.SampleType; -import source.tuner.FrequencyChangeEvent; -import source.tuner.FrequencyChangeEvent.Attribute; +import source.tuner.frequency.FrequencyChangeEvent; +import source.tuner.frequency.FrequencyChangeEvent.Attribute; import spectrum.converter.DFTResultsConverter; import spectrum.converter.RealDecibelConverter; import spectrum.menu.AveragingItem; diff --git a/src/spectrum/DFTProcessor.java b/src/spectrum/DFTProcessor.java index d445dc57..6df2e493 100644 --- a/src/spectrum/DFTProcessor.java +++ b/src/spectrum/DFTProcessor.java @@ -34,8 +34,8 @@ import sample.Listener; import sample.complex.ComplexBuffer; import source.Source; import source.Source.SampleType; -import source.tuner.FrequencyChangeEvent; -import source.tuner.FrequencyChangeListener; +import source.tuner.frequency.FrequencyChangeEvent; +import source.tuner.frequency.FrequencyChangeListener; import spectrum.converter.DFTResultsConverter; import dsp.filter.Window; import dsp.filter.Window.WindowType; @@ -460,7 +460,7 @@ public class DFTProcessor implements Listener, case SAMPLE_RATE_ERROR: break; case SAMPLE_RATE: - mSampleRate = (int)event.getValue(); + mSampleRate = event.getValue().intValue(); calculateConsumptionRate(); break; default: diff --git a/src/spectrum/OverlayPanel.java b/src/spectrum/OverlayPanel.java index 1db379c7..1ffd2835 100644 --- a/src/spectrum/OverlayPanel.java +++ b/src/spectrum/OverlayPanel.java @@ -41,9 +41,9 @@ import settings.ColorSetting; import settings.ColorSetting.ColorSettingName; import settings.Setting; import settings.SettingChangeListener; -import source.tuner.FrequencyChangeEvent; -import source.tuner.FrequencyChangeListener; import source.tuner.TunerChannel; +import source.tuner.frequency.FrequencyChangeEvent; +import source.tuner.frequency.FrequencyChangeListener; import controller.ResourceManager; import controller.channel.Channel; import controller.channel.Channel.ChannelType; @@ -568,7 +568,7 @@ public class OverlayPanel extends JPanel switch( event.getAttribute() ) { case SAMPLE_RATE: - mBandwidth = (int)event.getValue(); + mBandwidth = event.getValue().intValue(); if( mBandwidth < 200000 ) { @@ -580,7 +580,7 @@ public class OverlayPanel extends JPanel } break; case FREQUENCY: - mFrequency = event.getValue(); + mFrequency = event.getValue().longValue(); break; default: break; @@ -798,8 +798,8 @@ public class OverlayPanel extends JPanel switch( event.getAttribute() ) { case FREQUENCY: - LabelDisplay display = - LabelDisplay.fromFrequency( event.getValue() ); + LabelDisplay display = LabelDisplay + .fromFrequency( event.getValue().longValue() ); if( mLabelDisplay != display ) { diff --git a/src/spectrum/SpectralDisplayPanel.java b/src/spectrum/SpectralDisplayPanel.java index 9417d629..f31d621e 100644 --- a/src/spectrum/SpectralDisplayPanel.java +++ b/src/spectrum/SpectralDisplayPanel.java @@ -56,9 +56,9 @@ import settings.SettingChangeListener; import settings.SettingsManager; import source.Source.SampleType; import source.SourceException; -import source.tuner.FrequencyChangeEvent; -import source.tuner.FrequencyChangeEvent.Attribute; -import source.tuner.FrequencyChangeListener; +import source.tuner.frequency.FrequencyChangeEvent; +import source.tuner.frequency.FrequencyChangeListener; +import source.tuner.frequency.FrequencyChangeEvent.Attribute; import source.tuner.Tuner; import source.tuner.TunerSelectionListener; import spectrum.OverlayPanel.ChannelDisplay;