diff --git a/src/controller/channel/Channel.java b/src/controller/channel/Channel.java index 32e8ca59..1927dfd1 100644 --- a/src/controller/channel/Channel.java +++ b/src/controller/channel/Channel.java @@ -141,24 +141,21 @@ public class Channel extends Configuration } /** - * Indicates if the channel is using Automatic Frequency Control - * @return + * Indicates if the channel has Automatic or Direct Frequency Control */ - public boolean hasAFC() + public boolean hasFrequencyControl() { - return getAFC() != null; + return mProcessingChain != null && mProcessingChain.hasFrequencyControl(); } - public AutomaticFrequencyControl getAFC() + public long getFrequencyCorrection() { - if( isProcessing() ) + if( mProcessingChain != null ) { - return getProcessingChain().getAFC(); - } - else - { - return null; + return mProcessingChain.getFrequencyCorrection(); } + + return 0; } /** diff --git a/src/controller/channel/ProcessingChain.java b/src/controller/channel/ProcessingChain.java index ba4a2476..8e15a605 100644 --- a/src/controller/channel/ProcessingChain.java +++ b/src/controller/channel/ProcessingChain.java @@ -46,6 +46,8 @@ 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 alias.AliasList; @@ -109,11 +111,28 @@ public class ProcessingChain implements Listener } /** - * Automatic Frequency Control, or null if disabled + * Current frequency correction setting */ - public AutomaticFrequencyControl getAFC() + public long getFrequencyCorrection() { - return mAFC; + 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() @@ -370,23 +389,31 @@ public class ProcessingChain implements Listener mAFC = null; } - if( mSource instanceof TunerChannelSource && - mChannel.getDecodeConfiguration().supportsAFC() && - mChannel.getDecodeConfiguration().isAFCEnabled() ) + if( mSource instanceof TunerChannelSource ) { TunerChannelSource tcs = (TunerChannelSource)mSource; - int maximumCorrection = mChannel - .getDecodeConfiguration().getAFCMaximumCorrection(); - - mAFC = new AutomaticFrequencyControl( tcs, - maximumCorrection ); + if( mChannel.getDecodeConfiguration().supportsAFC() && + mChannel.getDecodeConfiguration().isAFCEnabled() ) + { + + int maximumCorrection = mChannel + .getDecodeConfiguration().getAFCMaximumCorrection(); + + mAFC = new AutomaticFrequencyControl( tcs, + maximumCorrection ); - /* Register AFC to receive frequency change events */ - tcs.addListener( mAFC ); + /* Register AFC to receive frequency change events */ + tcs.addListener( mAFC ); - /* Add AFC as listener to the decoder */ - mDecoder.addUnfilteredRealSampleListener( mAFC ); + /* Add AFC as listener to the decoder */ + mDecoder.addUnfilteredRealSampleListener( mAFC ); + } + else if( mDecoder instanceof DirectFrequencyController ) + { + ((DirectFrequencyController)mDecoder) + .setListener( (FrequencyChangeListener)tcs ); + } } } } diff --git a/src/decode/p25/P25Decoder.java b/src/decode/p25/P25Decoder.java index 5559743e..1277b7cb 100644 --- a/src/decode/p25/P25Decoder.java +++ b/src/decode/p25/P25Decoder.java @@ -30,6 +30,8 @@ import org.slf4j.LoggerFactory; import sample.real.RealSampleListener; import source.Source.SampleType; +import source.tuner.DirectFrequencyController; +import source.tuner.FrequencyChangeListener; import alias.AliasList; import decode.Decoder; import decode.DecoderType; @@ -42,7 +44,8 @@ import dsp.fsk.C4FMSymbolFilter; import dsp.fsk.P25MessageFramer; import dsp.nbfm.FMDiscriminator; -public class P25Decoder extends Decoder implements Instrumentable +public class P25Decoder extends Decoder + implements DirectFrequencyController, Instrumentable { private final static Logger mLog = LoggerFactory.getLogger( P25Decoder.class ); @@ -196,4 +199,20 @@ public class P25Decoder extends Decoder implements Instrumentable throw new IllegalArgumentException( "unfiltered real sample provider " + "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 ) + { + mSymbolFilter.addListener( listener ); + } + + @Override + public long getFrequencyCorrection() + { + return mSymbolFilter.getFrequencyCorrection(); + } } diff --git a/src/decode/p25/message/pdu/PDUConfirmedMessage.java b/src/decode/p25/message/pdu/PDUConfirmedMessage.java index 4e0afeac..f2cc67be 100644 --- a/src/decode/p25/message/pdu/PDUConfirmedMessage.java +++ b/src/decode/p25/message/pdu/PDUConfirmedMessage.java @@ -117,8 +117,24 @@ public class PDUConfirmedMessage extends PDUMessage { /* 16 octets per block, and 12 octets in final block */ int count = getBlocksToFollowCount() * 16 - 4; + + int padding = getPadOctetCount(); + + /* Hack: motorola sndcp packet data control - when it says there are 15 + * pad octets and only 1 data block of 12 data octets, that means there + * are more pad octets than available data and they appear to suppress + * the final padded data block containing the 12 pad octets. The data + * block count should be 2, but it says 1, so they're either not sending + * the final block, or there is an error in the block count */ + if( padding > count ) + { + count -= ( padding - 12 ); + } + else + { + count -= padding; + } - count -= getPadOctetCount(); count -= getDataHeaderOffset(); diff --git a/src/dsp/fsk/C4FMSymbolFilter.java b/src/dsp/fsk/C4FMSymbolFilter.java index cea6f4c3..c9078212 100644 --- a/src/dsp/fsk/C4FMSymbolFilter.java +++ b/src/dsp/fsk/C4FMSymbolFilter.java @@ -13,8 +13,13 @@ 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; -public class C4FMSymbolFilter implements RealSampleListener, +public class C4FMSymbolFilter implements FrequencyChangeBroadcaster, + RealSampleListener, RealSampleProvider, Instrumentable { @@ -180,7 +185,8 @@ public class C4FMSymbolFilter implements RealSampleListener, private static final double K_FINE_FREQUENCY = 0.125; /* Frequency correction broadcast threshold */ - private static final double COARSE_FREQUENCY_DEADBAND = 1.66; +// private static final double COARSE_FREQUENCY_DEADBAND = 1.66; + private static final double COARSE_FREQUENCY_DEADBAND = 1.20; /* 2.0 symbol spread gives -3, -1, 1, 3 */ private float mSymbolSpread = 2.0f; @@ -194,6 +200,9 @@ public class C4FMSymbolFilter implements RealSampleListener, private int mHistoryLast = 0; private RealSampleListener mListener; + + private FrequencyChangeListener mFrequencyChangeListener; + private long mFrequencyCorrection; private float mGain = 15.4f; @@ -219,6 +228,11 @@ public class C4FMSymbolFilter implements RealSampleListener, { } + public long getFrequencyCorrection() + { + return mFrequencyCorrection; + } + @Override public void receive( float sample ) { @@ -311,13 +325,24 @@ public class C4FMSymbolFilter implements RealSampleListener, mFineFrequencyCorrection += ( symbolError * K_FINE_FREQUENCY ); -// /* send frequency correction */ -// if( Math.abs( mCoarseFrequencyCorrection ) > COARSE_FREQUENCY_DEADBAND ) -// { -// mLog.debug( "Frequency Correction [" + -// ( mCoarseFrequencyCorrection > 0 ? "-" : "+" ) + -// mCoarseFrequencyCorrection + "]" ); -// } + /* send frequency correction */ + if( Math.abs( mCoarseFrequencyCorrection ) > COARSE_FREQUENCY_DEADBAND ) + { + if( mFrequencyChangeListener != null ) + { + mFrequencyCorrection += ( + 500 * ( mCoarseFrequencyCorrection > 0 ? 1 : -1 ) ); + + mFrequencyChangeListener.frequencyChanged( + new FrequencyChangeEvent( Attribute.FREQUENCY_ERROR, + mFrequencyCorrection ) ); + + /* reset internal frequency error tracker */ + mCoarseFrequencyCorrection = 0; + + mLog.debug( "Frequency Corrected: " + mFrequencyCorrection ); + } + } if( mSymbolSpreadTap != null ) { @@ -343,7 +368,7 @@ public class C4FMSymbolFilter implements RealSampleListener, { mListener = null; } - + @Override public List getTaps() { @@ -379,4 +404,16 @@ public class C4FMSymbolFilter implements RealSampleListener, mSymbolSpreadTap = null; } } + + @Override + public void addListener( FrequencyChangeListener listener ) + { + mFrequencyChangeListener = listener; + } + + @Override + public void removeListener( FrequencyChangeListener listener ) + { + mFrequencyChangeListener = null; + } } diff --git a/src/source/tuner/DirectFrequencyController.java b/src/source/tuner/DirectFrequencyController.java new file mode 100644 index 00000000..ad6cf9e3 --- /dev/null +++ b/src/source/tuner/DirectFrequencyController.java @@ -0,0 +1,9 @@ +package source.tuner; + + +public interface DirectFrequencyController +{ + public void setListener( FrequencyChangeListener listener ); + + public long getFrequencyCorrection(); +} diff --git a/src/spectrum/OverlayPanel.java b/src/spectrum/OverlayPanel.java index 1083623c..1db379c7 100644 --- a/src/spectrum/OverlayPanel.java +++ b/src/spectrum/OverlayPanel.java @@ -500,11 +500,11 @@ public class OverlayPanel extends JPanel } /* Draw Automatic Frequency Control line */ - if( channel.hasAFC() ) + if( channel.hasFrequencyControl() ) { - int frequency = (int)tunerChannel.getFrequency(); + long frequency = tunerChannel.getFrequency(); - int error = frequency + channel.getAFC().getErrorCorrection(); + long error = frequency + channel.getFrequencyCorrection(); drawAFC( graphics, getAxisFromFrequency( frequency ), false );