mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 08:37:25 -05:00
P25 - Automatic Gain Control for P25 decoder
This commit is contained in:
@@ -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 } )
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ public enum DecoderType
|
||||
DecoderType.LTR_STANDARD,
|
||||
DecoderType.MPT1327,
|
||||
DecoderType.NBFM,
|
||||
// DecoderType.P25_PHASE1,
|
||||
DecoderType.P25_PHASE1,
|
||||
DecoderType.PASSPORT );
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package dsp.afc;
|
||||
package dsp.frequency;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package dsp.agc;
|
||||
package dsp.gain;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
95
src/dsp/gain/DirectGainControl.java
Normal file
95
src/dsp/gain/DirectGainControl.java
Normal 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;
|
||||
}
|
||||
}
|
||||
8
src/dsp/gain/GainController.java
Normal file
8
src/dsp/gain/GainController.java
Normal file
@@ -0,0 +1,8 @@
|
||||
package dsp.gain;
|
||||
|
||||
public interface GainController
|
||||
{
|
||||
public abstract void increase();
|
||||
|
||||
public abstract void decrease();
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package dsp.agc;
|
||||
package dsp.gain;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user