mirror of
				https://github.com/f4exb/sdrangel.git
				synced 2025-10-30 20:40:20 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			1183 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1183 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*  emnr.c
 | |
| 
 | |
| This file is part of a program that implements a Software-Defined Radio.
 | |
| 
 | |
| Copyright (C) 2015 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
 | |
| 
 | |
| */
 | |
| #include <limits>
 | |
| 
 | |
| #include "comm.hpp"
 | |
| #include "calculus.hpp"
 | |
| #include "emnr.hpp"
 | |
| #include "amd.hpp"
 | |
| #include "anr.hpp"
 | |
| #include "anf.hpp"
 | |
| #include "snba.hpp"
 | |
| #include "bandpass.hpp"
 | |
| 
 | |
| namespace WDSP {
 | |
| 
 | |
| EMNR::AE::AE(
 | |
|     int _msize,
 | |
|     const std::vector<double>& _lambda_y,
 | |
|     double _zetaThresh,
 | |
|     double _psi
 | |
| ) :
 | |
|     msize(_msize),
 | |
|     lambda_y(_lambda_y),
 | |
|     zetaThresh(_zetaThresh),
 | |
|     psi(_psi)
 | |
| {
 | |
|     nmask.resize(msize);
 | |
| }
 | |
| 
 | |
| EMNR::NPS::NPS(
 | |
|     int _incr,
 | |
|     double _rate,
 | |
|     int _msize,
 | |
|     const std::vector<double>& _lambda_y,
 | |
|     std::vector<double>& _lambda_d,
 | |
| 
 | |
|     double _alpha_pow,
 | |
|     double _alpha_Pbar,
 | |
|     double _epsH1
 | |
| ) :
 | |
|     incr(_incr),
 | |
|     rate(_rate),
 | |
|     msize(_msize),
 | |
|     lambda_y(_lambda_y),
 | |
|     lambda_d(_lambda_d),
 | |
|     alpha_pow(_alpha_pow),
 | |
|     alpha_Pbar(_alpha_Pbar),
 | |
|     epsH1(_epsH1)
 | |
| {
 | |
|     epsH1r = epsH1 / (1.0 + epsH1);
 | |
|     sigma2N.resize(msize);
 | |
|     PH1y.resize(msize);
 | |
|     Pbar.resize(msize);
 | |
|     EN2y.resize(msize);
 | |
| 
 | |
|     for (int i = 0; i < msize; i++)
 | |
|     {
 | |
|         sigma2N[i] = 0.5;
 | |
|         Pbar[i] = 0.5;
 | |
|     }
 | |
| }
 | |
| 
 | |
| void EMNR::NPS::LambdaDs()
 | |
| {
 | |
| 
 | |
|     for (int k = 0; k < msize; k++)
 | |
|     {
 | |
|         PH1y[k] = 1.0 / (1.0 + (1.0 + epsH1) * exp (- epsH1r * lambda_y[k] / sigma2N[k]));
 | |
|         Pbar[k] = alpha_Pbar * Pbar[k] + (1.0 - alpha_Pbar) * PH1y[k];
 | |
| 
 | |
|         if (Pbar[k] > 0.99)
 | |
|             PH1y[k] = std::min (PH1y[k], 0.99);
 | |
| 
 | |
|         EN2y[k] = (1.0 - PH1y[k]) * lambda_y[k] + PH1y[k] * sigma2N[k];
 | |
|         sigma2N[k] = alpha_pow * sigma2N[k] + (1.0 - alpha_pow) * EN2y[k];
 | |
|     }
 | |
| 
 | |
|     std::copy(sigma2N.begin(), sigma2N.end(), lambda_d.begin());
 | |
| }
 | |
| 
 | |
| const std::array<double, 18> EMNR::NP::DVals = { 1.0, 2.0, 5.0, 8.0, 10.0, 15.0, 20.0, 30.0, 40.0,
 | |
|     60.0, 80.0, 120.0, 140.0, 160.0, 180.0, 220.0, 260.0, 300.0 };
 | |
| const std::array<double, 18> EMNR::NP::MVals = { 0.000, 0.260, 0.480, 0.580, 0.610, 0.668, 0.705, 0.762, 0.800,
 | |
|     0.841, 0.865, 0.890, 0.900, 0.910, 0.920, 0.930, 0.935, 0.940 };
 | |
| 
 | |
| EMNR::NP::NP(
 | |
|     int _incr,
 | |
|     double _rate,
 | |
|     int _msize,
 | |
|     std::vector<double>& _lambda_y,
 | |
|     std::vector<double>& _lambda_d
 | |
| ) :
 | |
|     incr(_incr),
 | |
|     rate(_rate),
 | |
|     msize(_msize),
 | |
|     lambda_y(_lambda_y),
 | |
|     lambda_d(_lambda_d),
 | |
|     invQeqMax(0.5),
 | |
|     av(2.12)
 | |
| {
 | |
| 
 | |
|     double tau0 = -128.0 / 8000.0 / log(0.7);
 | |
|     alphaCsmooth = exp(-incr / rate / tau0);
 | |
|     double tau1 = -128.0 / 8000.0 / log(0.96);
 | |
|     alphaMax = exp(-incr / rate / tau1);
 | |
|     double tau2 = -128.0 / 8000.0 / log(0.7);
 | |
|     alphaCmin = exp(-incr / rate / tau2);
 | |
|     double tau3 = -128.0 / 8000.0 / log(0.3);
 | |
|     alphaMin_max_value = exp(-incr / rate / tau3);
 | |
|     snrq = -incr / (0.064 * rate);
 | |
|     double tau4 = -128.0 / 8000.0 / log(0.8);
 | |
|     betamax = exp(-incr / rate / tau4);
 | |
|     Dtime = 8.0 * 12.0 * 128.0 / 8000.0;
 | |
|     U = 8;
 | |
|     V = (int)(0.5 + (Dtime * rate / (U * incr)));
 | |
| 
 | |
|     if (V < 4)
 | |
|         V = 4;
 | |
| 
 | |
|     if ((U = (int)(0.5 + (Dtime * rate / (V * incr)))) < 1)
 | |
|         U = 1;
 | |
| 
 | |
|     D = U * V;
 | |
|     interpM(&MofD, D, 18, DVals, MVals);
 | |
|     interpM(&MofV, V, 18, DVals, MVals);
 | |
| 
 | |
|     invQbar_points[0] = 0.03;
 | |
|     invQbar_points[1] = 0.05;
 | |
|     invQbar_points[2] = 0.06;
 | |
|     invQbar_points[3] = std::numeric_limits<double>::max();
 | |
| 
 | |
|     double db;
 | |
|     db = 10.0 * log10(8.0) / (12.0 * 128 / 8000);
 | |
|     nsmax[0] = pow(10.0, db / 10.0 * V * incr / rate);
 | |
|     db = 10.0 * log10(4.0) / (12.0 * 128 / 8000);
 | |
|     nsmax[1] = pow(10.0, db / 10.0 * V * incr / rate);
 | |
|     db = 10.0 * log10(2.0) / (12.0 * 128 / 8000);
 | |
|     nsmax[2] = pow(10.0, db / 10.0 * V * incr / rate);
 | |
|     db = 10.0 * log10(1.2) / (12.0 * 128 / 8000);
 | |
|     nsmax[3] = pow(10.0, db / 10.0 * V * incr / rate);
 | |
| 
 | |
|     p.resize(msize);
 | |
|     alphaOptHat.resize(msize);
 | |
|     alphaHat.resize(msize);
 | |
|     sigma2N.resize(msize);
 | |
|     pbar.resize(msize);
 | |
|     p2bar.resize(msize);
 | |
|     Qeq.resize(msize);
 | |
|     bmin.resize(msize);
 | |
|     bmin_sub.resize(msize);
 | |
|     k_mod.resize(msize);
 | |
|     actmin.resize(msize);
 | |
|     actmin_sub.resize(msize);
 | |
|     lmin_flag.resize(msize);
 | |
|     pmin_u.resize(msize);
 | |
|     actminbuff.resize(U);
 | |
| 
 | |
|     for (int i = 0; i < U; i++) {
 | |
|         actminbuff[i].resize(msize);
 | |
|     }
 | |
| 
 | |
|     alphaC = 1.0;
 | |
|     subwc = V;
 | |
|     amb_idx = 0;
 | |
| 
 | |
|     for (int k = 0; k < msize; k++) {
 | |
|         lambda_y[k] = 0.5;
 | |
|     }
 | |
| 
 | |
|     std::copy(lambda_y.begin(), lambda_y.end(), p.begin());
 | |
|     std::copy(lambda_y.begin(), lambda_y.end(), sigma2N.begin());
 | |
|     std::copy(lambda_y.begin(), lambda_y.end(), pbar.begin());
 | |
|     std::copy(lambda_y.begin(), lambda_y.end(), pmin_u.begin());
 | |
| 
 | |
|     for (int k = 0; k < msize; k++)
 | |
|     {
 | |
|         p2bar[k] = lambda_y[k] * lambda_y[k];
 | |
|         actmin[k] = std::numeric_limits<double>::max();
 | |
|         actmin_sub[k] = std::numeric_limits<double>::max();
 | |
| 
 | |
|         for (int ku = 0; ku < U; ku++) {
 | |
|             actminbuff[ku][k] = std::numeric_limits<double>::max();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     std::fill(lmin_flag.begin(), lmin_flag.end(), 0);
 | |
| }
 | |
| 
 | |
| void EMNR::NP::interpM (
 | |
|     double* res,
 | |
|     double x,
 | |
|     int nvals,
 | |
|     const std::array<double, 18>& xvals,
 | |
|     const std::array<double, 18>& yvals
 | |
| )
 | |
| {
 | |
|     if (x <= xvals[0])
 | |
|     {
 | |
|         *res = yvals[0];
 | |
|     }
 | |
|     else if (x >= xvals[nvals - 1])
 | |
|     {
 | |
|         *res = yvals[nvals - 1];
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         int idx = 1;
 | |
|         double xllow;
 | |
|         double xlhigh;
 | |
|         double frac;
 | |
| 
 | |
|         while ((x >= xvals[idx]) && (idx < nvals - 1))
 | |
|             idx++;
 | |
| 
 | |
|         xllow = log10 (xvals[idx - 1]);
 | |
|         xlhigh = log10(xvals[idx]);
 | |
|         frac = (log10 (x) - xllow) / (xlhigh - xllow);
 | |
|         *res = yvals[idx - 1] + frac * (yvals[idx] - yvals[idx - 1]);
 | |
|     }
 | |
| }
 | |
| 
 | |
| void EMNR::NP::LambdaD()
 | |
| {
 | |
|     int k;
 | |
|     double f0;
 | |
|     double f1;
 | |
|     double f2;
 | |
|     double f3;
 | |
|     double sum_prev_p;
 | |
|     double sum_lambda_y;
 | |
|     double alphaCtilda;
 | |
|     double sum_prev_sigma2N;
 | |
|     double alphaMin;
 | |
|     double SNR;
 | |
|     double beta;
 | |
|     double varHat;
 | |
|     double invQeq;
 | |
|     double invQbar;
 | |
|     double bc;
 | |
|     double QeqTilda;
 | |
|     double QeqTildaSub;
 | |
|     double noise_slope_max;
 | |
| 
 | |
|     sum_prev_p = 0.0;
 | |
|     sum_lambda_y = 0.0;
 | |
|     sum_prev_sigma2N = 0.0;
 | |
| 
 | |
|     for (k = 0; k < msize; k++)
 | |
|     {
 | |
|         sum_prev_p += p[k];
 | |
|         sum_lambda_y += lambda_y[k];
 | |
|         sum_prev_sigma2N += sigma2N[k];
 | |
|     }
 | |
| 
 | |
|     for (k = 0; k < msize; k++)
 | |
|     {
 | |
|         f0 = p[k] / sigma2N[k] - 1.0;
 | |
|         alphaOptHat[k] = 1.0 / (1.0 + f0 * f0);
 | |
|     }
 | |
| 
 | |
|     SNR = sum_prev_p / sum_prev_sigma2N;
 | |
|     alphaMin = std::min (alphaMin_max_value, pow (SNR, snrq));
 | |
| 
 | |
|     for (k = 0; k < msize; k++)
 | |
|         if (alphaOptHat[k] < alphaMin) alphaOptHat[k] = alphaMin;
 | |
| 
 | |
|     f1 = sum_prev_p / sum_lambda_y - 1.0;
 | |
|     alphaCtilda = 1.0 / (1.0 + f1 * f1);
 | |
|     alphaC = alphaCsmooth * alphaC + (1.0 - alphaCsmooth) * std::max (alphaCtilda, alphaCmin);
 | |
|     f2 = alphaMax * alphaC;
 | |
| 
 | |
|     for (k = 0; k < msize; k++)
 | |
|         alphaHat[k] = f2 * alphaOptHat[k];
 | |
| 
 | |
|     for (k = 0; k < msize; k++)
 | |
|         p[k] = alphaHat[k] * p[k] + (1.0 - alphaHat[k]) * lambda_y[k];
 | |
| 
 | |
|     invQbar = 0.0;
 | |
| 
 | |
|     for (k = 0; k < msize; k++)
 | |
|     {
 | |
|         beta = std::min (betamax, alphaHat[k] * alphaHat[k]);
 | |
|         pbar[k] = beta * pbar[k] + (1.0 - beta) * p[k];
 | |
|         p2bar[k] = beta * p2bar[k] + (1.0 - beta) * p[k] * p[k];
 | |
|         varHat = p2bar[k] - pbar[k] * pbar[k];
 | |
|         invQeq = varHat / (2.0 * sigma2N[k] * sigma2N[k]);
 | |
|         if (invQeq > invQeqMax)
 | |
|             invQeq = invQeqMax;
 | |
|         Qeq[k] = 1.0 / invQeq;
 | |
|         invQbar += invQeq;
 | |
|     }
 | |
|     invQbar /= (double) msize;
 | |
|     bc = 1.0 + av * sqrt (invQbar);
 | |
| 
 | |
|     for (k = 0; k < msize; k++)
 | |
|     {
 | |
|         QeqTilda    = (Qeq[k] - 2.0 * MofD) / (1.0 - MofD);
 | |
|         QeqTildaSub = (Qeq[k] - 2.0 * MofV) / (1.0 - MofV);
 | |
|         bmin[k]     = 1.0 + 2.0 * (D - 1.0) / QeqTilda;
 | |
|         bmin_sub[k] = 1.0 + 2.0 * (V - 1.0) / QeqTildaSub;
 | |
|     }
 | |
| 
 | |
|     std::fill(k_mod.begin(), k_mod.end(), 0);
 | |
| 
 | |
|     for (k = 0; k < msize; k++)
 | |
|     {
 | |
|         f3 = p[k] * bmin[k] * bc;
 | |
| 
 | |
|         if (f3 < actmin[k])
 | |
|         {
 | |
|             actmin[k] = f3;
 | |
|             actmin_sub[k] = p[k] * bmin_sub[k] * bc;
 | |
|             k_mod[k] = 1;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (subwc == V)
 | |
|     {
 | |
|         if (invQbar < invQbar_points[0])
 | |
|             noise_slope_max = nsmax[0];
 | |
|         else if (invQbar < invQbar_points[1])
 | |
|             noise_slope_max = nsmax[1];
 | |
|         else if (invQbar < invQbar_points[2])
 | |
|             noise_slope_max = nsmax[2];
 | |
|         else
 | |
|             noise_slope_max = nsmax[3];
 | |
| 
 | |
|         for (k = 0; k < msize; k++)
 | |
|         {
 | |
|             int ku;
 | |
|             double min;
 | |
| 
 | |
|             if (k_mod[k])
 | |
|                 lmin_flag[k] = 0;
 | |
| 
 | |
|             actminbuff[amb_idx][k] = actmin[k];
 | |
|             min = std::numeric_limits<double>::max();
 | |
| 
 | |
|             for (ku = 0; ku < U; ku++)
 | |
|             {
 | |
|                 if (actminbuff[ku][k] < min)
 | |
|                     min = actminbuff[ku][k];
 | |
|             }
 | |
| 
 | |
|             pmin_u[k] = min;
 | |
| 
 | |
|             if ((lmin_flag[k] == 1)
 | |
|                 && (actmin_sub[k] < noise_slope_max * pmin_u[k])
 | |
|                 && (actmin_sub[k] >                   pmin_u[k]))
 | |
|             {
 | |
|                 pmin_u[k] = actmin_sub[k];
 | |
|                 for (ku = 0; ku < U; ku++)
 | |
|                     actminbuff[ku][k] = actmin_sub[k];
 | |
|             }
 | |
| 
 | |
|             lmin_flag[k] = 0;
 | |
|             actmin[k] = std::numeric_limits<double>::max();
 | |
|             actmin_sub[k] = std::numeric_limits<double>::max();
 | |
|         }
 | |
| 
 | |
|         if (++amb_idx == U)
 | |
|             amb_idx = 0;
 | |
| 
 | |
|         subwc = 1;
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         if (subwc > 1)
 | |
|         {
 | |
|             for (k = 0; k < msize; k++)
 | |
|             {
 | |
|                 if (k_mod[k])
 | |
|                 {
 | |
|                     lmin_flag[k] = 1;
 | |
|                     sigma2N[k] = std::min (actmin_sub[k], pmin_u[k]);
 | |
|                     pmin_u[k] = sigma2N[k];
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         ++subwc;
 | |
|     }
 | |
| 
 | |
|     std::copy(sigma2N.begin(), sigma2N.end(), lambda_d.begin());
 | |
| }
 | |
| 
 | |
| EMNR::G::G(
 | |
|     int _incr,
 | |
|     double _rate,
 | |
|     int _msize,
 | |
|     std::vector<double>& _mask,
 | |
|     const std::vector<float>& _y
 | |
| ) :
 | |
|     incr(_incr),
 | |
|     rate(_rate),
 | |
|     msize(_msize),
 | |
|     mask(_mask),
 | |
|     y(_y)
 | |
| {
 | |
| 
 | |
|     lambda_y.resize(msize);
 | |
|     lambda_d.resize(msize);
 | |
|     prev_gamma.resize(msize);
 | |
|     prev_mask.resize(msize);
 | |
| 
 | |
|     gf1p5 = sqrt(PI) / 2.0;
 | |
| 
 | |
|     double tau = -128.0 / 8000.0 / log(0.98);
 | |
|     alpha = exp(-incr / rate / tau);
 | |
| 
 | |
|     eps_floor = std::numeric_limits<double>::min();
 | |
|     gamma_max = 1000.0;
 | |
|     q = 0.2;
 | |
| 
 | |
|     std::fill(prev_mask.begin(), prev_mask.end(), 1.0);
 | |
|     std::fill(prev_gamma.begin(), prev_gamma.end(), 1.0);
 | |
| 
 | |
|     gmax = 10000.0;
 | |
| 
 | |
|     std::copy(Calculus::GG.begin(), Calculus::GG.end(), GG.begin());
 | |
|     std::copy(Calculus::GGS.begin(), Calculus::GGS.end(), GGS.begin());
 | |
| 
 | |
|     // We keep this pretty useless part just in case...
 | |
|     if ((fileb = fopen("calculus", "rb")))
 | |
|     {
 | |
|         std::array<double, 241*241> gg;
 | |
|         std::size_t lgg = fread(&gg[0], sizeof(double), 241 * 241, fileb);
 | |
|         if (lgg != 241 * 241) {
 | |
|             fprintf(stderr, "GG file has an invalid size\n");
 | |
|         } else {
 | |
|             std::copy(gg.begin(), gg.end(), GG.begin());
 | |
|         }
 | |
|         std::array<double, 241*241> ggs;
 | |
|         std::size_t lggs =fread(&ggs[0], sizeof(double), 241 * 241, fileb);
 | |
|         if (lggs != 241 * 241) {
 | |
|             fprintf(stderr, "GGS file has an invalid size\n");
 | |
|         } else {
 | |
|             std::copy(ggs.begin(), ggs.end(), GGS.begin());
 | |
|         }
 | |
|         fclose(fileb);
 | |
|     }
 | |
| }
 | |
| 
 | |
| void EMNR::G::calc_gamma0()
 | |
| {
 | |
|     double gamma;
 | |
|     double eps_hat;
 | |
|     double v;
 | |
| 
 | |
|     for (int k = 0; k < msize; k++)
 | |
|     {
 | |
|         gamma = std::min (lambda_y[k] / lambda_d[k], gamma_max);
 | |
|         eps_hat = alpha * prev_mask[k] * prev_mask[k] * prev_gamma[k]
 | |
|             + (1.0 - alpha) * std::max (gamma - 1.0f, eps_floor);
 | |
|         v = (eps_hat / (1.0 + eps_hat)) * gamma;
 | |
|         mask[k] = gf1p5 * sqrt (v) / gamma * exp (- 0.5 * v)
 | |
|             * ((1.0 + v) * bessI0 (0.5 * v) + v * bessI1 (0.5 * v));
 | |
|         double v2 = std::min (v, 700.0);
 | |
|         double eta = mask[k] * mask[k] * lambda_y[k] / lambda_d[k];
 | |
|         double eps = eta / (1.0 - q);
 | |
|         double witchHat = (1.0 - q) / q * exp (v2) / (1.0 + eps);
 | |
|         mask[k] *= witchHat / (1.0 + witchHat);
 | |
| 
 | |
|         if (mask[k] > gmax)
 | |
|             mask[k] = gmax;
 | |
| 
 | |
|         prev_gamma[k] = gamma;
 | |
|         prev_mask[k] = mask[k];
 | |
|     }
 | |
| }
 | |
| 
 | |
| void EMNR::G::calc_gamma1()
 | |
| {
 | |
|     double gamma;
 | |
|     double eps_hat;
 | |
|     double v;
 | |
|     double ehr;
 | |
| 
 | |
|     for (int k = 0; k < msize; k++)
 | |
|     {
 | |
|         gamma = std::min (lambda_y[k] / lambda_d[k], gamma_max);
 | |
|         eps_hat = alpha * prev_mask[k] * prev_mask[k] * prev_gamma[k]
 | |
|             + (1.0 - alpha) * std::max (gamma - 1.0f, eps_floor);
 | |
|         ehr = eps_hat / (1.0 + eps_hat);
 | |
|         v = ehr * gamma;
 | |
| 
 | |
|         if ((mask[k] = ehr * exp (std::min (700.0, 0.5 * e1xb(v)))) > gmax)
 | |
|             mask[k] = gmax;
 | |
| 
 | |
|         prev_gamma[k] = gamma;
 | |
|         prev_mask[k] = mask[k];
 | |
|     }
 | |
| }
 | |
| 
 | |
| void EMNR::G::calc_gamma2()
 | |
| {
 | |
|     double gamma;
 | |
|     double eps_hat;
 | |
|     double eps_p;
 | |
| 
 | |
|     for (int k = 0; k < msize; k++)
 | |
|     {
 | |
|         gamma = std::min(lambda_y[k] / lambda_d[k], gamma_max);
 | |
|         eps_hat = alpha * prev_mask[k] * prev_mask[k] * prev_gamma[k]
 | |
|             + (1.0 - alpha) * std::max(gamma - 1.0f, eps_floor);
 | |
|         eps_p = eps_hat / (1.0 - q);
 | |
|         mask[k] = getKey(GG, gamma, eps_hat) * getKey(GGS, gamma, eps_p);
 | |
|         prev_gamma[k] = gamma;
 | |
|         prev_mask[k] = mask[k];
 | |
|     }
 | |
| }
 | |
| 
 | |
| void EMNR::G::calc_lambda_y()
 | |
| {
 | |
|     for (int k = 0; k < msize; k++)
 | |
|     {
 | |
|         double y0 = y[2 * k + 0];
 | |
|         double y1 = y[2 * k + 1];
 | |
|         lambda_y[k] = y0 * y0 + y1 * y1;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /********************************************************************************************************
 | |
| *                                                                                                       *
 | |
| *                                           Special Functions                                           *
 | |
| *                                                                                                       *
 | |
| ********************************************************************************************************/
 | |
| 
 | |
| // MODIFIED BESSEL FUNCTIONS OF THE 0TH AND 1ST ORDERS, Polynomial Approximations
 | |
| // M. Abramowitz and I. Stegun, Eds., "Handbook of Mathematical Functions."  Washington, DC:  National
 | |
| //      Bureau of Standards, 1964.
 | |
| // Shanjie Zhang and Jianming Jin, "Computation of Special Functions."  New York, NY, John Wiley and Sons,
 | |
| //      Inc., 1996.  [Sample code given in FORTRAN]
 | |
| 
 | |
| double EMNR::G::bessI0 (double x)
 | |
| {
 | |
|     double res;
 | |
|     double p;
 | |
| 
 | |
|     if (x == 0.0)
 | |
|     {
 | |
|         res = 1.0;
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         if (x < 0.0)
 | |
|             x = -x;
 | |
| 
 | |
|         if (x <= 3.75)
 | |
|         {
 | |
|             p = x / 3.75;
 | |
|             p = p * p;
 | |
|             res = ((((( 0.0045813  * p
 | |
|                 + 0.0360768) * p
 | |
|                 + 0.2659732) * p
 | |
|                 + 1.2067492) * p
 | |
|                 + 3.0899424) * p
 | |
|                 + 3.5156229) * p
 | |
|                 + 1.0;
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             p = 3.75 / x;
 | |
|             res = exp (x) / sqrt (x)
 | |
|                   * (((((((( + 0.00392377  * p
 | |
|                     - 0.01647633) * p
 | |
|                     + 0.02635537) * p
 | |
|                     - 0.02057706) * p
 | |
|                     + 0.00916281) * p
 | |
|                     - 0.00157565) * p
 | |
|                     + 0.00225319) * p
 | |
|                     + 0.01328592) * p
 | |
|                     + 0.39894228);
 | |
|         }
 | |
|     }
 | |
|     return res;
 | |
| }
 | |
| 
 | |
| double EMNR::G::bessI1 (double x)
 | |
| {
 | |
| 
 | |
|     double res;
 | |
|     double p;
 | |
| 
 | |
|     if (x == 0.0)
 | |
|     {
 | |
|         res = 0.0;
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         if (x < 0.0)
 | |
|             x = -x;
 | |
| 
 | |
|         if (x <= 3.75)
 | |
|         {
 | |
|             p = x / 3.75;
 | |
|             p = p * p;
 | |
|             res = x
 | |
|                   * (((((( 0.00032411  * p
 | |
|                     + 0.00301532) * p
 | |
|                     + 0.02658733) * p
 | |
|                     + 0.15084934) * p
 | |
|                     + 0.51498869) * p
 | |
|                     + 0.87890594) * p
 | |
|                     + 0.5);
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             p = 3.75 / x;
 | |
|             res = exp (x) / sqrt (x)
 | |
|                   * (((((((( - 0.00420059  * p
 | |
|                     + 0.01787654) * p
 | |
|                     - 0.02895312) * p
 | |
|                     + 0.02282967) * p
 | |
|                     - 0.01031555) * p
 | |
|                     + 0.00163801) * p
 | |
|                     - 0.00362018) * p
 | |
|                     - 0.03988024) * p
 | |
|                     + 0.39894228);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return res;
 | |
| }
 | |
| 
 | |
| // EXPONENTIAL INTEGRAL, E1(x)
 | |
| // M. Abramowitz and I. Stegun, Eds., "Handbook of Mathematical Functions."  Washington, DC:  National
 | |
| //      Bureau of Standards, 1964.
 | |
| // Shanjie Zhang and Jianming Jin, "Computation of Special Functions."  New York, NY, John Wiley and Sons,
 | |
| //      Inc., 1996.  [Sample code given in FORTRAN]
 | |
| 
 | |
| double EMNR::G::e1xb (double x)
 | |
| {
 | |
|     double e1;
 | |
|     double ga;
 | |
|     double r;
 | |
|     double t;
 | |
|     double t0;
 | |
|     int k;
 | |
|     int m;
 | |
| 
 | |
|     if (x == 0.0)
 | |
|     {
 | |
|         e1 = std::numeric_limits<double>::max();
 | |
|     }
 | |
|     else if (x <= 1.0)
 | |
|     {
 | |
|         e1 = 1.0;
 | |
|         r = 1.0;
 | |
| 
 | |
|         for (k = 1; k <= 25; k++)
 | |
|         {
 | |
|             r = -r * k * x / ((k + 1.0)*(k + 1.0));
 | |
|             e1 = e1 + r;
 | |
| 
 | |
|             if ( fabs (r) <= fabs (e1) * 1.0e-15 )
 | |
|                 break;
 | |
|         }
 | |
| 
 | |
|         ga = 0.5772156649015328;
 | |
|         e1 = - ga - log (x) + x * e1;
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         m = 20 + (int)(80.0 / x);
 | |
|         t0 = 0.0;
 | |
| 
 | |
|         for (k = m; k >= 1; k--)
 | |
|             t0 = (float)k / (1.0 + k / (x + t0));
 | |
| 
 | |
|         t = 1.0 / (x + t0);
 | |
|         e1 = exp (- x) * t;
 | |
|     }
 | |
| 
 | |
|     return e1;
 | |
| }
 | |
| 
 | |
| /********************************************************************************************************
 | |
| *                                                                                                       *
 | |
| *                                           Main Body of Code                                           *
 | |
| *                                                                                                       *
 | |
| ********************************************************************************************************/
 | |
| 
 | |
| void EMNR::calc_window()
 | |
| {
 | |
|     int i;
 | |
|     double arg;
 | |
|     double sum;
 | |
|     double inv_coherent_gain;
 | |
| 
 | |
|     if (wintype == 0)
 | |
|     {
 | |
|         arg = 2.0 * PI / (double) fsize;
 | |
|         sum = 0.0;
 | |
| 
 | |
|         for (i = 0; i < fsize; i++)
 | |
|         {
 | |
|             window[i] = (float) (sqrt (0.54 - 0.46 * cos((float)i * arg)));
 | |
|             sum += window[i];
 | |
|         }
 | |
| 
 | |
|         inv_coherent_gain = (double) fsize / sum;
 | |
| 
 | |
|         for (i = 0; i < fsize; i++)
 | |
|             window[i] *= (float) inv_coherent_gain;
 | |
|     }
 | |
| }
 | |
| 
 | |
| void EMNR::calc()
 | |
| {
 | |
|     // float Hvals[18] = { 0.000, 0.150, 0.480, 0.780, 0.980, 1.550, 2.000, 2.300, 2.520,
 | |
|     //     3.100, 3.380, 4.150, 4.350, 4.250, 3.900, 4.100, 4.700, 5.000 };
 | |
|     incr = fsize / ovrlp;
 | |
|     gain = ogain / fsize / (float)ovrlp;
 | |
| 
 | |
|     if (fsize > bsize)
 | |
|         iasize = fsize;
 | |
|     else
 | |
|         iasize = bsize + fsize - incr;
 | |
| 
 | |
|     iainidx = 0;
 | |
|     iaoutidx = 0;
 | |
| 
 | |
|     if (fsize > bsize)
 | |
|     {
 | |
|         if (bsize > incr)
 | |
|             oasize = bsize;
 | |
|         else
 | |
|             oasize = incr;
 | |
| 
 | |
|         oainidx = (fsize - bsize - incr) % oasize;
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         oasize = bsize;
 | |
|         oainidx = fsize - incr;
 | |
|     }
 | |
| 
 | |
|     init_oainidx = oainidx;
 | |
|     oaoutidx = 0;
 | |
|     msize = fsize / 2 + 1;
 | |
|     window.resize(fsize);
 | |
|     inaccum.resize(iasize);
 | |
|     forfftin.resize(fsize);
 | |
|     forfftout.resize(msize * 2);
 | |
|     mask.resize(msize);
 | |
|     std::fill(mask.begin(), mask.end(), 1.0);
 | |
|     revfftin.resize(msize * 2);
 | |
|     revfftout.resize(fsize);
 | |
|     save.resize(ovrlp);
 | |
| 
 | |
|     for (int i = 0; i < ovrlp; i++)
 | |
|         save[i].resize(fsize);
 | |
| 
 | |
|     outaccum.resize(oasize);
 | |
|     nsamps = 0;
 | |
|     saveidx = 0;
 | |
|     Rfor = fftwf_plan_dft_r2c_1d(
 | |
|         fsize,
 | |
|         forfftin.data(),
 | |
|         (fftwf_complex *)forfftout.data(),
 | |
|         FFTW_ESTIMATE
 | |
|     );
 | |
|     Rrev = fftwf_plan_dft_c2r_1d(
 | |
|         fsize,
 | |
|         (fftwf_complex *)revfftin.data(),
 | |
|         revfftout.data(),
 | |
|         FFTW_ESTIMATE
 | |
|     );
 | |
| 
 | |
|     calc_window();
 | |
| 
 | |
|     // G
 | |
| 
 | |
|     g = new G(
 | |
|         incr,
 | |
|         rate,
 | |
|         msize,
 | |
|         mask,
 | |
|         forfftout
 | |
|     );
 | |
| 
 | |
|     // NP
 | |
| 
 | |
|     np = new NP(
 | |
|         incr,
 | |
|         rate,
 | |
|         msize,
 | |
|         g->lambda_y,
 | |
|         g->lambda_d
 | |
|     );
 | |
| 
 | |
|     // NPS
 | |
| 
 | |
|     double tauNPS0 = -128.0 / 8000.0 / log(0.8);
 | |
|     double alpha_pow = exp(-incr / rate / tauNPS0);
 | |
| 
 | |
|     double tauNPS1 = -128.0 / 8000.0 / log(0.9);
 | |
|     double alpha_Pbar = exp(-incr / rate / tauNPS1);
 | |
| 
 | |
|     nps = new NPS(
 | |
|         incr,
 | |
|         rate,
 | |
|         msize,
 | |
|         g->lambda_y,
 | |
|         g->lambda_d,
 | |
|         alpha_pow,
 | |
|         alpha_Pbar,
 | |
|         pow(10.0, 15.0 / 10.0) // epsH1
 | |
|     );
 | |
| 
 | |
|     // AE
 | |
| 
 | |
|     ae = new AE(
 | |
|         msize,
 | |
|         g->lambda_y,
 | |
|         0.75, // zetaThresh
 | |
|         10.0 // psi
 | |
|     );
 | |
| }
 | |
| 
 | |
| void EMNR::decalc()
 | |
| {
 | |
|     delete ae;
 | |
|     delete nps;
 | |
|     delete np;
 | |
|     delete g;
 | |
| 
 | |
|     fftwf_destroy_plan(Rrev);
 | |
|     fftwf_destroy_plan(Rfor);
 | |
| }
 | |
| 
 | |
| EMNR::EMNR(
 | |
|     int _run,
 | |
|     int _position,
 | |
|     int _size,
 | |
|     float* _in,
 | |
|     float* _out,
 | |
|     int _fsize,
 | |
|     int _ovrlp,
 | |
|     int _rate,
 | |
|     int _wintype,
 | |
|     double _gain,
 | |
|     int _gain_method,
 | |
|     int _npe_method,
 | |
|     int _ae_run
 | |
| )
 | |
| {
 | |
|     run = _run;
 | |
|     position = _position;
 | |
|     bsize = _size;
 | |
|     in = _in;
 | |
|     out = _out;
 | |
|     fsize = _fsize;
 | |
|     ovrlp = _ovrlp;
 | |
|     rate = _rate;
 | |
|     wintype = _wintype;
 | |
|     ogain = _gain;
 | |
|     calc();
 | |
|     g->gain_method = _gain_method;
 | |
|     g->npe_method = _npe_method;
 | |
|     g->ae_run = _ae_run;
 | |
| }
 | |
| 
 | |
| void EMNR::flush()
 | |
| {
 | |
|     std::fill(inaccum.begin(), inaccum.end(), 0);
 | |
| 
 | |
|     for (int i = 0; i < ovrlp; i++)
 | |
|         std::fill(save[i].begin(), save[i].end(), 0);
 | |
| 
 | |
|     std::fill(outaccum.begin(), outaccum.end(), 0);
 | |
|     nsamps   = 0;
 | |
|     iainidx  = 0;
 | |
|     iaoutidx = 0;
 | |
|     oainidx  = init_oainidx;
 | |
|     oaoutidx = 0;
 | |
|     saveidx  = 0;
 | |
| }
 | |
| 
 | |
| EMNR::~EMNR()
 | |
| {
 | |
|     decalc();
 | |
| }
 | |
| 
 | |
| void EMNR::aepf()
 | |
| {
 | |
|     int k;
 | |
|     int N;
 | |
|     int n;
 | |
|     double sumPre;
 | |
|     double sumPost;
 | |
|     double zeta;
 | |
|     double zetaT;
 | |
|     sumPre = 0.0;
 | |
|     sumPost = 0.0;
 | |
| 
 | |
|     for (k = 0; k < ae->msize; k++)
 | |
|     {
 | |
|         sumPre += ae->lambda_y[k];
 | |
|         sumPost += mask[k] * mask[k] * ae->lambda_y[k];
 | |
|     }
 | |
| 
 | |
|     zeta = sumPost / sumPre;
 | |
| 
 | |
|     if (zeta >= ae->zetaThresh)
 | |
|         zetaT = 1.0;
 | |
|     else
 | |
|         zetaT = zeta;
 | |
| 
 | |
|     if (zetaT == 1.0)
 | |
|         N = 1;
 | |
|     else
 | |
|         N = 1 + 2 * (int)(0.5 + ae->psi * (1.0 - zetaT / ae->zetaThresh));
 | |
| 
 | |
|     n = N / 2;
 | |
| 
 | |
|     for (k = n; k < (ae->msize - n); k++)
 | |
|     {
 | |
|         ae->nmask[k] = 0.0;
 | |
|         for (int m = k - n; m <= (k + n); m++)
 | |
|             ae->nmask[k] += mask[m];
 | |
|         ae->nmask[k] /= (float)N;
 | |
|     }
 | |
| 
 | |
|     std::copy(ae->nmask.begin(), ae->nmask.end() - 2*n, &mask[n]);
 | |
| }
 | |
| 
 | |
| double EMNR::G::getKey(const std::array<double, 241*241>& type, double gamma, double xi)
 | |
| {
 | |
|     int ngamma1;
 | |
|     int ngamma2;
 | |
|     int nxi1 = 0;
 | |
|     int nxi2 = 0;
 | |
|     double tg;
 | |
|     double tx;
 | |
|     double dg;
 | |
|     double dx;
 | |
|     const double dmin = 0.001;
 | |
|     const double dmax = 1000.0;
 | |
| 
 | |
|     if (gamma <= dmin)
 | |
|     {
 | |
|         ngamma1 = ngamma2 = 0;
 | |
|         tg = 0.0;
 | |
|     }
 | |
|     else if (gamma >= dmax)
 | |
|     {
 | |
|         ngamma1 = ngamma2 = 240;
 | |
|         tg = 60.0;
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         tg = 10.0 * log10(gamma / dmin);
 | |
|         ngamma1 = (int)(4.0 * tg);
 | |
|         ngamma2 = ngamma1 + 1;
 | |
|     }
 | |
| 
 | |
|     if (xi <= dmin)
 | |
|     {
 | |
|         nxi1 = nxi2 = 0;
 | |
|         tx = 0.0;
 | |
|     }
 | |
|     else if (xi >= dmax)
 | |
|     {
 | |
|         nxi1 = nxi2 = 240;
 | |
|         tx = 60.0;
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         tx = 10.0 * log10(xi / dmin);
 | |
|         nxi1 = (int)(4.0 * tx);
 | |
|         nxi2 = nxi1 + 1;
 | |
|     }
 | |
| 
 | |
|     dg = (tg - 0.25 * ngamma1) / 0.25;
 | |
|     dx = (tx - 0.25 * nxi1) / 0.25;
 | |
| 
 | |
|     std::array<int, 4> ix;
 | |
|     ix[0] = 241 * nxi1 + ngamma1;
 | |
|     ix[1] = 241 * nxi2 + ngamma1;
 | |
|     ix[2] = 241 * nxi1 + ngamma2;
 | |
|     ix[3] = 241 * nxi2 + ngamma2;
 | |
| 
 | |
|     for (auto& ixi : ix)
 | |
|     {
 | |
|         if (ixi < 0) {
 | |
|             ixi = 0;
 | |
|         } else if (ixi >= 241*241) {
 | |
|             ixi = 241*241 - 1;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return (1.0 - dg)  * (1.0 - dx) * type[ix[0]]
 | |
|         +  (1.0 - dg)  *        dx  * type[ix[1]]
 | |
|         +         dg   * (1.0 - dx) * type[ix[2]]
 | |
|         +         dg   *        dx  * type[ix[3]];
 | |
| }
 | |
| 
 | |
| void EMNR::calc_gain()
 | |
| {
 | |
|     g->calc_lambda_y();
 | |
| 
 | |
|     switch (g->npe_method)
 | |
|     {
 | |
|     case 0:
 | |
|         np->LambdaD();
 | |
|         break;
 | |
|     case 1:
 | |
|         nps->LambdaDs();
 | |
|         break;
 | |
|     default:
 | |
|         break;
 | |
|     }
 | |
| 
 | |
|     switch (g->gain_method)
 | |
|     {
 | |
|     case 0:
 | |
|         g->calc_gamma0();
 | |
|         break;
 | |
|     case 1:
 | |
|         g->calc_gamma1();
 | |
|         break;
 | |
|     case 2:
 | |
|         g->calc_gamma2();
 | |
|         break;
 | |
|     default:
 | |
|         break;
 | |
|     }
 | |
| 
 | |
|     if (g->ae_run)
 | |
|         aepf();
 | |
| }
 | |
| 
 | |
| void EMNR::execute(int _pos)
 | |
| {
 | |
|     if (run && _pos == position)
 | |
|     {
 | |
|         int i;
 | |
|         int j;
 | |
|         int k;
 | |
|         int sbuff;
 | |
|         int sbegin;
 | |
|         double g1;
 | |
| 
 | |
|         for (i = 0; i < 2 * bsize; i += 2)
 | |
|         {
 | |
|             inaccum[iainidx] = in[i];
 | |
|             iainidx = (iainidx + 1) % iasize;
 | |
|         }
 | |
| 
 | |
|         nsamps += bsize;
 | |
| 
 | |
|         while (nsamps >= fsize)
 | |
|         {
 | |
|             for (i = 0, j = iaoutidx; i < fsize; i++, j = (j + 1) % iasize)
 | |
|                 forfftin[i] = window[i] * inaccum[j];
 | |
| 
 | |
|             iaoutidx = (iaoutidx + incr) % iasize;
 | |
|             nsamps -= incr;
 | |
|             fftwf_execute (Rfor);
 | |
|             calc_gain();
 | |
| 
 | |
|             for (i = 0; i < msize; i++)
 | |
|             {
 | |
|                 g1 = gain * mask[i];
 | |
|                 revfftin[2 * i + 0] = (float) (g1 * forfftout[2 * i + 0]);
 | |
|                 revfftin[2 * i + 1] = (float) (g1 * forfftout[2 * i + 1]);
 | |
|             }
 | |
| 
 | |
|             fftwf_execute (Rrev);
 | |
| 
 | |
|             for (i = 0; i < fsize; i++)
 | |
|                 save[saveidx][i] = window[i] * revfftout[i];
 | |
| 
 | |
|             for (i = ovrlp; i > 0; i--)
 | |
|             {
 | |
|                 sbuff = (saveidx + i) % ovrlp;
 | |
|                 sbegin = incr * (ovrlp - i);
 | |
| 
 | |
|                 for (j = sbegin, k = oainidx; j < incr + sbegin; j++, k = (k + 1) % oasize)
 | |
|                 {
 | |
|                     if ( i == ovrlp)
 | |
|                         outaccum[k]  = save[sbuff][j];
 | |
|                     else
 | |
|                         outaccum[k] += save[sbuff][j];
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             saveidx = (saveidx + 1) % ovrlp;
 | |
|             oainidx = (oainidx + incr) % oasize;
 | |
|         }
 | |
| 
 | |
|         for (i = 0; i < bsize; i++)
 | |
|         {
 | |
|             out[2 * i + 0] = outaccum[oaoutidx];
 | |
|             out[2 * i + 1] = 0.0;
 | |
|             oaoutidx = (oaoutidx + 1) % oasize;
 | |
|         }
 | |
|     }
 | |
|     else if (out != in)
 | |
|     {
 | |
|         std::copy(in, in + bsize * 2, out);
 | |
|     }
 | |
| }
 | |
| 
 | |
| void EMNR::setBuffers(float* _in, float* _out)
 | |
| {
 | |
|     in = _in;
 | |
|     out = _out;
 | |
| }
 | |
| 
 | |
| void EMNR::setSamplerate(int _rate)
 | |
| {
 | |
|     decalc();
 | |
|     rate = _rate;
 | |
|     calc();
 | |
| }
 | |
| 
 | |
| void EMNR::setSize(int _size)
 | |
| {
 | |
|     decalc();
 | |
|     bsize = _size;
 | |
|     calc();
 | |
| }
 | |
| 
 | |
| /********************************************************************************************************
 | |
| *                                                                                                       *
 | |
| *                                           RXA Properties                                              *
 | |
| *                                                                                                       *
 | |
| ********************************************************************************************************/
 | |
| 
 | |
| void EMNR::setGainMethod(int _method)
 | |
| {
 | |
|     g->gain_method = _method;
 | |
| }
 | |
| 
 | |
| void EMNR::setNpeMethod(int _method)
 | |
| {
 | |
|     g->npe_method = _method;
 | |
| }
 | |
| 
 | |
| void EMNR::setAeRun(int _run)
 | |
| {
 | |
|     g->ae_run = _run;
 | |
| }
 | |
| 
 | |
| void EMNR::setAeZetaThresh(double _zetathresh)
 | |
| {
 | |
|     ae->zetaThresh = _zetathresh;
 | |
| }
 | |
| 
 | |
| void EMNR::setAePsi(double _psi)
 | |
| {
 | |
|     ae->psi = _psi;
 | |
| }
 | |
| 
 | |
| } // namespace WDSP
 |