P25 - Automatic Gain Control for P25 decoder

This commit is contained in:
dennis.sheirer
2015-01-19 19:25:43 +00:00
parent 1c0da75c01
commit e5fad7ee22
14 changed files with 154 additions and 162 deletions

View File

@@ -59,7 +59,7 @@ import controller.system.System;
import decode.config.AuxDecodeConfiguration;
import decode.config.DecodeConfigFactory;
import decode.config.DecodeConfiguration;
import dsp.afc.AutomaticFrequencyControl;
import dsp.frequency.AutomaticFrequencyControl;
import eventlog.config.EventLogConfiguration;
@XmlSeeAlso( { Configuration.class } )

View File

@@ -62,7 +62,7 @@ import controller.state.ChannelState;
import controller.state.ChannelState.State;
import decode.Decoder;
import decode.DecoderFactory;
import dsp.afc.AutomaticFrequencyControl;
import dsp.frequency.AutomaticFrequencyControl;
import eventlog.EventLogType;
import eventlog.EventLogger;

View File

@@ -68,7 +68,7 @@ public enum DecoderType
DecoderType.LTR_STANDARD,
DecoderType.MPT1327,
DecoderType.NBFM,
// DecoderType.P25_PHASE1,
DecoderType.P25_PHASE1,
DecoderType.PASSPORT );
}

View File

@@ -4,14 +4,14 @@ import sample.real.RealSampleListener;
import source.Source.SampleType;
import decode.Decoder;
import decode.DecoderType;
import dsp.agc.ComplexAutomaticGainControl;
import dsp.agc.RealAutomaticGainControl;
import dsp.am.AMDemodulator;
import dsp.filter.ComplexFIRFilter;
import dsp.filter.DCRemovalFilter2;
import dsp.filter.FilterFactory;
import dsp.filter.FloatFIRFilter;
import dsp.filter.Window.WindowType;
import dsp.gain.ComplexAutomaticGainControl;
import dsp.gain.RealAutomaticGainControl;
public class AMDecoder extends Decoder
{

View File

@@ -35,13 +35,13 @@ import source.tuner.FrequencyChangeListener;
import alias.AliasList;
import decode.Decoder;
import decode.DecoderType;
import dsp.agc.RealAutomaticGainControl;
import dsp.filter.ComplexFIRFilter;
import dsp.filter.FilterFactory;
import dsp.filter.Window.WindowType;
import dsp.fsk.C4FMSlicer;
import dsp.fsk.C4FMSymbolFilter;
import dsp.fsk.P25MessageFramer;
import dsp.gain.DirectGainControl;
import dsp.nbfm.FMDiscriminator;
public class P25Decoder extends Decoder
@@ -60,8 +60,9 @@ public class P25Decoder extends Decoder
FilterFactory.getLowPass( 48000, 5000, 31, WindowType.HAMMING ), 1.0 );
private FMDiscriminator mDemodulator = new FMDiscriminator( 1 );
private RealAutomaticGainControl mAGC = new RealAutomaticGainControl();
private C4FMSymbolFilter mSymbolFilter = new C4FMSymbolFilter();
private DirectGainControl mGainControl =
new DirectGainControl( 15.0f, 0.1f, 25.0f, 0.3f );
private C4FMSymbolFilter mSymbolFilter;
private C4FMSlicer mSlicer = new C4FMSlicer();
private P25MessageFramer mNormalFramer;
private P25MessageFramer mInvertedFramer;
@@ -74,6 +75,8 @@ public class P25Decoder extends Decoder
super( sampleType );
mAliasList = aliasList;
mSymbolFilter = new C4FMSymbolFilter( mGainControl );
/**
* Only setup a demod chain if we're receiving complex samples. If
@@ -94,8 +97,8 @@ public class P25Decoder extends Decoder
mDemodulator.setListener( getRealReceiver() );
}
addRealSampleListener( mAGC );
mAGC.setListener( mSymbolFilter );
addRealSampleListener( mGainControl );
mGainControl.setListener( mSymbolFilter );
mSymbolFilter.setListener( mSlicer );
@@ -147,13 +150,13 @@ public class P25Decoder extends Decoder
{
case INSTRUMENT_INPUT_TO_AGC:
FloatTap inputAGC = (FloatTap)tap;
removeRealListener( mAGC );
removeRealListener( mGainControl );
addRealSampleListener( inputAGC );
inputAGC.setListener( mAGC );
inputAGC.setListener( mGainControl );
break;
case INSTRUMENT_AGC_TO_SYMBOL_FILTER:
FloatTap agcSymbol = (FloatTap)tap;
mAGC.setListener( agcSymbol );
mGainControl.setListener( agcSymbol );
agcSymbol.setListener( mSymbolFilter );
break;
case INSTRUMENT_SYMBOL_FILTER_TO_SLICER:
@@ -178,10 +181,10 @@ public class P25Decoder extends Decoder
case INSTRUMENT_INPUT_TO_AGC:
FloatTap inputAGC = (FloatTap)tap;
removeRealListener( inputAGC );
addRealSampleListener( mAGC );
addRealSampleListener( mGainControl );
break;
case INSTRUMENT_AGC_TO_SYMBOL_FILTER:
mAGC.setListener( mSymbolFilter );
mGainControl.setListener( mSymbolFilter );
break;
case INSTRUMENT_SYMBOL_FILTER_TO_SLICER:
mSymbolFilter.setListener( mSlicer );

View File

@@ -1,45 +0,0 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 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 <http://www.gnu.org/licenses/>
******************************************************************************/
package dsp.filter;
import sample.Listener;
public class Gain implements Listener<Float>
{
private double mGain;
private Listener<Float> mListener;
public Gain( double gain )
{
mGain = gain;
}
@Override
public void receive( Float sample )
{
if( mListener != null )
{
mListener.receive( (float)( sample * mGain ) );
}
}
public void setListener( Listener<Float> listener )
{
mListener = listener;
}
}

