P25 - adds direct frequency control for the C4FM symbol filter to adjust the tuner channel for frequency correction.

This commit is contained in:
dennis.sheirer
2015-01-19 16:20:37 +00:00
parent cc5edd4790
commit 1c0da75c01
7 changed files with 146 additions and 41 deletions

View File

@@ -141,24 +141,21 @@ public class Channel extends Configuration
} }
/** /**
* Indicates if the channel is using Automatic Frequency Control * Indicates if the channel has Automatic or Direct Frequency Control
* @return
*/ */
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(); return mProcessingChain.getFrequencyCorrection();
}
else
{
return null;
} }
return 0;
} }
/** /**

View File

@@ -46,6 +46,8 @@ import source.RealSource;
import source.Source; import source.Source;
import source.SourceException; import source.SourceException;
import source.config.SourceConfigMixer; import source.config.SourceConfigMixer;
import source.tuner.DirectFrequencyController;
import source.tuner.FrequencyChangeListener;
import source.tuner.Tuner; import source.tuner.Tuner;
import source.tuner.TunerChannelSource; import source.tuner.TunerChannelSource;
import alias.AliasList; import alias.AliasList;
@@ -109,11 +111,28 @@ public class ProcessingChain implements Listener<Message>
} }
/** /**
* 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<List<ComplexSample>> getComplexReceiver() public Listener<List<ComplexSample>> getComplexReceiver()
@@ -370,23 +389,31 @@ public class ProcessingChain implements Listener<Message>
mAFC = null; mAFC = null;
} }
if( mSource instanceof TunerChannelSource && if( mSource instanceof TunerChannelSource )
mChannel.getDecodeConfiguration().supportsAFC() &&
mChannel.getDecodeConfiguration().isAFCEnabled() )
{ {
TunerChannelSource tcs = (TunerChannelSource)mSource; TunerChannelSource tcs = (TunerChannelSource)mSource;
int maximumCorrection = mChannel if( mChannel.getDecodeConfiguration().supportsAFC() &&
.getDecodeConfiguration().getAFCMaximumCorrection(); mChannel.getDecodeConfiguration().isAFCEnabled() )
{
mAFC = new AutomaticFrequencyControl( tcs,
maximumCorrection ); int maximumCorrection = mChannel
.getDecodeConfiguration().getAFCMaximumCorrection();
mAFC = new AutomaticFrequencyControl( tcs,
maximumCorrection );
/* Register AFC to receive frequency change events */ /* Register AFC to receive frequency change events */
tcs.addListener( mAFC ); tcs.addListener( mAFC );
/* Add AFC as listener to the decoder */ /* Add AFC as listener to the decoder */
mDecoder.addUnfilteredRealSampleListener( mAFC ); mDecoder.addUnfilteredRealSampleListener( mAFC );
}
else if( mDecoder instanceof DirectFrequencyController )
{
((DirectFrequencyController)mDecoder)
.setListener( (FrequencyChangeListener)tcs );
}
} }
} }
} }

View File

@@ -30,6 +30,8 @@ import org.slf4j.LoggerFactory;
import sample.real.RealSampleListener; import sample.real.RealSampleListener;
import source.Source.SampleType; import source.Source.SampleType;
import source.tuner.DirectFrequencyController;
import source.tuner.FrequencyChangeListener;
import alias.AliasList; import alias.AliasList;
import decode.Decoder; import decode.Decoder;
import decode.DecoderType; import decode.DecoderType;
@@ -42,7 +44,8 @@ import dsp.fsk.C4FMSymbolFilter;
import dsp.fsk.P25MessageFramer; import dsp.fsk.P25MessageFramer;
import dsp.nbfm.FMDiscriminator; 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 ); 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 " throw new IllegalArgumentException( "unfiltered real sample provider "
+ "not implemented in P25 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 )
{
mSymbolFilter.addListener( listener );
}
@Override
public long getFrequencyCorrection()
{
return mSymbolFilter.getFrequencyCorrection();
}
} }

View File

@@ -117,8 +117,24 @@ public class PDUConfirmedMessage extends PDUMessage
{ {
/* 16 octets per block, and 12 octets in final block */ /* 16 octets per block, and 12 octets in final block */
int count = getBlocksToFollowCount() * 16 - 4; 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(); count -= getDataHeaderOffset();

View File

@@ -13,8 +13,13 @@ import org.slf4j.LoggerFactory;
import sample.real.RealSampleListener; import sample.real.RealSampleListener;
import sample.real.RealSampleProvider; 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, RealSampleProvider,
Instrumentable Instrumentable
{ {
@@ -180,7 +185,8 @@ public class C4FMSymbolFilter implements RealSampleListener,
private static final double K_FINE_FREQUENCY = 0.125; private static final double K_FINE_FREQUENCY = 0.125;
/* Frequency correction broadcast threshold */ /* 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 */ /* 2.0 symbol spread gives -3, -1, 1, 3 */
private float mSymbolSpread = 2.0f; private float mSymbolSpread = 2.0f;
@@ -194,6 +200,9 @@ public class C4FMSymbolFilter implements RealSampleListener,
private int mHistoryLast = 0; private int mHistoryLast = 0;
private RealSampleListener mListener; private RealSampleListener mListener;
private FrequencyChangeListener mFrequencyChangeListener;
private long mFrequencyCorrection;
private float mGain = 15.4f; private float mGain = 15.4f;
@@ -219,6 +228,11 @@ public class C4FMSymbolFilter implements RealSampleListener,
{ {
} }
public long getFrequencyCorrection()
{
return mFrequencyCorrection;
}
@Override @Override
public void receive( float sample ) public void receive( float sample )
{ {
@@ -311,13 +325,24 @@ public class C4FMSymbolFilter implements RealSampleListener,
mFineFrequencyCorrection += ( symbolError * K_FINE_FREQUENCY ); mFineFrequencyCorrection += ( symbolError * K_FINE_FREQUENCY );
// /* send frequency correction */ /* send frequency correction */
// if( Math.abs( mCoarseFrequencyCorrection ) > COARSE_FREQUENCY_DEADBAND ) if( Math.abs( mCoarseFrequencyCorrection ) > COARSE_FREQUENCY_DEADBAND )
// { {
// mLog.debug( "Frequency Correction [" + if( mFrequencyChangeListener != null )
// ( mCoarseFrequencyCorrection > 0 ? "-" : "+" ) + {
// mCoarseFrequencyCorrection + "]" ); 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 ) if( mSymbolSpreadTap != null )
{ {
@@ -343,7 +368,7 @@ public class C4FMSymbolFilter implements RealSampleListener,
{ {
mListener = null; mListener = null;
} }
@Override @Override
public List<Tap> getTaps() public List<Tap> getTaps()
{ {
@@ -379,4 +404,16 @@ public class C4FMSymbolFilter implements RealSampleListener,
mSymbolSpreadTap = null; mSymbolSpreadTap = null;
} }
} }
@Override
public void addListener( FrequencyChangeListener listener )
{
mFrequencyChangeListener = listener;
}
@Override
public void removeListener( FrequencyChangeListener listener )
{
mFrequencyChangeListener = null;
}
} }

View File

@@ -0,0 +1,9 @@
package source.tuner;
public interface DirectFrequencyController
{
public void setListener( FrequencyChangeListener listener );
public long getFrequencyCorrection();
}

View File

@@ -500,11 +500,11 @@ public class OverlayPanel extends JPanel
} }
/* Draw Automatic Frequency Control line */ /* 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 ); drawAFC( graphics, getAxisFromFrequency( frequency ), false );