diff --git a/src/controller/channel/Channel.java b/src/controller/channel/Channel.java
index 1927dfd1..3cc95f81 100644
--- a/src/controller/channel/Channel.java
+++ b/src/controller/channel/Channel.java
@@ -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 } )
diff --git a/src/controller/channel/ProcessingChain.java b/src/controller/channel/ProcessingChain.java
index 8e15a605..ee577053 100644
--- a/src/controller/channel/ProcessingChain.java
+++ b/src/controller/channel/ProcessingChain.java
@@ -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;
diff --git a/src/decode/DecoderType.java b/src/decode/DecoderType.java
index 92a69710..98217436 100644
--- a/src/decode/DecoderType.java
+++ b/src/decode/DecoderType.java
@@ -68,7 +68,7 @@ public enum DecoderType
DecoderType.LTR_STANDARD,
DecoderType.MPT1327,
DecoderType.NBFM,
-// DecoderType.P25_PHASE1,
+ DecoderType.P25_PHASE1,
DecoderType.PASSPORT );
}
diff --git a/src/decode/am/AMDecoder.java b/src/decode/am/AMDecoder.java
index 15ac40c3..f329d9ca 100644
--- a/src/decode/am/AMDecoder.java
+++ b/src/decode/am/AMDecoder.java
@@ -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
{
diff --git a/src/decode/p25/P25Decoder.java b/src/decode/p25/P25Decoder.java
index 1277b7cb..df883f86 100644
--- a/src/decode/p25/P25Decoder.java
+++ b/src/decode/p25/P25Decoder.java
@@ -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 );
diff --git a/src/dsp/filter/Gain.java b/src/dsp/filter/Gain.java
deleted file mode 100644
index e73c17eb..00000000
--- a/src/dsp/filter/Gain.java
+++ /dev/null
@@ -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
- ******************************************************************************/
-package dsp.filter;
-
-import sample.Listener;
-
-public class Gain implements Listener
-{
- private double mGain;
- private Listener 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 listener )
- {
- mListener = listener;
- }
-}
diff --git a/src/dsp/afc/AutomaticFrequencyControl.java b/src/dsp/frequency/AutomaticFrequencyControl.java
similarity index 99%
rename from src/dsp/afc/AutomaticFrequencyControl.java
rename to src/dsp/frequency/AutomaticFrequencyControl.java
index 5f57544a..ee634c8a 100644
--- a/src/dsp/afc/AutomaticFrequencyControl.java
+++ b/src/dsp/frequency/AutomaticFrequencyControl.java
@@ -1,4 +1,4 @@
-package dsp.afc;
+package dsp.frequency;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
diff --git a/src/dsp/fsk/C4FMDecoder.java b/src/dsp/fsk/C4FMDecoder.java
deleted file mode 100644
index e7a11af3..00000000
--- a/src/dsp/fsk/C4FMDecoder.java
+++ /dev/null
@@ -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 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 getTaps()
- {
- if( mTaps == null )
- {
- mTaps = new ArrayList();
-
- 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;
- }
- }
-}
diff --git a/src/dsp/fsk/C4FMSymbolFilter.java b/src/dsp/fsk/C4FMSymbolFilter.java
index c9078212..af6e4072 100644
--- a/src/dsp/fsk/C4FMSymbolFilter.java
+++ b/src/dsp/fsk/C4FMSymbolFilter.java
@@ -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 );
}
}
diff --git a/src/dsp/agc/ComplexAutomaticGainControl.java b/src/dsp/gain/ComplexAutomaticGainControl.java
similarity index 99%
rename from src/dsp/agc/ComplexAutomaticGainControl.java
rename to src/dsp/gain/ComplexAutomaticGainControl.java
index 566d01a1..9fef2ace 100644
--- a/src/dsp/agc/ComplexAutomaticGainControl.java
+++ b/src/dsp/gain/ComplexAutomaticGainControl.java
@@ -1,4 +1,4 @@
-package dsp.agc;
+package dsp.gain;
import java.util.concurrent.atomic.AtomicBoolean;
diff --git a/src/dsp/gain/DirectGainControl.java b/src/dsp/gain/DirectGainControl.java
new file mode 100644
index 00000000..1ee5df38
--- /dev/null
+++ b/src/dsp/gain/DirectGainControl.java
@@ -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
+ ******************************************************************************/
+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;
+ }
+}
diff --git a/src/dsp/gain/GainController.java b/src/dsp/gain/GainController.java
new file mode 100644
index 00000000..b1f15e8a
--- /dev/null
+++ b/src/dsp/gain/GainController.java
@@ -0,0 +1,8 @@
+package dsp.gain;
+
+public interface GainController
+{
+ public abstract void increase();
+
+ public abstract void decrease();
+}
\ No newline at end of file
diff --git a/src/dsp/agc/RealAutomaticGainControl.java b/src/dsp/gain/RealAutomaticGainControl.java
similarity index 99%
rename from src/dsp/agc/RealAutomaticGainControl.java
rename to src/dsp/gain/RealAutomaticGainControl.java
index 84512135..53e98d90 100644
--- a/src/dsp/agc/RealAutomaticGainControl.java
+++ b/src/dsp/gain/RealAutomaticGainControl.java
@@ -1,4 +1,4 @@
-package dsp.agc;
+package dsp.gain;
import java.util.concurrent.atomic.AtomicBoolean;
diff --git a/src/source/mixer/RealMixerSource.java b/src/source/mixer/RealMixerSource.java
index 58937bda..2e9ad4bd 100644
--- a/src/source/mixer/RealMixerSource.java
+++ b/src/source/mixer/RealMixerSource.java
@@ -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;