sdrangel/wdsp/cfcomp.cpp

495 wiersze
12 KiB
C++

/* cfcomp.c
This file is part of a program that implements a Software-Defined Radio.
Copyright (C) 2017, 2021 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 "comm.hpp"
#include "cfcomp.hpp"
#include "meterlog10.hpp"
#include "TXA.hpp"
namespace WDSP {
void CFCOMP::calc_cfcwindow()
{
int i;
double arg0;
double arg1;
double cgsum;
double igsum;
double coherent_gain;
double inherent_power_gain;
double wmult;
switch (wintype)
{
case 0:
arg0 = 2.0 * PI / (float)fsize;
cgsum = 0.0;
igsum = 0.0;
for (i = 0; i < fsize; i++)
{
window[i] = sqrt (0.54 - 0.46 * cos((float)i * arg0));
cgsum += window[i];
igsum += window[i] * window[i];
}
coherent_gain = cgsum / (float)fsize;
inherent_power_gain = igsum / (float)fsize;
wmult = 1.0 / sqrt (inherent_power_gain);
for (i = 0; i < fsize; i++)
window[i] *= wmult;
winfudge = sqrt (1.0 / coherent_gain);
break;
case 1:
arg0 = 2.0 * PI / (float)fsize;
cgsum = 0.0;
igsum = 0.0;
for (i = 0; i < fsize; i++)
{
arg1 = cos(arg0 * (float)i);
window[i] = sqrt (+0.21747
+ arg1 * (-0.45325
+ arg1 * (+0.28256
+ arg1 * (-0.04672))));
cgsum += window[i];
igsum += window[i] * window[i];
}
coherent_gain = cgsum / (float)fsize;
inherent_power_gain = igsum / (float)fsize;
wmult = 1.0 / sqrt (inherent_power_gain);
for (i = 0; i < fsize; i++)
window[i] *= wmult;
winfudge = sqrt (1.0 / coherent_gain);
break;
default:
break;
}
}
int CFCOMP::fCOMPcompare (const void *a, const void *b)
{
if (*(double*)a < *(double*)b)
return -1;
else if (*(double*)a == *(double*)b)
return 0;
else
return 1;
}
void CFCOMP::calc_comp()
{
int i;
int j;
double f;
double frac;
double fincr;
double fmax;
double* sary;
precomplin = pow (10.0, 0.05 * precomp);
prepeqlin = pow (10.0, 0.05 * prepeq);
fmax = 0.5 * rate;
for (i = 0; i < nfreqs; i++)
{
F[i] = std::max (F[i], 0.0);
F[i] = std::min (F[i], fmax);
G[i] = std::max (G[i], 0.0);
}
sary = new double[3 * nfreqs];
for (i = 0; i < nfreqs; i++)
{
sary[3 * i + 0] = F[i];
sary[3 * i + 1] = G[i];
sary[3 * i + 2] = E[i];
}
qsort (sary, nfreqs, 3 * sizeof (float), fCOMPcompare);
for (i = 0; i < nfreqs; i++)
{
F[i] = sary[3 * i + 0];
G[i] = sary[3 * i + 1];
E[i] = sary[3 * i + 2];
}
fp[0] = 0.0;
fp[nfreqs + 1] = fmax;
gp[0] = G[0];
gp[nfreqs + 1] = G[nfreqs - 1];
ep[0] = E[0]; // cutoff?
ep[nfreqs + 1] = E[nfreqs - 1]; // cutoff?
for (i = 0, j = 1; i < nfreqs; i++, j++)
{
fp[j] = F[i];
gp[j] = G[i];
ep[j] = E[i];
}
fincr = rate / (float)fsize;
j = 0;
for (i = 0; i < msize; i++)
{
f = fincr * (float)i;
while (f >= fp[j + 1] && j < nfreqs) j++;
frac = (f - fp[j]) / (fp[j + 1] - fp[j]);
comp[i] = pow (10.0, 0.05 * (frac * gp[j + 1] + (1.0 - frac) * gp[j]));
peq[i] = pow (10.0, 0.05 * (frac * ep[j + 1] + (1.0 - frac) * ep[j]));
cfc_gain[i] = precomplin * comp[i];
}
delete[] sary;
}
void CFCOMP::calc_cfcomp()
{
incr = fsize / 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);
cmask.resize(msize);
mask.resize(msize);
cfc_gain.resize(msize);
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_cfcwindow();
pregain = (2.0 * winfudge) / (double)fsize;
postgain = 0.5 / ((double)ovrlp * winfudge);
fp.resize(nfreqs + 2);
gp.resize(nfreqs + 2);
ep.resize(nfreqs + 2);
comp.resize(msize);
peq.resize(msize);
calc_comp();
gain = 0.0;
mmult = exp (-1.0 / (rate * ovrlp * mtau));
dmult = exp (-(float)fsize / (rate * ovrlp * dtau));
delta.resize(msize);
delta_copy.resize(msize);
cfc_gain_copy.resize(msize);
}
void CFCOMP::decalc_cfcomp()
{
fftwf_destroy_plan(Rrev);
fftwf_destroy_plan(Rfor);
}
CFCOMP::CFCOMP(
int _run,
int _position,
int _peq_run,
int _size,
float* _in,
float* _out,
int _fsize,
int _ovrlp,
int _rate,
int _wintype,
int _comp_method,
int _nfreqs,
double _precomp,
double _prepeq,
const double* _F,
const double* _G,
const double* _E,
double _mtau,
double _dtau
) :
run (_run),
position(_position),
bsize(_size),
in(_in),
out(_out),
fsize(_fsize),
ovrlp(_ovrlp),
rate(_rate),
wintype(_wintype),
comp_method(_comp_method),
nfreqs(_nfreqs),
precomp(_precomp),
peq_run(_peq_run),
prepeq(_prepeq),
mtau(_mtau), // compression metering time constant
dtau(_dtau) // compression display time constant
{
F.resize(nfreqs);
G.resize(nfreqs);
E.resize(nfreqs);
std::copy(_F, _F + nfreqs, F.begin());
std::copy(_G, _G + nfreqs, G.begin());
std::copy(_E, _E + nfreqs, E.begin());
calc_cfcomp();
}
CFCOMP::~CFCOMP()
{
decalc_cfcomp();
}
void CFCOMP::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;
gain = 0.0;
std::fill(delta.begin(), delta.end(), 0);
}
void CFCOMP::calc_mask()
{
int i;
double _comp;
double _mask;
double _delta;
if (comp_method == 0)
{
double mag;
double test;
for (i = 0; i < msize; i++)
{
mag = sqrt (forfftout[2 * i + 0] * forfftout[2 * i + 0]
+ forfftout[2 * i + 1] * forfftout[2 * i + 1]);
_comp = cfc_gain[i];
test = _comp * mag;
if (test > 1.0)
_mask = 1.0 / mag;
else
_mask = _comp;
cmask[i] = _mask;
if (test > gain) gain = test;
else gain = mmult * gain;
_delta = cfc_gain[i] - cmask[i];
if (_delta > delta[i]) delta[i] = _delta;
else delta[i] *= dmult;
}
}
if (peq_run)
{
for (i = 0; i < msize; i++)
{
mask[i] = cmask[i] * prepeqlin * peq[i];
}
}
else
std::copy(cmask.begin(), cmask.end(), mask.begin());
mask_ready = 1;
}
void CFCOMP::execute(int pos)
{
if (run && pos == position)
{
int i;
int j;
int k;
int sbuff;
int sbegin;
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] = (float) (pregain * window[i] * inaccum[j]);
iaoutidx = (iaoutidx + incr) % iasize;
nsamps -= incr;
fftwf_execute (Rfor);
calc_mask();
for (i = 0; i < msize; i++)
{
revfftin[2 * i + 0] = (float) (mask[i] * forfftout[2 * i + 0]);
revfftin[2 * i + 1] = (float) (mask[i] * forfftout[2 * i + 1]);
}
fftwf_execute (Rrev);
for (i = 0; i < fsize; i++)
save[saveidx][i] = postgain * 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] = (float) (outaccum[oaoutidx]);
out[2 * i + 1] = 0.0;
oaoutidx = (oaoutidx + 1) % oasize;
}
}
else if (out != in)
std::copy(in, in + bsize * 2, out);
}
void CFCOMP::setBuffers(float* _in, float* _out)
{
in = _in;
out = _out;
}
void CFCOMP::setSamplerate(int _rate)
{
decalc_cfcomp();
rate = _rate;
calc_cfcomp();
}
void CFCOMP::setSize(int size)
{
decalc_cfcomp();
bsize = size;
calc_cfcomp();
}
/********************************************************************************************************
* *
* TXA Properties *
* *
********************************************************************************************************/
void CFCOMP::setRun(int _run)
{
if (run != _run) {
run = _run;
}
}
void CFCOMP::setPosition(int pos)
{
if (position != pos) {
position = pos;
}
}
void CFCOMP::setProfile(int _nfreqs, const double* _F, const double* _G, const double* _E)
{
nfreqs = _nfreqs < 1 ? 1 : _nfreqs;
F.resize(nfreqs);
G.resize(nfreqs);
E.resize(nfreqs);
std::copy(_F, _F + nfreqs, F.begin());
std::copy(_G, _G + nfreqs, G.begin());
std::copy(_E, _E + nfreqs, E.begin());
fp.resize(nfreqs + 2);
gp.resize(nfreqs + 2);
ep.resize(nfreqs + 2);
calc_comp();
}
void CFCOMP::setPrecomp(double _precomp)
{
if (precomp != _precomp)
{
precomp = _precomp;
precomplin = pow (10.0, 0.05 * precomp);
for (int i = 0; i < msize; i++)
{
cfc_gain[i] = precomplin * comp[i];
}
}
}
void CFCOMP::setPeqRun(int _run)
{
if (peq_run != _run) {
peq_run = _run;
}
}
void CFCOMP::setPrePeq(double _prepeq)
{
prepeq = _prepeq;
prepeqlin = pow (10.0, 0.05 * prepeq);
}
void CFCOMP::getDisplayCompression(double* comp_values, int* ready)
{
if ((*ready = mask_ready))
{
std::copy(delta.begin(), delta.end(), delta_copy.begin());
std::copy(cfc_gain.begin(), cfc_gain.end(), cfc_gain_copy.begin());
mask_ready = 0;
}
if (*ready)
{
for (int i = 0; i < msize; i++)
comp_values[i] = 20.0 * MemLog::mlog10 (cfc_gain_copy[i] / (cfc_gain_copy[i] - delta_copy[i]));
}
}
} // namespace WDSP