mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-13 09:07:27 -05:00
Merge branch 'master' into 39-polyphase-channelizer
# Conflicts: # src/main/java/io/github/dsheirer/buffer/FloatCircularBuffer.java # src/main/java/io/github/dsheirer/dsp/filter/FilterFactory.java # src/main/java/io/github/dsheirer/dsp/filter/design/FilterView.java # src/main/java/io/github/dsheirer/dsp/filter/design/FilterViewer.java # src/main/java/io/github/dsheirer/dsp/filter/fir/complex/ComplexFIRFilter.java # src/main/java/io/github/dsheirer/dsp/gain/ComplexFeedForwardGainControl.java # src/main/java/io/github/dsheirer/dsp/mixer/Oscillator.java # src/main/java/io/github/dsheirer/dsp/psk/LSMDemodulator.java # src/main/java/io/github/dsheirer/module/decode/Decoder.java # src/main/java/io/github/dsheirer/module/decode/DecoderFactory.java # src/main/java/io/github/dsheirer/module/decode/p25/P25Decoder.java # src/main/java/io/github/dsheirer/module/decode/p25/P25_C4FMDecoder.java # src/main/java/io/github/dsheirer/module/decode/p25/P25_LSMDecoder.java # src/main/java/io/github/dsheirer/source/tuner/TunerChannelSource.java # src/main/java/io/github/dsheirer/source/tuner/TunerModel.java # src/main/java/io/github/dsheirer/source/tuner/TunerSourceEditor.java # src/main/java/io/github/dsheirer/spectrum/ChannelSpectrumPanel.java
This commit is contained in:
@@ -1,112 +1,125 @@
|
||||
/*******************************************************************************
|
||||
* sdr-trunk
|
||||
* Copyright (C) 2014-2018 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.buffer.RealCircularBuffer;
|
||||
import io.github.dsheirer.buffer.FloatCircularBuffer;
|
||||
import io.github.dsheirer.sample.complex.Complex;
|
||||
import io.github.dsheirer.sample.complex.ComplexBuffer;
|
||||
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;
|
||||
public static final float OBJECTIVE_ENVELOPE = 1.0f;
|
||||
public static final float MINIMUM_ENVELOPE = 0.0001f;
|
||||
|
||||
private ComplexSampleListener mListener;
|
||||
private ComplexSampleListener mListener;
|
||||
|
||||
private RealCircularBuffer 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 RealCircularBuffer( window );
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
mListener = null;
|
||||
}
|
||||
private FloatCircularBuffer mEnvelopeHistory;
|
||||
|
||||
@Override
|
||||
public void receive( float inphase, float quadrature )
|
||||
{
|
||||
float envelope = Complex.envelope( inphase, quadrature );
|
||||
private float mMaxEnvelope = 0.0f;
|
||||
private float mGain = 1.0f;
|
||||
|
||||
if( envelope > mMaxEnvelope )
|
||||
{
|
||||
mMaxEnvelope = envelope;
|
||||
|
||||
adjustGain();
|
||||
}
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public ComplexFeedForwardGainControl(int window)
|
||||
{
|
||||
mEnvelopeHistory = new FloatCircularBuffer(window);
|
||||
}
|
||||
|
||||
/* Replace oldest envelope value with current envelope value */
|
||||
float oldestEnvelope = mEnvelopeHistory.putAndGet( envelope );
|
||||
public void dispose()
|
||||
{
|
||||
mListener = null;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
@Override
|
||||
public void receive(float inphase, float quadrature)
|
||||
{
|
||||
process(inphase, quadrature);
|
||||
|
||||
public void setListener( ComplexSampleListener listener )
|
||||
{
|
||||
mListener = listener;
|
||||
}
|
||||
/* Apply current gain value to the sample and send to the listener */
|
||||
if(mListener != null)
|
||||
{
|
||||
mListener.receive(inphase *= mGain, quadrature *= mGain);
|
||||
}
|
||||
}
|
||||
|
||||
private void process(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();
|
||||
}
|
||||
}
|
||||
|
||||
private void adjustGain()
|
||||
{
|
||||
mGain = OBJECTIVE_ENVELOPE / mMaxEnvelope;
|
||||
}
|
||||
|
||||
public void setListener(ComplexSampleListener listener)
|
||||
{
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
public ComplexBuffer filter(ComplexBuffer complexBuffer)
|
||||
{
|
||||
float[] samples = complexBuffer.getSamples();
|
||||
float[] processed = new float[samples.length];
|
||||
|
||||
for(int x = 0; x < samples.length; x += 2)
|
||||
{
|
||||
process(samples[x], samples[x + 1]);
|
||||
|
||||
processed[x] = samples[x] * mGain;
|
||||
processed[x + 1] = samples[x + 1] * mGain;
|
||||
}
|
||||
|
||||
return new ComplexBuffer(processed);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,27 +5,34 @@ import io.github.dsheirer.sample.complex.Complex;
|
||||
|
||||
public class ComplexGain implements Listener<Complex>
|
||||
{
|
||||
private float mGain;
|
||||
private Listener<Complex> mListener;
|
||||
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 ComplexGain(float gain)
|
||||
{
|
||||
mGain = gain;
|
||||
}
|
||||
|
||||
public void setListener( Listener<Complex> listener )
|
||||
{
|
||||
mListener = listener;
|
||||
}
|
||||
public Complex apply(Complex sample)
|
||||
{
|
||||
sample.multiply(mGain);
|
||||
|
||||
return sample;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(Complex sample)
|
||||
{
|
||||
if(mListener != null)
|
||||
{
|
||||
apply(sample);
|
||||
|
||||
mListener.receive(sample);
|
||||
}
|
||||
}
|
||||
|
||||
public void setListener(Listener<Complex> listener)
|
||||
{
|
||||
mListener = listener;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user