mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-13 17:17:29 -05:00
Updates pull request from Roman Tsypuk to convert the build system from
ant to gradle. Removes linux and windows application launch scripts and produces an executable jar with all resources included.
This commit is contained in:
@@ -0,0 +1,224 @@
|
||||
package io.github.dsheirer.dsp.gain;
|
||||
|
||||
import io.github.dsheirer.buffer.DoubleCircularBuffer;
|
||||
import io.github.dsheirer.buffer.FloatCircularBuffer;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014 Dennis Sheirer
|
||||
* Copyright (C) 2011 Alex Csete
|
||||
* Copyright (C) 2010 Moe Wheatley
|
||||
*
|
||||
* 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/>
|
||||
*
|
||||
* ---------------------------------------------------
|
||||
* Ported from Alex Csete's gqrx source code at:
|
||||
* https://github.com/csete/gqrx/blob/74ba2742c89b0dfb66854fc9f72d19e5c6e355b6/dsp/agc_impl.cpp
|
||||
*
|
||||
* This Software is released under the "Simplified BSD License" + + +
|
||||
* Copyright 2010 Moe Wheatley. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY Moe Wheatley ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL Moe Wheatley OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are
|
||||
* those of the authors and should not be interpreted as representing
|
||||
* official policies, either expressed or implied, of Moe Wheatley.
|
||||
******************************************************************************/
|
||||
public class AutomaticGainControl
|
||||
{
|
||||
private static final double SAMPLE_RATE = 48000;
|
||||
|
||||
/* Signal delay line - time delay in seconds */
|
||||
// private static final double DELAY_TIME_CONSTANT = 0.015;
|
||||
private static final double DELAY_TIME_CONSTANT = 0.01;
|
||||
|
||||
/* Peak detector window - time delay in seconds */
|
||||
private static final double WINDOW_TIME_CONSTANT = 0.018;
|
||||
|
||||
/* Attack time constants in seconds */
|
||||
private static final double ATTACK_RISE_TIME_CONSTANT = 0.002;
|
||||
private static final double ATTACK_FALL_TIME_CONSTANT = 0.005;
|
||||
|
||||
private static final double ATTACK_RISE_ALPHA = 1.0 -
|
||||
Math.exp( -1.0 / SAMPLE_RATE * ATTACK_RISE_TIME_CONSTANT );
|
||||
|
||||
private static final double ATTACK_FALL_ALPHA = 1.0 -
|
||||
Math.exp( -1.0 / SAMPLE_RATE * ATTACK_FALL_TIME_CONSTANT );
|
||||
|
||||
/* AGC decay value in milliseconds (20 to 5000) */
|
||||
private static final double DECAY = 200;
|
||||
|
||||
/* Ratio between rise and fall times of decay time constants - adjust for
|
||||
* best action with SSB */
|
||||
private static final double DECAY_RISEFALL_RATIO = 0.3;
|
||||
|
||||
private static final double DECAY_RISE_ALPHA = 1.0 -
|
||||
Math.exp( -1.0 / ( SAMPLE_RATE * DECAY * .001 * DECAY_RISEFALL_RATIO ) );
|
||||
|
||||
private static final double DECAY_FALL_ALPHA = 1.0 -
|
||||
Math.exp( -1.0 / ( SAMPLE_RATE * DECAY * .001 ) );
|
||||
|
||||
/* Hang timer release decay time constant in seconds */
|
||||
@SuppressWarnings( "unused" )
|
||||
private static final double RELEASE_TIME_CONSTANT = 0.05;
|
||||
|
||||
/* Specifies the AGC Knee in dB if AGC is active (nominal range -160 to 0 dB) */
|
||||
private static final double THRESHOLD = -100;
|
||||
|
||||
/* Limit output to about 3db of maximum */
|
||||
private static final double AGC_OUT_SCALE = 0.7;
|
||||
|
||||
/* Keep max input and output the same */
|
||||
private static final double MAX_AMPLITUDE = 32767.0; //1.0;
|
||||
private static final double MAX_MANUAL_AMPLITUDE = 32767.0; //1.0;
|
||||
|
||||
/* Specifies AGC manual gain in dB if AGC is not active ( 0 to 100 dB) */
|
||||
private static final double MANUAL_GAIN = 0.0;
|
||||
|
||||
private static final double MANUAL_AGC_GAIN = MAX_MANUAL_AMPLITUDE *
|
||||
Math.pow( 10.0, MANUAL_GAIN / 20.0 );
|
||||
|
||||
/* Specifies dB reduction in output at knee from max output level (0 - 10dB) */
|
||||
private static final double SLOPE_FACTOR = 2.0;
|
||||
|
||||
private static final double KNEE = THRESHOLD / 20.0;
|
||||
|
||||
private static final double GAIN_SLOPE = SLOPE_FACTOR / 100.0;
|
||||
|
||||
private static final double FIXED_GAIN = AGC_OUT_SCALE *
|
||||
Math.pow( 10.0, KNEE * ( GAIN_SLOPE - 1.0 ) );
|
||||
|
||||
/* Constant for calc log() so that a value of 0 magnitude = -8 */
|
||||
private static final double MIN_CONSTANT = 3.2767E-4;
|
||||
|
||||
private AtomicBoolean mAGCEnabled = new AtomicBoolean( true );
|
||||
|
||||
private double mPeakMagnitude = 0.0;
|
||||
private double mAttackAverage = 0.0;
|
||||
private double mDecayAverage = 0.0;
|
||||
|
||||
private FloatCircularBuffer mDelayBuffer =
|
||||
new FloatCircularBuffer( (int)( SAMPLE_RATE * DELAY_TIME_CONSTANT ) );
|
||||
private DoubleCircularBuffer mMagnitudeBuffer =
|
||||
new DoubleCircularBuffer( (int)( SAMPLE_RATE * WINDOW_TIME_CONSTANT ) );
|
||||
|
||||
public AutomaticGainControl()
|
||||
{
|
||||
}
|
||||
|
||||
public float process( float currentSample )
|
||||
{
|
||||
float delayedSample = mDelayBuffer.get( currentSample );
|
||||
|
||||
double gain = MANUAL_AGC_GAIN;
|
||||
|
||||
if( mAGCEnabled.get() )
|
||||
{
|
||||
double currentMagnitude = Math.log10( Math.abs( currentSample ) +
|
||||
MIN_CONSTANT ) - Math.log10( MAX_AMPLITUDE );
|
||||
|
||||
double delayedMagnitude = mMagnitudeBuffer.get( currentMagnitude );
|
||||
|
||||
if( currentMagnitude > mPeakMagnitude )
|
||||
{
|
||||
/* Use current magnitude as peak if it's larger */
|
||||
mPeakMagnitude = currentMagnitude;
|
||||
}
|
||||
else if( delayedMagnitude == mPeakMagnitude )
|
||||
{
|
||||
/* If delayed magnitude is the current peak, then find a new peak */
|
||||
mPeakMagnitude = mMagnitudeBuffer.max();
|
||||
}
|
||||
|
||||
/* Exponential decay mode */
|
||||
if( mPeakMagnitude > mAttackAverage )
|
||||
{
|
||||
mAttackAverage = ( ( 1.0 - ATTACK_RISE_ALPHA ) * mAttackAverage ) +
|
||||
( ATTACK_RISE_ALPHA * mPeakMagnitude );
|
||||
}
|
||||
else
|
||||
{
|
||||
mAttackAverage = ( ( 1.0 - ATTACK_FALL_ALPHA ) * mAttackAverage ) +
|
||||
( ATTACK_FALL_ALPHA * mPeakMagnitude );
|
||||
}
|
||||
|
||||
if( mPeakMagnitude > mDecayAverage )
|
||||
{
|
||||
mDecayAverage = ( ( 1.0 - DECAY_RISE_ALPHA ) * mDecayAverage ) +
|
||||
( DECAY_RISE_ALPHA * mPeakMagnitude );
|
||||
}
|
||||
else
|
||||
{
|
||||
mDecayAverage = ( ( 1.0 - DECAY_FALL_ALPHA ) * mDecayAverage ) +
|
||||
( DECAY_RISE_ALPHA * mPeakMagnitude );
|
||||
}
|
||||
|
||||
double magnitude = ( mAttackAverage > mDecayAverage ) ?
|
||||
mAttackAverage : mDecayAverage;
|
||||
|
||||
if( magnitude < KNEE )
|
||||
{
|
||||
gain = FIXED_GAIN;
|
||||
}
|
||||
else
|
||||
{
|
||||
gain = AGC_OUT_SCALE * Math.pow( 10.0,
|
||||
magnitude * ( GAIN_SLOPE - 1.0 ) );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return (float)( delayedSample * gain );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables Automatic Gain Control (AGC).
|
||||
*/
|
||||
public void setAGCEnabled( boolean enabled )
|
||||
{
|
||||
mAGCEnabled.set( enabled );
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if AGC is enabled
|
||||
* @return true=AGC, false=MANUAL GAIN
|
||||
*/
|
||||
public boolean isAGCEnabled()
|
||||
{
|
||||
return mAGCEnabled.get();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package io.github.dsheirer.dsp.gain;
|
||||
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.real.RealBuffer;
|
||||
|
||||
public class AutomaticGainControl_RB extends AutomaticGainControl
|
||||
implements Listener<RealBuffer>
|
||||
{
|
||||
private Listener<RealBuffer> mListener;
|
||||
|
||||
public AutomaticGainControl_RB()
|
||||
{
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
mListener = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies AGC to a buffer of real (float) samples
|
||||
*/
|
||||
public RealBuffer process( RealBuffer buffer )
|
||||
{
|
||||
float[] samples = buffer.getSamples();
|
||||
|
||||
for( int x = 0; x < samples.length; x++ )
|
||||
{
|
||||
samples[ x ] = process( samples[ x ] );
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( RealBuffer buffer )
|
||||
{
|
||||
if( mListener != null )
|
||||
{
|
||||
mListener.receive( process( buffer ) );
|
||||
}
|
||||
}
|
||||
|
||||
public void setListener( Listener<RealBuffer> listener )
|
||||
{
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
public void removeListener()
|
||||
{
|
||||
mListener = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
package io.github.dsheirer.dsp.gain;
|
||||
|
||||
import io.github.dsheirer.buffer.ComplexCircularBuffer;
|
||||
import io.github.dsheirer.buffer.DoubleCircularBuffer;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.Provider;
|
||||
import io.github.dsheirer.sample.complex.Complex;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014 Dennis Sheirer
|
||||
* Copyright (C) 2011 Alex Csete
|
||||
* Copyright (C) 2010 Moe Wheatley
|
||||
*
|
||||
* 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/>
|
||||
*
|
||||
* ---------------------------------------------------
|
||||
* Ported from Alex Csete's gqrx source code at:
|
||||
* https://github.com/csete/gqrx/blob/74ba2742c89b0dfb66854fc9f72d19e5c6e355b6/dsp/agc_impl.cpp
|
||||
*
|
||||
* This Software is released under the "Simplified BSD License" + + +
|
||||
* Copyright 2010 Moe Wheatley. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY Moe Wheatley ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL Moe Wheatley OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are
|
||||
* those of the authors and should not be interpreted as representing
|
||||
* official policies, either expressed or implied, of Moe Wheatley.
|
||||
******************************************************************************/
|
||||
public class ComplexAutomaticGainControl implements Listener<Complex>,
|
||||
Provider<Complex>
|
||||
{
|
||||
private static final double SAMPLE_RATE = 48000;
|
||||
|
||||
/* Signal delay line - time delay in seconds */
|
||||
private static final double DELAY_TIME_CONSTANT = 0.015;
|
||||
|
||||
/* Peak detector window - time delay in seconds */
|
||||
private static final double WINDOW_TIME_CONSTANT = 0.018;
|
||||
|
||||
/* Attack time constants in seconds */
|
||||
private static final double ATTACK_RISE_TIME_CONSTANT = 0.002;
|
||||
private static final double ATTACK_FALL_TIME_CONSTANT = 0.005;
|
||||
|
||||
private static final double ATTACK_RISE_ALPHA = 1.0 -
|
||||
Math.exp( -1.0 / SAMPLE_RATE * ATTACK_RISE_TIME_CONSTANT );
|
||||
|
||||
private static final double ATTACK_FALL_ALPHA = 1.0 -
|
||||
Math.exp( -1.0 / SAMPLE_RATE * ATTACK_FALL_TIME_CONSTANT );
|
||||
|
||||
/* AGC decay value in milliseconds (20 to 5000) */
|
||||
private static final double DECAY = 200;
|
||||
|
||||
/* Ratio between rise and fall times of decay time constants - adjust for
|
||||
* best action with SSB */
|
||||
private static final double DECAY_RISEFALL_RATIO = 0.3;
|
||||
|
||||
private static final double DECAY_RISE_ALPHA = 1.0 -
|
||||
Math.exp( -1.0 / ( SAMPLE_RATE * DECAY * .001 * DECAY_RISEFALL_RATIO ) );
|
||||
|
||||
private static final double DECAY_FALL_ALPHA = 1.0 -
|
||||
Math.exp( -1.0 / ( SAMPLE_RATE * DECAY * .001 ) );
|
||||
|
||||
/* Hang timer release decay time constant in seconds */
|
||||
@SuppressWarnings( "unused" )
|
||||
private static final double RELEASE_TIME_CONSTANT = 0.05;
|
||||
|
||||
/* Specifies the AGC Knee in dB if AGC is active (nominal range -160 to 0 dB) */
|
||||
private static final double THRESHOLD = -100;
|
||||
|
||||
/* Limit output to about 3db of maximum */
|
||||
private static final double AGC_OUT_SCALE = 0.7;
|
||||
|
||||
/* Keep max input and output the same */
|
||||
private static final double MAX_AMPLITUDE = 1.0;
|
||||
private static final double MAX_MANUAL_AMPLITUDE = 1.0;
|
||||
|
||||
/* Specifies AGC manual gain in dB if AGC is not active ( 0 to 100 dB) */
|
||||
private static final double MANUAL_GAIN = 0.0;
|
||||
|
||||
private static final double MANUAL_AGC_GAIN = MAX_MANUAL_AMPLITUDE *
|
||||
Math.pow( 10.0, MANUAL_GAIN / 20.0 );
|
||||
|
||||
/* Specifies dB reduction in output at knee from max output level (0 - 10dB) */
|
||||
private static final double SLOPE_FACTOR = 2.0;
|
||||
|
||||
private static final double KNEE = THRESHOLD / 20.0;
|
||||
|
||||
private static final double GAIN_SLOPE = SLOPE_FACTOR / 100.0;
|
||||
|
||||
private static final double FIXED_GAIN = AGC_OUT_SCALE *
|
||||
Math.pow( 10.0, KNEE * ( GAIN_SLOPE - 1.0 ) );
|
||||
|
||||
/* Constant for calc log() so that a value of 0 magnitude = -8 */
|
||||
private static final double MIN_CONSTANT = 3.2767E-4;
|
||||
|
||||
private AtomicBoolean mAGCEnabled = new AtomicBoolean( true );
|
||||
|
||||
private double mPeakMagnitude = 0.0;
|
||||
private double mAttackAverage = 0.0;
|
||||
private double mDecayAverage = 0.0;
|
||||
|
||||
private ComplexCircularBuffer mDelayBuffer =
|
||||
new ComplexCircularBuffer( (int)( SAMPLE_RATE * DELAY_TIME_CONSTANT ) );
|
||||
private DoubleCircularBuffer mMagnitudeBuffer =
|
||||
new DoubleCircularBuffer( (int)( SAMPLE_RATE * WINDOW_TIME_CONSTANT ) );
|
||||
|
||||
private Listener<Complex> mListener;
|
||||
|
||||
public ComplexAutomaticGainControl()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( Complex currentSample )
|
||||
{
|
||||
Complex delayedSample = mDelayBuffer.get( currentSample );
|
||||
|
||||
double gain = MANUAL_AGC_GAIN;
|
||||
|
||||
if( mAGCEnabled.get() )
|
||||
{
|
||||
float max = currentSample.maximumAbsolute();
|
||||
|
||||
double currentMagnitude = Math.log10( max + MIN_CONSTANT ) -
|
||||
Math.log10( MAX_AMPLITUDE );
|
||||
|
||||
double delayedMagnitude = mMagnitudeBuffer.get( currentMagnitude );
|
||||
|
||||
if( currentMagnitude > mPeakMagnitude )
|
||||
{
|
||||
/* Use current magnitude as peak if it's larger */
|
||||
mPeakMagnitude = currentMagnitude;
|
||||
}
|
||||
else if( delayedMagnitude == mPeakMagnitude )
|
||||
{
|
||||
/* If delayed magnitude is the current peak, then find a new peak */
|
||||
mPeakMagnitude = mMagnitudeBuffer.max();
|
||||
}
|
||||
|
||||
/* Exponential decay mode */
|
||||
if( mPeakMagnitude > mAttackAverage )
|
||||
{
|
||||
mAttackAverage = ( ( 1.0 - ATTACK_RISE_ALPHA ) * mAttackAverage ) +
|
||||
( ATTACK_RISE_ALPHA * mPeakMagnitude );
|
||||
}
|
||||
else
|
||||
{
|
||||
mAttackAverage = ( ( 1.0 - ATTACK_FALL_ALPHA ) * mAttackAverage ) +
|
||||
( ATTACK_FALL_ALPHA * mPeakMagnitude );
|
||||
}
|
||||
|
||||
if( mPeakMagnitude > mDecayAverage )
|
||||
{
|
||||
mDecayAverage = ( ( 1.0 - DECAY_RISE_ALPHA ) * mDecayAverage ) +
|
||||
( DECAY_RISE_ALPHA * mPeakMagnitude );
|
||||
}
|
||||
else
|
||||
{
|
||||
mDecayAverage = ( ( 1.0 - DECAY_FALL_ALPHA ) * mDecayAverage ) +
|
||||
( DECAY_RISE_ALPHA * mPeakMagnitude );
|
||||
}
|
||||
|
||||
double magnitude = ( mAttackAverage > mDecayAverage ) ?
|
||||
mAttackAverage : mDecayAverage;
|
||||
|
||||
if( magnitude < KNEE )
|
||||
{
|
||||
gain = FIXED_GAIN;
|
||||
}
|
||||
else
|
||||
{
|
||||
gain = AGC_OUT_SCALE * Math.pow( 10.0,
|
||||
magnitude * ( GAIN_SLOPE - 1.0 ) );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
delayedSample.multiply( (float)gain );
|
||||
|
||||
if( mListener != null )
|
||||
{
|
||||
mListener.receive( delayedSample );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables Automatic Gain Control (AGC).
|
||||
*/
|
||||
public void setAGCEnabled( boolean enabled )
|
||||
{
|
||||
mAGCEnabled.set( enabled );
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if AGC is enabled
|
||||
* @return true=AGC, false=MANUAL GAIN
|
||||
*/
|
||||
public boolean isAGCEnabled()
|
||||
{
|
||||
return mAGCEnabled.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setListener( Listener<Complex> listener )
|
||||
{
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeListener( Listener<Complex> listener )
|
||||
{
|
||||
mListener = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package io.github.dsheirer.dsp.gain;
|
||||
|
||||
import io.github.dsheirer.buffer.FloatCircularBuffer;
|
||||
import io.github.dsheirer.sample.complex.Complex;
|
||||
import io.github.dsheirer.sample.complex.ComplexSampleListener;
|
||||
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2015 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/>
|
||||
*
|
||||
* This class is modeled using concepts detailed in the GNURadio
|
||||
* implementation of feed forward AGC class located at:
|
||||
* https://github.com/gnuradio/gnuradio/blob/master/gr-analog/lib/
|
||||
* feedforward_agc_cc_impl.cc
|
||||
*
|
||||
******************************************************************************/
|
||||
public class ComplexFeedForwardGainControl implements ComplexSampleListener
|
||||
{
|
||||
public static final float OBJECTIVE_ENVELOPE = 1.0f;
|
||||
public static final float MINIMUM_ENVELOPE = 0.0001f;
|
||||
|
||||
private ComplexSampleListener mListener;
|
||||
|
||||
private FloatCircularBuffer mEnvelopeHistory;
|
||||
|
||||
private float mMaxEnvelope = 0.0f;
|
||||
private float mGain = 1.0f;
|
||||
|
||||
/**
|
||||
* Dynamic gain control for incoming sample stream to amplify or attenuate
|
||||
* all samples toward an objective unity)gain, using the maximum envelope
|
||||
* value detected in the stream history window.
|
||||
*
|
||||
* Uses the specified damping factor to limit gain swings. Damping factor
|
||||
* is applied against the delta between current gain value and a recalculated
|
||||
* gain value to limit how quickly the gain value will increase or decrease.
|
||||
*
|
||||
* @param window - history size to use in detecting maximum envelope value
|
||||
* @param damping - a value between 0 < damping <= 1.0;
|
||||
*/
|
||||
public ComplexFeedForwardGainControl( int window )
|
||||
{
|
||||
mEnvelopeHistory = new FloatCircularBuffer( window );
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
mListener = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( float inphase, float quadrature )
|
||||
{
|
||||
float envelope = Complex.envelope( inphase, quadrature );
|
||||
|
||||
if( envelope > mMaxEnvelope )
|
||||
{
|
||||
mMaxEnvelope = envelope;
|
||||
|
||||
adjustGain();
|
||||
}
|
||||
|
||||
/* Replace oldest envelope value with current envelope value */
|
||||
float oldestEnvelope = mEnvelopeHistory.get( envelope );
|
||||
|
||||
/* If the oldest envelope value was the max envelope value, then we
|
||||
* have to rediscover the max value from the envelope history */
|
||||
if( mMaxEnvelope == oldestEnvelope && mMaxEnvelope != envelope )
|
||||
{
|
||||
mMaxEnvelope = MINIMUM_ENVELOPE;
|
||||
|
||||
for( float value: mEnvelopeHistory.getBuffer() )
|
||||
{
|
||||
if( value > mMaxEnvelope )
|
||||
{
|
||||
mMaxEnvelope = value;
|
||||
}
|
||||
}
|
||||
|
||||
adjustGain();
|
||||
}
|
||||
|
||||
/* Apply current gain value to the sample and send to the listener */
|
||||
if( mListener != null )
|
||||
{
|
||||
mListener.receive( inphase *= mGain, quadrature *= mGain );
|
||||
}
|
||||
}
|
||||
|
||||
private void adjustGain()
|
||||
{
|
||||
mGain = OBJECTIVE_ENVELOPE / mMaxEnvelope;
|
||||
}
|
||||
|
||||
public void setListener( ComplexSampleListener listener )
|
||||
{
|
||||
mListener = listener;
|
||||
}
|
||||
}
|
||||
31
src/main/java/io/github/dsheirer/dsp/gain/ComplexGain.java
Normal file
31
src/main/java/io/github/dsheirer/dsp/gain/ComplexGain.java
Normal file
@@ -0,0 +1,31 @@
|
||||
package io.github.dsheirer.dsp.gain;
|
||||
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.complex.Complex;
|
||||
|
||||
public class ComplexGain implements Listener<Complex>
|
||||
{
|
||||
private float mGain;
|
||||
private Listener<Complex> mListener;
|
||||
|
||||
public ComplexGain( float gain )
|
||||
{
|
||||
mGain = gain;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( Complex sample )
|
||||
{
|
||||
if( mListener != null )
|
||||
{
|
||||
sample.multiply( mGain );
|
||||
|
||||
mListener.receive( sample );
|
||||
}
|
||||
}
|
||||
|
||||
public void setListener( Listener<Complex> listener )
|
||||
{
|
||||
mListener = listener;
|
||||
}
|
||||
}
|
||||
109
src/main/java/io/github/dsheirer/dsp/gain/DirectGainControl.java
Normal file
109
src/main/java/io/github/dsheirer/dsp/gain/DirectGainControl.java
Normal file
@@ -0,0 +1,109 @@
|
||||
/*******************************************************************************
|
||||
* 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 io.github.dsheirer.dsp.gain;
|
||||
|
||||
import io.github.dsheirer.sample.real.RealSampleListener;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class DirectGainControl implements GainController, RealSampleListener
|
||||
{
|
||||
private final static Logger mLog =
|
||||
LoggerFactory.getLogger( DirectGainControl.class );
|
||||
|
||||
private float mDefaultGain;
|
||||
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 )
|
||||
{
|
||||
Validate.isTrue(minimum > 0);
|
||||
|
||||
mDefaultGain = gain;
|
||||
mGain = gain;
|
||||
mMinimum = minimum;
|
||||
mMaximum = maximum;
|
||||
mIncrement = increment;
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
mListener = null;
|
||||
}
|
||||
|
||||
public float correct( float sample )
|
||||
{
|
||||
return sample * mGain;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( float sample )
|
||||
{
|
||||
mListener.receive( correct( sample ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset()
|
||||
{
|
||||
mGain = mDefaultGain;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void increase()
|
||||
{
|
||||
mGain += mIncrement;
|
||||
|
||||
if( mGain > mMaximum )
|
||||
{
|
||||
mGain = mMaximum;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decrease()
|
||||
{
|
||||
mGain -= mIncrement;
|
||||
|
||||
if( mGain < mMinimum )
|
||||
{
|
||||
mGain = mMinimum;
|
||||
}
|
||||
}
|
||||
|
||||
public void setListener( RealSampleListener listener )
|
||||
{
|
||||
mListener = listener;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package io.github.dsheirer.dsp.gain;
|
||||
|
||||
public interface GainController
|
||||
{
|
||||
public abstract void increase();
|
||||
|
||||
public abstract void decrease();
|
||||
|
||||
public abstract void reset();
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package io.github.dsheirer.dsp.gain;
|
||||
|
||||
import io.github.dsheirer.sample.real.RealBuffer;
|
||||
|
||||
public class NonClippingGain
|
||||
{
|
||||
private float mGain;
|
||||
private float mMaxValue;
|
||||
|
||||
/**
|
||||
* Applies a fixed gain value to the float samples.
|
||||
*
|
||||
* @param gain value
|
||||
* @param maxValue max absolute value to prevent clipping ( 0.0 to 1.0 )
|
||||
*/
|
||||
public NonClippingGain( float gain, float maxValue )
|
||||
{
|
||||
mGain = gain;
|
||||
mMaxValue = maxValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies gain to the sample
|
||||
*/
|
||||
public float apply( float sample )
|
||||
{
|
||||
float adjusted = sample * mGain;
|
||||
|
||||
if( adjusted > mMaxValue )
|
||||
{
|
||||
adjusted = mMaxValue;
|
||||
}
|
||||
if( adjusted < -mMaxValue )
|
||||
{
|
||||
adjusted = -mMaxValue;
|
||||
}
|
||||
|
||||
return adjusted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies gain to the sample array
|
||||
*/
|
||||
public float[] apply( float[] samples )
|
||||
{
|
||||
for( int x = 0; x < samples.length; x++ )
|
||||
{
|
||||
samples[ x ] = apply( samples[ x ]);
|
||||
}
|
||||
|
||||
return samples;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies gain to the real buffer
|
||||
*/
|
||||
public RealBuffer apply( RealBuffer buffer )
|
||||
{
|
||||
float[] samples = buffer.getSamples();
|
||||
|
||||
apply( samples );
|
||||
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user