kopia lustrzana https://github.com/f4exb/sdrangel
				
				
				
			
		
			
				
	
	
		
			504 wiersze
		
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
			
		
		
	
	
			504 wiersze
		
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
| /*  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 {
 | |
| 
 | |
| void WCPAGC::calc()
 | |
| {
 | |
|     //assign constants
 | |
|     //do one-time initialization
 | |
|     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
 | |
| ) :
 | |
|     //initialize per call parameters
 | |
|     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()
 | |
| {
 | |
|     double tmp;
 | |
|     //calculate internal parameters
 | |
|     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;
 | |
| 
 | |
|     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;
 | |
| 
 | |
|     tmp = log10(out_target / (max_input * var_gain * max_gain));
 | |
| 
 | |
|     if (tmp == 0.0)
 | |
|         tmp = 1e-16;
 | |
| 
 | |
|     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));
 | |
| }
 | |
| 
 | |
| void WCPAGC::flush()
 | |
| {
 | |
|     std::fill(ring.begin(), ring.end(), 0);
 | |
|     std::fill(abs_ring.begin(), abs_ring.end(), 0);
 | |
|     ring_max = 0.0;
 | |
| }
 | |
| 
 | |
| void WCPAGC::execute()
 | |
| {
 | |
|     int i;
 | |
|     int k;
 | |
|     double mult;
 | |
| 
 | |
|     if (run)
 | |
|     {
 | |
|         if (mode == 0)
 | |
|         {
 | |
|             for (i = 0; i < io_buffsize; i++)
 | |
|             {
 | |
|                 out[2 * i + 0] = (float) (fixed_gain * in[2 * i + 0]);
 | |
|                 out[2 * i + 1] = (float) (fixed_gain * in[2 * i + 1]);
 | |
|             }
 | |
| 
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         for (i = 0; i < io_buffsize; i++)
 | |
|         {
 | |
|             if (++out_index >= ring_buffsize)
 | |
|                 out_index -= ring_buffsize;
 | |
| 
 | |
|             if (++in_index >= ring_buffsize)
 | |
|                 in_index -= ring_buffsize;
 | |
| 
 | |
|             out_sample[0] = ring[2 * out_index + 0];
 | |
|             out_sample[1] = ring[2 * out_index + 1];
 | |
|             abs_out_sample = abs_ring[out_index];
 | |
|             ring[2 * in_index + 0] = in[2 * i + 0];
 | |
|             ring[2 * in_index + 1] = in[2 * i + 1];
 | |
|             double xr = ring[2 * in_index + 0];
 | |
|             double xi = ring[2 * in_index + 1];
 | |
| 
 | |
|             if (pmode == 0)
 | |
|                 abs_ring[in_index] = std::max(fabs(xr), fabs(xi));
 | |
|             else
 | |
|                 abs_ring[in_index] = sqrt(xr*xr + xi*xi);
 | |
| 
 | |
|             fast_backaverage = fast_backmult * abs_out_sample + onemfast_backmult * fast_backaverage;
 | |
|             hang_backaverage = hang_backmult * abs_out_sample + onemhang_backmult * hang_backaverage;
 | |
| 
 | |
|             if ((abs_out_sample >= ring_max) && (abs_out_sample > 0.0))
 | |
|             {
 | |
|                 ring_max = 0.0;
 | |
|                 k = out_index;
 | |
| 
 | |
|                 for (int j = 0; j < attack_buffsize; j++)
 | |
|                 {
 | |
|                     if (++k == ring_buffsize)
 | |
|                         k = 0;
 | |
|                     if (abs_ring[k] > ring_max)
 | |
|                         ring_max = abs_ring[k];
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             if (abs_ring[in_index] > ring_max)
 | |
|                 ring_max = abs_ring[in_index];
 | |
| 
 | |
|             if (hang_counter > 0)
 | |
|                 --hang_counter;
 | |
| 
 | |
|             switch (state)
 | |
|             {
 | |
|             case 0:
 | |
|                 {
 | |
|                     if (ring_max >= volts)
 | |
|                     {
 | |
|                         volts += (ring_max - volts) * attack_mult;
 | |
|                     }
 | |
|                     else
 | |
|                     {
 | |
|                         if (volts > pop_ratio * fast_backaverage)
 | |
|                         {
 | |
|                             state = 1;
 | |
|                             volts += (ring_max - volts) * fast_decay_mult;
 | |
|                         }
 | |
|                         else
 | |
|                         {
 | |
|                             if (hang_enable && (hang_backaverage > hang_level))
 | |
|                             {
 | |
|                                 state = 2;
 | |
|                                 hang_counter = (int)(hangtime * sample_rate);
 | |
|                                 decay_type = 1;
 | |
|                             }
 | |
|                             else
 | |
|                             {
 | |
|                                 state = 3;
 | |
|                                 volts += (ring_max - volts) * decay_mult;
 | |
|                                 decay_type = 0;
 | |
|                             }
 | |
|                         }
 | |
|                     }
 | |
|                     break;
 | |
|                 }
 | |
| 
 | |
|             case 1:
 | |
|                 {
 | |
|                     if (ring_max >= volts)
 | |
|                     {
 | |
|                         state = 0;
 | |
|                         volts += (ring_max - volts) * attack_mult;
 | |
|                     }
 | |
|                     else
 | |
|                     {
 | |
|                         if (volts > save_volts)
 | |
|                         {
 | |
|                             volts += (ring_max - volts) * fast_decay_mult;
 | |
|                         }
 | |
|                         else
 | |
|                         {
 | |
|                             if (hang_counter > 0)
 | |
|                             {
 | |
|                                 state = 2;
 | |
|                             }
 | |
|                             else
 | |
|                             {
 | |
|                                 if (decay_type == 0)
 | |
|                                 {
 | |
|                                     state = 3;
 | |
|                                     volts += (ring_max - volts) * decay_mult;
 | |
|                                 }
 | |
|                                 else
 | |
|                                 {
 | |
|                                     state = 4;
 | |
|                                     volts += (ring_max - volts) * hang_decay_mult;
 | |
|                                 }
 | |
|                             }
 | |
|                         }
 | |
|                     }
 | |
|                     break;
 | |
|                 }
 | |
| 
 | |
|             case 2:
 | |
|                 {
 | |
|                     if (ring_max >= volts)
 | |
|                     {
 | |
|                         state = 0;
 | |
|                         save_volts = volts;
 | |
|                         volts += (ring_max - volts) * attack_mult;
 | |
|                     }
 | |
|                     else
 | |
|                     {
 | |
|                         if (hang_counter == 0)
 | |
|                         {
 | |
|                             state = 4;
 | |
|                             volts += (ring_max - volts) * hang_decay_mult;
 | |
|                         }
 | |
|                     }
 | |
|                     break;
 | |
|                 }
 | |
| 
 | |
|             case 3:
 | |
|                 {
 | |
|                     if (ring_max >= volts)
 | |
|                     {
 | |
|                         state = 0;
 | |
|                         save_volts = volts;
 | |
|                         volts += (ring_max - volts) * attack_mult;
 | |
|                     }
 | |
|                     else
 | |
|                     {
 | |
|                         volts += (ring_max - volts) * decay_mult;
 | |
|                     }
 | |
|                     break;
 | |
|                 }
 | |
| 
 | |
|             case 4:
 | |
|                 {
 | |
|                     if (ring_max >= volts)
 | |
|                     {
 | |
|                         state = 0;
 | |
|                         save_volts = volts;
 | |
|                         volts += (ring_max - volts) * attack_mult;
 | |
|                     }
 | |
|                     else
 | |
|                     {
 | |
|                         volts += (ring_max - volts) * hang_decay_mult;
 | |
|                     }
 | |
|                     break;
 | |
|                 }
 | |
|             default:
 | |
|                 break;
 | |
|             }
 | |
| 
 | |
|             if (volts < min_volts)
 | |
|                 volts = min_volts;
 | |
| 
 | |
|             gain = volts * inv_out_target;
 | |
|             mult = (out_target - slope_constant * std::min (0.0, log10(inv_max_input * volts))) / volts;
 | |
|             out[2 * i + 0] = (float) (out_sample[0] * mult);
 | |
|             out[2 * i + 1] = (float) (out_sample[1] * mult);
 | |
|         }
 | |
|     }
 | |
|     else if (out != in)
 | |
|     {
 | |
|         std::copy(in, in + io_buffsize * 2, out);
 | |
|     }
 | |
| }
 | |
| 
 | |
| void WCPAGC::setBuffers(float* _in, float* _out)
 | |
| {
 | |
|     in = _in;
 | |
|     out = _out;
 | |
| }
 | |
| 
 | |
| void WCPAGC::setSamplerate(int _rate)
 | |
| {
 | |
|     sample_rate = _rate;
 | |
|     calc();
 | |
| }
 | |
| 
 | |
| void WCPAGC::setSize(int _size)
 | |
| {
 | |
|     io_buffsize = _size;
 | |
|     calc();
 | |
| }
 | |
| 
 | |
| /********************************************************************************************************
 | |
| *                                                                                                       *
 | |
| *                                        Public Properties                                              *
 | |
| *                                                                                                       *
 | |
| ********************************************************************************************************/
 | |