View File

@@ -1,4 +1,4 @@
package dsp.afc;
package dsp.frequency;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View File

@@ -1,85 +0,0 @@
package dsp.fsk;
import instrument.Instrumentable;
import instrument.tap.Tap;
import instrument.tap.stream.FloatTap;
import java.util.ArrayList;
import java.util.List;
import sample.real.RealSampleListener;
public class C4FMDecoder implements RealSampleListener, Instrumentable
{
private static final String TAP_C4FM_INPUT = "C4FM Decoder Input";
private static final String TAP_SYMBOL_FILTER_OUTPUT = "C4FM Symbol Filter Output";
private ArrayList<Tap> mTaps;
private FloatTap mInputTap;
private FloatTap mSymbolFilterOutputTap;
private C4FMSymbolFilter mSymbolFilter = new C4FMSymbolFilter();
private RealSampleListener mInput;
public C4FMDecoder()
{
mInput = mSymbolFilter;
}
@Override
public void receive( float sample )
{
mInput.receive( sample );
}
@Override
public List<Tap> getTaps()
{
if( mTaps == null )
{
mTaps = new ArrayList<Tap>();
FloatTap floatTap = new FloatTap( TAP_C4FM_INPUT, 0, 1.0f );
mTaps.add( floatTap );
FloatTap symbolTap = new FloatTap( TAP_SYMBOL_FILTER_OUTPUT, 10, 0.1f );
mTaps.add( symbolTap );
}
return mTaps;
}
@Override
public void addTap( Tap tap )
{
switch( tap.getName() )
{
case TAP_C4FM_INPUT:
mInputTap = (FloatTap)tap;
mInput = mInputTap;
mInputTap.setListener( mSymbolFilter );
break;
case TAP_SYMBOL_FILTER_OUTPUT:
mSymbolFilterOutputTap = (FloatTap)tap;
mSymbolFilter.setListener( mSymbolFilterOutputTap );
break;
}
}
@Override
public void removeTap( Tap tap )
{
switch( tap.getName() )
{
case TAP_C4FM_INPUT:
mInput = mSymbolFilter;
mInputTap = null;
break;
case TAP_SYMBOL_FILTER_OUTPUT:
mSymbolFilter.removeListener( mSymbolFilterOutputTap );
mSymbolFilterOutputTap = null;
break;
}
}
}

View File

