mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-15 01:57:39 -05:00
P25 - adds direct frequency control for the C4FM symbol filter to adjust the tuner channel for frequency correction.
This commit is contained in:
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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,
|
int maximumCorrection = mChannel
|
||||||
maximumCorrection );
|
.getDecodeConfiguration().getAFCMaximumCorrection();
|
||||||
|
|
||||||
/* Register AFC to receive frequency change events */
|
mAFC = new AutomaticFrequencyControl( tcs,
|
||||||
tcs.addListener( mAFC );
|
maximumCorrection );
|
||||||
|
|
||||||
/* Add AFC as listener to the decoder */
|
/* Register AFC to receive frequency change events */
|
||||||
mDecoder.addUnfilteredRealSampleListener( mAFC );
|
tcs.addListener( mAFC );
|
||||||
|
|
||||||
|
/* Add AFC as listener to the decoder */
|
||||||
|
mDecoder.addUnfilteredRealSampleListener( mAFC );
|
||||||
|
}
|
||||||
|
else if( mDecoder instanceof DirectFrequencyController )
|
||||||
|
{
|
||||||
|
((DirectFrequencyController)mDecoder)
|
||||||
|
.setListener( (FrequencyChangeListener)tcs );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -118,7 +118,23 @@ 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;
|
||||||
|
|
||||||
count -= getPadOctetCount();
|
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 -= getDataHeaderOffset();
|
count -= getDataHeaderOffset();
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -195,6 +201,9 @@ public class C4FMSymbolFilter implements RealSampleListener,
|
|||||||
|
|
||||||
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 )
|
||||||
{
|
{
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
9
src/source/tuner/DirectFrequencyController.java
Normal file
9
src/source/tuner/DirectFrequencyController.java
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package source.tuner;
|
||||||
|
|
||||||
|
|
||||||
|
public interface DirectFrequencyController
|
||||||
|
{
|
||||||
|
public void setListener( FrequencyChangeListener listener );
|
||||||
|
|
||||||
|
public long getFrequencyCorrection();
|
||||||
|
}
|
||||||
@@ -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 );
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user