| 
 | |
| void WCPAGC::setMode(int _mode)
 | |
| {
 | |
|     switch (_mode)
 | |
|     {
 | |
|         case 0: //agcOFF
 | |
|             mode = 0;
 | |
|             loadWcpAGC();
 | |
|             break;
 | |
|         case 1: //agcLONG
 | |
|             mode = 1;
 | |
|             hangtime = 2.000;
 | |
|             tau_decay = 2.000;
 | |
|             loadWcpAGC();
 | |
|             break;
 | |
|         case 2: //agcSLOW
 | |
|             mode = 2;
 | |
|             hangtime = 1.000;
 | |
|             tau_decay = 0.500;
 | |
|             loadWcpAGC();
 | |
|             break;
 | |
|         case 3: //agcMED
 | |
|             mode = 3;
 | |
|             hang_thresh = 1.0;
 | |
|             hangtime = 0.000;
 | |
|             tau_decay = 0.250;
 | |
|             loadWcpAGC();
 | |
|             break;
 | |
|         case 4: //agcFAST
 | |
|             mode = 4;
 | |
|             hang_thresh = 1.0;
 | |
|             hangtime = 0.000;
 | |
|             tau_decay = 0.050;
 | |
|             loadWcpAGC();
 | |
|             break;
 | |
|         default:
 | |
|             mode = 5;
 | |
|             break;
 | |
|     }
 | |
| }
 | |
