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;