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
* @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;
}
/**

View File

@@ -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<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()
@@ -370,23 +389,31 @@ public class ProcessingChain implements Listener<Message>
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 );
}
}
}
}

View File

@@ -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();
}
}

View File

@@ -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();

View File

@@ -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<Tap> 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;
}
}

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 */
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 );