| 
 | |
| void WCPAGC::setFixed(double _fixed_agc)
 | |
| {
 | |
|     fixed_gain = pow (10.0, _fixed_agc / 20.0);
 | |
|     loadWcpAGC();
 | |
| }
 | |
| 
 | |
| void WCPAGC::setAttack(int _attack)
 | |
| {
 | |
|     tau_attack = (double) _attack / 1000.0;
 | |
|     loadWcpAGC();
 | |
| }
 | |
| 
 | |
| void WCPAGC::setDecay(int _decay)
 | |
| {
 | |
|     tau_decay = (double) _decay / 1000.0;
 | |
|     loadWcpAGC();
 | |
| }
 | |
| 
 | |
| void WCPAGC::setHang(int _hang)
 | |
| {
 | |
|     hangtime = (double) _hang / 1000.0;
 | |
|     loadWcpAGC();
 | |
| }
 | |
| 
 | |
| void WCPAGC::getHangLevel(double *hangLevel) const
 | |
| //for line on bandscope
 | |
| {
 | |
|     *hangLevel = 20.0 * log10(hang_level / 0.637);
 | |
| }
 | |
| 
 | |
| void WCPAGC::setHangLevel(double _hangLevel)
 | |
| //for line on bandscope
 | |
| {
 | |
|     double convert;
 | |
|     double tmp;
 | |
| 
 | |
|     if (max_input > min_volts)
 | |
|     {
 | |
|         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);
 | |
|     }
 | |
|     else
 | |
|         hang_thresh = 1.0;
 | |
| 
 | |
|     loadWcpAGC();
 | |
| }
 | |
| 
 | |
| void WCPAGC::getHangThreshold(int *hangthreshold) const
 | |
| //for slider in setup
 | |
| {
 | |
|     *hangthreshold = (int) (100.0 * hang_thresh);
 | |
| }
 | |
| 
 | |
| void WCPAGC::setHangThreshold(int _hangthreshold)
 | |
| //For slider in setup
 | |
| {
 | |
|     hang_thresh = (double) _hangthreshold / 100.0;
 | |
|     loadWcpAGC();
 | |
| }
 | |
| 
 | |
| void WCPAGC::getTop(double *max_agc) const
 | |
| //for AGC Max Gain in setup
 | |
| {
 | |
|     *max_agc = 20 * log10 (max_gain);
 | |
| }
 | |
| 
 | |
| void WCPAGC::setTop(double _max_agc)
 | |
| //for AGC Max Gain in setup
 | |
| {
 | |
|     max_gain = pow (10.0, _max_agc / 20.0);
 | |
|     loadWcpAGC();
 | |
| }
 | |
| 
 | |
| void WCPAGC::setSlope(int _slope)
 | |
| {
 | |
|     var_gain = pow (10.0, (double) _slope / 20.0 / 10.0);
 | |
|     loadWcpAGC();
 | |
| }
 | |
| 
 | |
| void WCPAGC::setMaxInputLevel(double _level)
 | |
| {
 | |
|     max_input = _level;
 | |
|     loadWcpAGC();
 | |
| }
 | |
| 
 | |
| void WCPAGC::setRun(int _state)
 | |
| {
 | |
|     run = _state;
 | |
| }
 | |
| 
 | |
| } // namespace WDSP
 |