@@ -17,6 +17,7 @@ import source.tuner.FrequencyChangeBroadcaster;
import source.tuner.FrequencyChangeEvent;
import source.tuner.FrequencyChangeEvent.Attribute;
import source.tuner.FrequencyChangeListener;
import dsp.gain.GainController;
public class C4FMSymbolFilter implements FrequencyChangeBroadcaster,
RealSampleListener,
@@ -201,10 +202,11 @@ public class C4FMSymbolFilter implements FrequencyChangeBroadcaster,
private RealSampleListener mListener;
public static final long MAX_FREQUENCY_CORRECTION = 7000;
private FrequencyChangeListener mFrequencyChangeListener;
private long mFrequencyCorrection;
private float mGain = 15.4f;
private GainController mGainController;
/**
* C4FM Symbol Filter
@@ -219,13 +221,9 @@ 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( float gain )
{
mGain = gain;
}
public C4FMSymbolFilter()
public C4FMSymbolFilter( GainController gainController )
{
mGainController = gainController;
}
public long getFrequencyCorrection()
@@ -238,7 +236,8 @@ public class C4FMSymbolFilter implements FrequencyChangeBroadcaster,
{
mSymbolClock += mSymbolTime;
mHistory[ mHistoryLast++ ] = sample * mGain;
mHistory[ mHistoryLast++ ] = sample;
mHistoryLast %= NUMBER_FILTER_TAPS;
if( mSymbolClock > 1.0f )
@@ -317,9 +316,19 @@ public class C4FMSymbolFilter implements FrequencyChangeBroadcaster,
mSymbolClock -= symbolError * K_SYMBOL_TIMING;
}
mSymbolSpread = Math.max( mSymbolSpread, SYMBOL_SPREAD_MIN );
mSymbolSpread = Math.min( mSymbolSpread, SYMBOL_SPREAD_MAX );
if( mSymbolSpread < SYMBOL_SPREAD_MIN )
{
mGainController.increase();
mSymbolSpread = SYMBOL_SPREAD_MIN;
}
else if( mSymbolSpread > SYMBOL_SPREAD_MAX )
{
mGainController.decrease();
mSymbolSpread = SYMBOL_SPREAD_MAX;
}
mCoarseFrequencyCorrection += ( ( mFineFrequencyCorrection -
mCoarseFrequencyCorrection ) * K_COARSE_FREQUENCY );
@@ -333,14 +342,21 @@ public class C4FMSymbolFilter implements FrequencyChangeBroadcaster,
mFrequencyCorrection += (
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;
mLog.debug( "Frequency Corrected: " + mFrequencyCorrection );
}
}

View File

@@ -1,4 +1,4 @@
package dsp.agc;
package dsp.gain;
import java.util.concurrent.atomic.AtomicBoolean;

View File

@@ -0,0 +1,95 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 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 <http://www.gnu.org/licenses/>
******************************************************************************/
package dsp.gain;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.real.RealSampleListener;
public class DirectGainControl implements GainController, RealSampleListener
{
private final static Logger mLog =
LoggerFactory.getLogger( DirectGainControl.class );
private float mGain;
private float mMinimum;
private float mMaximum;
private float mIncrement;
private RealSampleListener mListener;
/**
* Gain Control.
*
* Note: does not perform null checking on output listener in order to
* streamline sample processing.
*
* @param gain - initial value
* @param minimum - minimum value greater than 0
* @param maximum - maximum permitted value
* @param increment - amount gain is adjusted when increase() or decrease()
* is invoked
*/
public DirectGainControl( float gain, float minimum, float maximum, float increment )
{
assert( minimum > 0 );
mGain = gain;
mMinimum = minimum;
mMaximum = maximum;
mIncrement = increment;
}
@Override
public void receive( float sample )
{
mListener.receive( (float)( sample * mGain ) );
}
@Override
public void increase()
{
mGain += mIncrement;
if( mGain > mMaximum )
{
mGain = mMaximum;
}
mLog.debug( "Gain Increased: " + mGain );
}
@Override
public void decrease()
{
mGain -= mIncrement;
if( mGain < mMinimum )
{
mGain = mMinimum;
}
mLog.debug( "Gain Decreased: " + mGain );
}
public void setListener( RealSampleListener listener )
{
mListener = listener;
}
}

View File

@@ -0,0 +1,8 @@
package dsp.gain;
public interface GainController
{
public abstract void increase();
public abstract void decrease();
}

View File

@@ -1,4 +1,4 @@
package dsp.agc;
package dsp.gain;
import java.util.concurrent.atomic.AtomicBoolean;

View File

@@ -29,7 +29,7 @@ import javax.sound.sampled.TargetDataLine;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import dsp.agc.RealAutomaticGainControl;
import dsp.gain.RealAutomaticGainControl;
import sample.Listener;
import sample.adapter.SampleAdapter;
import sample.real.RealBuffer;