1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-09-21 12:26:34 -04:00
sdrangel/wdsp/wcpAGC.cpp

498 lines
13 KiB
C++
Raw Normal View History

2024-06-16 05:31:13 -04:00
/* wcpAGC.c
This file is part of a program that implements a Software-Defined Radio.
Copyright (C) 2011 - 2017 Warren Pratt, NR0V
Copyright (C) 2024 Edouard Griffiths, F4EXB Adapted to SDRangel
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 2
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, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
The author can be reached by email at
warren@wpratt.com
or by paper mail at
Warren Pratt
11303 Empire Grade
Santa Cruz, CA 95060
*/
#include "comm.hpp"
#include "nbp.hpp"
#include "wcpAGC.hpp"
namespace WDSP {
2024-07-29 17:31:43 -04:00
void WCPAGC::calc()
2024-06-16 05:31:13 -04:00
{
//assign constants
//do one-time initialization
2024-07-29 17:31:43 -04:00
out_index = -1;
ring_max = 0.0;
volts = 0.0;
save_volts = 0.0;
fast_backaverage = 0.0;
hang_backaverage = 0.0;
hang_counter = 0;
decay_type = 0;
state = 0;
loadWcpAGC();
}
WCPAGC::WCPAGC(
int _run,
int _mode,
int _pmode,
float* _in,
float* _out,
int _io_buffsize,
int _sample_rate,
double _tau_attack,
double _tau_decay,
int _n_tau,
double _max_gain,
double _var_gain,
double _fixed_gain,
double _max_input,
double _out_targ,
double _tau_fast_backaverage,
double _tau_fast_decay,
double _pop_ratio,
int _hang_enable,
double _tau_hang_backmult,
double _hangtime,
double _hang_thresh,
double _tau_hang_decay
) :
2024-06-16 05:31:13 -04:00
//initialize per call parameters
2024-07-29 17:31:43 -04:00
run(_run),
mode(_mode),
pmode(_pmode),
in(_in),
out(_out),
io_buffsize(_io_buffsize),
sample_rate((double) _sample_rate),
tau_attack(_tau_attack),
tau_decay(_tau_decay),
n_tau(_n_tau),
max_gain(_max_gain),
var_gain(_var_gain),
fixed_gain(_fixed_gain),
max_input(_max_input),
out_targ(_out_targ),
tau_fast_backaverage(_tau_fast_backaverage),
tau_fast_decay(_tau_fast_decay),
pop_ratio(_pop_ratio),
hang_enable(_hang_enable),
tau_hang_backmult(_tau_hang_backmult),
hangtime(_hangtime),
hang_thresh(_hang_thresh),
tau_hang_decay(_tau_hang_decay)
{
calc();
}
void WCPAGC::loadWcpAGC()
2024-06-16 05:31:13 -04:00
{
double tmp;
2024-06-16 05:31:13 -04:00
//calculate internal parameters
2024-07-29 17:31:43 -04:00
attack_buffsize = (int)ceil(sample_rate * n_tau * tau_attack);
in_index = attack_buffsize + out_index;
attack_mult = 1.0 - exp(-1.0 / (sample_rate * tau_attack));
decay_mult = 1.0 - exp(-1.0 / (sample_rate * tau_decay));
fast_decay_mult = 1.0 - exp(-1.0 / (sample_rate * tau_fast_decay));
fast_backmult = 1.0 - exp(-1.0 / (sample_rate * tau_fast_backaverage));
onemfast_backmult = 1.0 - fast_backmult;
2024-06-16 05:31:13 -04:00
2024-07-29 17:31:43 -04:00
out_target = out_targ * (1.0 - exp(-(double)n_tau)) * 0.9999;
min_volts = out_target / (var_gain * max_gain);
inv_out_target = 1.0 / out_target;
2024-06-16 05:31:13 -04:00
2024-07-29 17:31:43 -04:00
tmp = log10(out_target / (max_input * var_gain * max_gain));
2024-06-16 05:31:13 -04:00
if (tmp == 0.0)
tmp = 1e-16;
2024-07-29 17:31:43 -04:00
slope_constant = (out_target * (1.0 - 1.0 / var_gain)) / tmp;
inv_max_input = 1.0 / max_input;
tmp = pow (10.0, (hang_thresh - 1.0) / 0.125);
hang_level = (max_input * tmp + (out_target /
(var_gain * max_gain)) * (1.0 - tmp)) * 0.637;
hang_backmult = 1.0 - exp(-1.0 / (sample_rate * tau_hang_backmult));
onemhang_backmult = 1.0 - hang_backmult;
hang_decay_mult = 1.0 - exp(-1.0 / (sample_rate * tau_hang_decay));
2024-06-16 05:31:13 -04:00
}
2024-07-29 17:31:43 -04:00
void WCPAGC::flush()
2024-06-16 05:31:13 -04:00
{
2024-07-29 17:31:43 -04:00
std::fill(ring.begin(), ring.end(), 0);
std::fill(abs_ring.begin(), abs_ring.end(), 0);
ring_max = 0.0;
2024-06-16 05:31:13 -04:00
}
2024-07-29 17:31:43 -04:00
void WCPAGC::execute()
2024-06-16 05:31:13 -04:00
{
int i, j, k;
double mult;
2024-07-29 17:31:43 -04:00
if (run)
2024-06-16 05:31:13 -04:00
{
2024-07-29 17:31:43 -04:00
if (mode == 0)
2024-06-16 05:31:13 -04:00
{
2024-07-29 17:31:43 -04:00
for (i = 0; i < io_buffsize; i++)
2024-06-16 05:31:13 -04:00
{
2024-07-29 17:31:43 -04:00
out[2 * i + 0] = fixed_gain * in[2 * i + 0];
out[2 * i + 1] = fixed_gain * in[2 * i + 1];
2024-06-16 05:31:13 -04:00
}
2024-06-16 05:31:13 -04:00
return;
}
2024-07-29 17:31:43 -04:00
for (i = 0; i < io_buffsize; i++)
2024-06-16 05:31:13 -04:00
{
2024-07-29 17:31:43 -04:00
if (++out_index >= ring_buffsize)
out_index -= ring_buffsize;
2024-07-29 17:31:43 -04:00
if (++in_index >= ring_buffsize)
in_index -= ring_buffsize;
2024-06-16 05:31:13 -04:00
2024-07-29 17:31:43 -04:00
out_sample[0] = ring[2 * out_index + 0];
out_sample[1] = ring[2 * out_index + 1];
abs_out_sample = abs_ring[out_index];
double xr = ring[2 * in_index + 0] = in[2 * i + 0];
double xi = ring[2 * in_index + 1] = in[2 * i + 1];
2024-07-29 17:31:43 -04:00
if (pmode == 0)
abs_ring[in_index] = std::max(fabs(xr), fabs(xi));
2024-06-16 05:31:13 -04:00
else
2024-07-29 17:31:43 -04:00
abs_ring[in_index] = sqrt(xr*xr + xi*xi);
2024-06-16 05:31:13 -04:00
2024-07-29 17:31:43 -04:00
fast_backaverage = fast_backmult * abs_out_sample + onemfast_backmult * fast_backaverage;
hang_backaverage = hang_backmult * abs_out_sample + onemhang_backmult * hang_backaverage;
2024-06-16 05:31:13 -04:00
2024-07-29 17:31:43 -04:00
if ((abs_out_sample >= ring_max) && (abs_out_sample > 0.0))
2024-06-16 05:31:13 -04:00
{
2024-07-29 17:31:43 -04:00
ring_max = 0.0;
k = out_index;
2024-07-29 17:31:43 -04:00
for (j = 0; j < attack_buffsize; j++)
2024-06-16 05:31:13 -04:00
{
2024-07-29 17:31:43 -04:00
if (++k == ring_buffsize)
2024-06-16 05:31:13 -04:00
k = 0;
2024-07-29 17:31:43 -04:00
if (abs_ring[k] > ring_max)
ring_max = abs_ring[k];
2024-06-16 05:31:13 -04:00
}
}
2024-07-29 17:31:43 -04:00
if (abs_ring[in_index] > ring_max)
ring_max = abs_ring[in_index];
2024-06-16 05:31:13 -04:00
2024-07-29 17:31:43 -04:00
if (hang_counter > 0)
--hang_counter;
2024-06-16 05:31:13 -04:00
2024-07-29 17:31:43 -04:00
switch (state)
2024-06-16 05:31:13 -04:00
{
case 0:
{
2024-07-29 17:31:43 -04:00
if (ring_max >= volts)
2024-06-16 05:31:13 -04:00
{
2024-07-29 17:31:43 -04:00
volts += (ring_max - volts) * attack_mult;
2024-06-16 05:31:13 -04:00
}
else
{
2024-07-29 17:31:43 -04:00
if (volts > pop_ratio * fast_backaverage)
2024-06-16 05:31:13 -04:00
{
2024-07-29 17:31:43 -04:00
state = 1;
volts += (ring_max - volts) * fast_decay_mult;
2024-06-16 05:31:13 -04:00
}
else
{
2024-07-29 17:31:43 -04:00
if (hang_enable && (hang_backaverage > hang_level))
2024-06-16 05:31:13 -04:00
{
2024-07-29 17:31:43 -04:00
state = 2;
hang_counter = (int)(hangtime * sample_rate);
decay_type = 1;
2024-06-16 05:31:13 -04:00
}
else
{
2024-07-29 17:31:43 -04:00
state = 3;
volts += (ring_max - volts) * decay_mult;
decay_type = 0;
2024-06-16 05:31:13 -04:00
}
}
}
break;
}
2024-06-16 05:31:13 -04:00
case 1:
{
2024-07-29 17:31:43 -04:00
if (ring_max >= volts)
2024-06-16 05:31:13 -04:00
{
2024-07-29 17:31:43 -04:00
state = 0;
volts += (ring_max - volts) * attack_mult;
2024-06-16 05:31:13 -04:00
}
else
{
2024-07-29 17:31:43 -04:00
if (volts > save_volts)
2024-06-16 05:31:13 -04:00
{
2024-07-29 17:31:43 -04:00
volts += (ring_max - volts) * fast_decay_mult;
2024-06-16 05:31:13 -04:00
}
else
{
2024-07-29 17:31:43 -04:00
if (hang_counter > 0)
2024-06-16 05:31:13 -04:00
{
2024-07-29 17:31:43 -04:00
state = 2;
2024-06-16 05:31:13 -04:00
}
else
{
2024-07-29 17:31:43 -04:00
if (decay_type == 0)
2024-06-16 05:31:13 -04:00
{
2024-07-29 17:31:43 -04:00
state = 3;
volts += (ring_max - volts) * decay_mult;
2024-06-16 05:31:13 -04:00
}
else
{
2024-07-29 17:31:43 -04:00
state = 4;
volts += (ring_max - volts) * hang_decay_mult;
2024-06-16 05:31:13 -04:00
}
}
}
}
break;
}
2024-06-16 05:31:13 -04:00
case 2:
{
2024-07-29 17:31:43 -04:00
if (ring_max >= volts)
2024-06-16 05:31:13 -04:00
{
2024-07-29 17:31:43 -04:00
state = 0;
save_volts = volts;
volts += (ring_max - volts) * attack_mult;
2024-06-16 05:31:13 -04:00
}
else
{
2024-07-29 17:31:43 -04:00
if (hang_counter == 0)
2024-06-16 05:31:13 -04:00
{
2024-07-29 17:31:43 -04:00
state = 4;
volts += (ring_max - volts) * hang_decay_mult;
2024-06-16 05:31:13 -04:00
}
}
break;
}
2024-06-16 05:31:13 -04:00
case 3:
{
2024-07-29 17:31:43 -04:00
if (ring_max >= volts)
2024-06-16 05:31:13 -04:00
{
2024-07-29 17:31:43 -04:00
state = 0;
save_volts = volts;
volts += (ring_max - volts) * attack_mult;
2024-06-16 05:31:13 -04:00
}
else
{
2024-07-29 17:31:43 -04:00
volts += (ring_max - volts) * decay_mult;
2024-06-16 05:31:13 -04:00
}
break;
}
2024-06-16 05:31:13 -04:00
case 4:
{
2024-07-29 17:31:43 -04:00
if (ring_max >= volts)
2024-06-16 05:31:13 -04:00
{
2024-07-29 17:31:43 -04:00
state = 0;
save_volts = volts;
volts += (ring_max - volts) * attack_mult;
2024-06-16 05:31:13 -04:00
}
else
{
2024-07-29 17:31:43 -04:00
volts += (ring_max - volts) * hang_decay_mult;
2024-06-16 05:31:13 -04:00
}
break;
}
}
2024-07-29 17:31:43 -04:00
if (volts < min_volts)
volts = min_volts;
2024-07-29 17:31:43 -04:00
gain = volts * inv_out_target;
mult = (out_target - slope_constant * std::min (0.0, log10(inv_max_input * volts))) / volts;
out[2 * i + 0] = out_sample[0] * mult;
out[2 * i + 1] = out_sample[1] * mult;
2024-06-16 05:31:13 -04:00
}
}
2024-07-29 17:31:43 -04:00
else if (out != in)
{
2024-07-29 17:31:43 -04:00
std::copy(in, in + io_buffsize * 2, out);
}
2024-06-16 05:31:13 -04:00
}
2024-07-29 17:31:43 -04:00
void WCPAGC::setBuffers(float* _in, float* _out)
2024-06-16 05:31:13 -04:00
{
2024-07-29 17:31:43 -04:00
in = _in;
out = _out;
2024-06-16 05:31:13 -04:00
}
2024-07-29 17:31:43 -04:00
void WCPAGC::setSamplerate(int _rate)
2024-06-16 05:31:13 -04:00
{
2024-07-29 17:31:43 -04:00
sample_rate = _rate;
calc();
2024-06-16 05:31:13 -04:00
}
2024-07-29 17:31:43 -04:00
void WCPAGC::setSize(int _size)
2024-06-16 05:31:13 -04:00
{
2024-07-29 17:31:43 -04:00
io_buffsize = _size;
calc();
2024-06-16 05:31:13 -04:00
}
/********************************************************************************************************
* *
2024-07-29 17:31:43 -04:00
* Public Properties *
2024-06-16 05:31:13 -04:00
* *
********************************************************************************************************/
2024-07-29 17:31:43 -04:00
void WCPAGC::setMode(int _mode)
2024-06-16 05:31:13 -04:00
{
2024-07-29 17:31:43 -04:00
switch (_mode)
2024-06-16 05:31:13 -04:00
{
case 0: //agcOFF
2024-07-29 17:31:43 -04:00
mode = 0;
loadWcpAGC();
2024-06-16 05:31:13 -04:00
break;
case 1: //agcLONG
2024-07-29 17:31:43 -04:00
mode = 1;
hangtime = 2.000;
tau_decay = 2.000;
loadWcpAGC();
2024-06-16 05:31:13 -04:00
break;
case 2: //agcSLOW
2024-07-29 17:31:43 -04:00
mode = 2;
hangtime = 1.000;
tau_decay = 0.500;
loadWcpAGC();
2024-06-16 05:31:13 -04:00
break;
case 3: //agcMED
2024-07-29 17:31:43 -04:00
mode = 3;
hang_thresh = 1.0;
hangtime = 0.000;
tau_decay = 0.250;
loadWcpAGC();
2024-06-16 05:31:13 -04:00
break;
case 4: //agcFAST
2024-07-29 17:31:43 -04:00
mode = 4;
hang_thresh = 1.0;
hangtime = 0.000;
tau_decay = 0.050;
loadWcpAGC();
2024-06-16 05:31:13 -04:00
break;
default:
2024-07-29 17:31:43 -04:00
mode = 5;
2024-06-16 05:31:13 -04:00
break;
}
}
2024-07-29 17:31:43 -04:00
void WCPAGC::setFixed(double _fixed_agc)
{
fixed_gain = pow (10.0, (double) _fixed_agc / 20.0);
loadWcpAGC();
}
void WCPAGC::setAttack(int _attack)
2024-06-16 05:31:13 -04:00
{
2024-07-29 17:31:43 -04:00
tau_attack = (double) _attack / 1000.0;
loadWcpAGC();
2024-06-16 05:31:13 -04:00
}
2024-07-29 17:31:43 -04:00
void WCPAGC::setDecay(int _decay)
2024-06-16 05:31:13 -04:00
{
2024-07-29 17:31:43 -04:00
tau_decay = (double) _decay / 1000.0;
loadWcpAGC();
2024-06-16 05:31:13 -04:00
}
2024-07-29 17:31:43 -04:00
void WCPAGC::setHang(int _hang)
2024-06-16 05:31:13 -04:00
{
2024-07-29 17:31:43 -04:00
hangtime = (double) _hang / 1000.0;
loadWcpAGC();
2024-06-16 05:31:13 -04:00
}
2024-07-29 17:31:43 -04:00
void WCPAGC::getHangLevel(double *hangLevel)
2024-06-16 05:31:13 -04:00
//for line on bandscope
{
2024-07-29 17:31:43 -04:00
*hangLevel = 20.0 * log10(hang_level / 0.637);
2024-06-16 05:31:13 -04:00
}
2024-07-29 17:31:43 -04:00
void WCPAGC::setHangLevel(double _hangLevel)
2024-06-16 05:31:13 -04:00
//for line on bandscope
{
double convert, tmp;
2024-07-13 17:59:46 -04:00
2024-07-29 17:31:43 -04:00
if (max_input > min_volts)
2024-06-16 05:31:13 -04:00
{
2024-07-29 17:31:43 -04:00
convert = pow (10.0, _hangLevel / 20.0);
tmp = std::max(1e-8, (convert - min_volts) / (max_input - min_volts));
hang_thresh = 1.0 + 0.125 * log10 (tmp);
2024-06-16 05:31:13 -04:00
}
else
2024-07-29 17:31:43 -04:00
hang_thresh = 1.0;
2024-07-13 17:59:46 -04:00
2024-07-29 17:31:43 -04:00
loadWcpAGC();
2024-06-16 05:31:13 -04:00
}
2024-07-29 17:31:43 -04:00
void WCPAGC::getHangThreshold(int *hangthreshold)
2024-06-16 05:31:13 -04:00
//for slider in setup
{
2024-07-29 17:31:43 -04:00
*hangthreshold = (int) (100.0 * hang_thresh);
2024-06-16 05:31:13 -04:00
}
2024-07-29 17:31:43 -04:00
void WCPAGC::setHangThreshold(int _hangthreshold)
2024-06-16 05:31:13 -04:00
//For slider in setup
{
2024-07-29 17:31:43 -04:00
hang_thresh = (double) _hangthreshold / 100.0;
loadWcpAGC();
2024-06-16 05:31:13 -04:00
}
2024-07-29 17:31:43 -04:00
void WCPAGC::getTop(double *max_agc)
2024-06-16 05:31:13 -04:00
//for AGC Max Gain in setup
{
2024-07-29 17:31:43 -04:00
*max_agc = 20 * log10 (max_gain);
2024-06-16 05:31:13 -04:00
}
2024-07-29 17:31:43 -04:00
void WCPAGC::setTop(double _max_agc)
2024-06-16 05:31:13 -04:00
//for AGC Max Gain in setup
{
2024-07-29 17:31:43 -04:00
max_gain = pow (10.0, (double) _max_agc / 20.0);
loadWcpAGC();
2024-06-16 05:31:13 -04:00
}
2024-07-29 17:31:43 -04:00
void WCPAGC::setSlope(int _slope)
2024-06-16 05:31:13 -04:00
{
2024-07-29 17:31:43 -04:00
var_gain = pow (10.0, (double) _slope / 20.0 / 10.0);
loadWcpAGC();
2024-06-16 05:31:13 -04:00
}
2024-07-29 17:31:43 -04:00
void WCPAGC::setMaxInputLevel(double _level)
2024-06-16 05:31:13 -04:00
{
2024-07-29 17:31:43 -04:00
max_input = _level;
loadWcpAGC();
2024-06-16 05:31:13 -04:00
}
2024-07-29 17:31:43 -04:00
void WCPAGC::setRun(int state)
2024-06-16 05:31:13 -04:00
{
2024-07-29 17:31:43 -04:00
run = state;
2024-06-16 05:31:13 -04:00
}
} // namespace WDSP