SDRSharper/SDRSharper.Radio/SDRSharp.Radio/AutomaticGainControl.cs
SDRSharpR c07e6e6034 SDRSharper (SDRSharp Remake) Full Source (VS2017)
SDRSharper (SDRSharp Remake) Full Source (VS2017)
2018-03-26 14:02:05 -07:00

288 lines
6.5 KiB
C#

using System;
namespace SDRSharp.Radio
{
public sealed class AutomaticGainControl
{
private const float DelayTimeconst = 0.015f;
private const float WindowTimeconst = 0.018f;
private const float AttackRiseTimeconst = 0.002f;
private const float AttackFallTimeconst = 0.005f;
private const float DecayRisefallRatio = 0.3f;
private const float ReleaseTimeconst = 0.05f;
private const float AGCOutscale = 0.7f;
private UnsafeBuffer _sigDelayBuf;
private unsafe float* _sigDelayBufPtr;
private UnsafeBuffer _magBuf;
private unsafe float* _magBufPtr;
private float _decayAve;
private float _attackAve;
private float _attackRiseAlpha;
private float _attackFallAlpha;
private float _decayRiseAlpha;
private float _decayFallAlpha;
private float _fixedGain;
private float _knee;
private float _gainSlope;
private float _peak;
private int _sigDelayPtr;
private int _magBufPos;
private int _delaySamples;
private int _windowSamples;
private int _hangTime;
private int _hangTimer;
private float _threshold;
private float _slopeFactor;
private float _decay;
private double _sampleRate;
private bool _useHang;
public double SampleRate
{
get
{
return this._sampleRate;
}
set
{
if (this._sampleRate != value)
{
this._sampleRate = value;
this.Configure(true);
}
}
}
public bool UseHang
{
get
{
return this._useHang;
}
set
{
if (this._useHang != value)
{
this._useHang = value;
this.Configure(false);
}
}
}
public float Threshold
{
get
{
return this._threshold;
}
set
{
if (this._threshold != value)
{
this._threshold = value;
this.Configure(false);
}
}
}
public float Slope
{
get
{
return this._slopeFactor;
}
set
{
if (this._slopeFactor != value)
{
this._slopeFactor = value;
this.Configure(false);
}
}
}
public float Decay
{
get
{
return this._decay;
}
set
{
if (this._decay != value)
{
this._decay = value;
this.Configure(false);
}
}
}
private unsafe void Configure(bool resetBuffers)
{
if (resetBuffers)
{
this._sigDelayPtr = 0;
this._hangTimer = 0;
this._peak = -16f;
this._decayAve = -5f;
this._attackAve = -5f;
this._magBufPos = 0;
if (this._sampleRate > 0.0)
{
this._delaySamples = (int)(this._sampleRate * 0.014999999664723873);
this._windowSamples = (int)(this._sampleRate * 0.017999999225139618);
this._sigDelayBuf = UnsafeBuffer.Create(this._delaySamples, 4);
this._sigDelayBufPtr = (float*)(void*)this._sigDelayBuf;
this._magBuf = UnsafeBuffer.Create(this._windowSamples, 4);
this._magBufPtr = (float*)(void*)this._magBuf;
for (int i = 0; i < this._windowSamples; i++)
{
this._magBufPtr[i] = -16f;
}
if (this._delaySamples >= this._sigDelayBuf.Length - 1)
{
this._delaySamples = this._sigDelayBuf.Length - 1;
}
}
}
this._knee = this._threshold / 20f;
this._gainSlope = this._slopeFactor / 100f;
this._fixedGain = 0.7f * (float)Math.Pow(10.0, (double)this._knee * ((double)this._gainSlope - 1.0));
this._attackRiseAlpha = 1f - (float)Math.Exp(-1.0 / (this._sampleRate * 0.0020000000949949026));
this._attackFallAlpha = 1f - (float)Math.Exp(-1.0 / (this._sampleRate * 0.004999999888241291));
this._decayRiseAlpha = 1f - (float)Math.Exp(-1.0 / (this._sampleRate * (double)this.Decay * 0.001 * 0.30000001192092896));
this._hangTime = (int)(this._sampleRate * (double)this.Decay * 0.001);
if (this._useHang)
{
this._decayFallAlpha = 1f - (float)Math.Exp(-1.0 / (this._sampleRate * 0.05000000074505806));
}
else
{
this._decayFallAlpha = 1f - (float)Math.Exp(-1.0 / (this._sampleRate * (double)this.Decay * 0.001));
}
}
public unsafe void Process(float* buffer, int length)
{
for (int i = 0; i < length; i++)
{
float num = buffer[i];
if ((double)num != 0.0)
{
num *= 1000f;
float num2 = this._sigDelayBufPtr[this._sigDelayPtr];
this._sigDelayBufPtr[this._sigDelayPtr++] = num;
if (this._sigDelayPtr >= this._delaySamples)
{
this._sigDelayPtr = 0;
}
float num3 = (float)Math.Log10((double)Math.Abs(num));
if (float.IsNaN(num3) || float.IsInfinity(num3))
{
num3 = -8f;
}
float num4 = this._magBufPtr[this._magBufPos];
this._magBufPtr[this._magBufPos++] = num3;
if (this._magBufPos >= this._windowSamples)
{
this._magBufPos = 0;
}
if (num3 > this._peak)
{
this._peak = num3;
}
else if (num4 == this._peak)
{
this._peak = -8f;
for (int j = 0; j < this._windowSamples; j++)
{
num4 = this._magBufPtr[j];
if (num4 > this._peak)
{
this._peak = num4;
}
}
}
if (this.UseHang)
{
if (this._peak > this._attackAve)
{
this._attackAve = (1f - this._attackRiseAlpha) * this._attackAve + this._attackRiseAlpha * this._peak;
}
else
{
this._attackAve = (1f - this._attackFallAlpha) * this._attackAve + this._attackFallAlpha * this._peak;
}
if (this._peak > this._decayAve)
{
this._decayAve = (1f - this._decayRiseAlpha) * this._decayAve + this._decayRiseAlpha * this._peak;
this._hangTimer = 0;
}
else if (this._hangTimer < this._hangTime)
{
this._hangTimer++;
}
else
{
this._decayAve = (1f - this._decayFallAlpha) * this._decayAve + this._decayFallAlpha * this._peak;
}
}
else
{
if (this._peak > this._attackAve)
{
this._attackAve = (1f - this._attackRiseAlpha) * this._attackAve + this._attackRiseAlpha * this._peak;
}
else
{
this._attackAve = (1f - this._attackFallAlpha) * this._attackAve + this._attackFallAlpha * this._peak;
}
if (this._peak > this._decayAve)
{
this._decayAve = (1f - this._decayRiseAlpha) * this._decayAve + this._decayRiseAlpha * this._peak;
}
else
{
this._decayAve = (1f - this._decayFallAlpha) * this._decayAve + this._decayFallAlpha * this._peak;
}
}
num3 = ((this._attackAve > this._decayAve) ? this._attackAve : this._decayAve);
float num5 = (!(num3 <= this._knee)) ? (0.7f * (float)Math.Pow(10.0, (double)num3 * ((double)this._gainSlope - 1.0))) : this._fixedGain;
buffer[i] = num2 * num5 * 1E-05f;
}
}
}
}
}