1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-12-23 10:05:46 -05:00
sdrangel/wdsp/nbp.cpp
2024-08-10 12:21:04 +02:00

545 lines
13 KiB
C++

/* nbp.c
This file is part of a program that implements a Software-Defined Radio.
Copyright (C) 2015, 2016 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 <array>
#include "comm.hpp"
#include "fir.hpp"
#include "fircore.hpp"
#include "bpsnba.hpp"
#include "nbp.hpp"
namespace WDSP {
/********************************************************************************************************
* *
* Notch Database *
* *
********************************************************************************************************/
NOTCHDB::NOTCHDB(int _master_run, int _maxnotches)
{
master_run = _master_run;
maxnotches = _maxnotches;
nn = 0;
fcenter.resize(maxnotches);
fwidth.resize(maxnotches);
nlow.resize(maxnotches);
nhigh.resize(maxnotches);
active.resize(maxnotches);
}
int NOTCHDB::addNotch(int notch, double _fcenter, double _fwidth, int _active)
{
int i;
int j;
int rval;
if (notch <= nn && nn < maxnotches)
{
nn++;
for (i = nn - 2, j = nn - 1; i >= notch; i--, j--)
{
fcenter[j] = fcenter[i];
fwidth[j] = fwidth[i];
nlow[j] = nlow[i];
nhigh[j] = nhigh[i];
active[j] = active[i];
}
fcenter[notch] = _fcenter;
fwidth[notch] = _fwidth;
nlow[notch] = _fcenter - 0.5 * _fwidth;
nhigh[notch] = _fcenter + 0.5 * _fwidth;
active[notch] = _active;
rval = 0;
}
else
rval = -1;
return rval;
}
int NOTCHDB::getNotch(int _notch, double* _fcenter, double* _fwidth, int* _active)
{
int rval;
if (_notch < nn)
{
*_fcenter = fcenter[_notch];
*_fwidth = fwidth[_notch];
*_active = active[_notch];
rval = 0;
}
else
{
*_fcenter = -1.0;
*_fwidth = 0.0;
*_active = -1;
rval = -1;
}
return rval;
}
int NOTCHDB::deleteNotch(int _notch)
{
int i;
int j;
int rval;
if (_notch < nn)
{
nn--;
for (i = _notch, j = _notch + 1; i < nn; i++, j++)
{
fcenter[i] = fcenter[j];
fwidth[i] = fwidth[j];
nlow[i] = nlow[j];
nhigh[i] = nhigh[j];
active[i] = active[j];
}
rval = 0;
}
else
rval = -1;
return rval;
}
int NOTCHDB::editNotch(int _notch, double _fcenter, double _fwidth, int _active)
{
int rval;
if (_notch < nn)
{
fcenter[_notch] = _fcenter;
fwidth[_notch] = _fwidth;
nlow[_notch] = _fcenter - 0.5 * _fwidth;
nhigh[_notch] = _fcenter + 0.5 * _fwidth;
active[_notch] = _active;
rval = 0;
}
else
rval = -1;
return rval;
}
void NOTCHDB::getNumNotches(int* _nnotches) const
{
*_nnotches = nn;
}
/********************************************************************************************************
* *
* Notched Bandpass Filter *
* *
********************************************************************************************************/
void NBP::fir_mbandpass (std::vector<float>& impulse, int N, int nbp, const double* flow, const double* fhigh, double rate, double scale, int wintype)
{
impulse.resize(N * 2);
std::fill(impulse.begin(), impulse.end(), 0);
for (int k = 0; k < nbp; k++)
{
std::vector<float> imp;
FIR::fir_bandpass (imp, N, flow[k], fhigh[k], rate, wintype, 1, scale);
for (int i = 0; i < N; i++)
{
impulse[2 * i + 0] += imp[2 * i + 0];
impulse[2 * i + 1] += imp[2 * i + 1];
}
}
}
double NBP::min_notch_width() const
{
double min_width;
switch (wintype)
{
case 0:
min_width = 1600.0 / (nc / 256) * (rate / 48000);
break;
case 1:
min_width = 2200.0 / (nc / 256) * (rate / 48000);
break;
default:
min_width = 0;
}
return min_width;
}
int NBP::make_nbp (
int nn,
std::vector<int>& active,
std::vector<double>& center,
std::vector<double>& width,
std::vector<double>& nlow,
std::vector<double>& nhigh,
double minwidth,
int autoincr,
double flow,
double fhigh,
std::vector<double>& bplow,
std::vector<double>& bphigh,
int* havnotch
)
{
int nbp;
int nnbp;
int adds;
int i;
double nl;
double nh;
std::array<int, 1024> del;
if (fhigh > flow)
{
bplow[0] = flow;
bphigh[0] = fhigh;
nbp = 1;
}
else
{
nbp = 0;
return nbp;
}
*havnotch = 0;
for (int k = 0; k < nn; k++)
{
if (autoincr && width[k] < minwidth)
{
nl = center[k] - 0.5 * minwidth;
nh = center[k] + 0.5 * minwidth;
}
else
{
nl = nlow[k];
nh = nhigh[k];
}
if (active[k] && (nh > flow && nl < fhigh))
{
*havnotch = 1;
adds = 0;
for (i = 0; i < nbp; i++)
{
if (nh > bplow[i] && nl < bphigh[i])
{
if (nl <= bplow[i] && nh >= bphigh[i])
{
del[i] = 1;
}
else if (nl > bplow[i] && nh < bphigh[i])
{
bplow[nbp + adds] = nh;
bphigh[nbp + adds] = bphigh[i];
bphigh[i] = nl;
adds++;
}
else if (nl <= bplow[i] && nh > bplow[i])
{
bplow[i] = nh;
}
else if (nl < bphigh[i] && nh >= bphigh[i])
{
bphigh[i] = nl;
}
}
}
nbp += adds;
nnbp = nbp;
for (i = 0; i < nbp; i++)
{
if (del[i] == 1)
{
nnbp--;
for (int j = i; j < nnbp; j++)
{
bplow[j] = bplow[j + 1];
bphigh[j] = bphigh[j + 1];
}
del[i] = 0;
}
}
nbp = nnbp;
}
}
return nbp;
}
void NBP::calc_lightweight()
{ // calculate and set new impulse response; used when changing tune freq or shift freq
double fl;
double fh;
double offset;
NOTCHDB *b = notchdb;
if (fnfrun)
{
offset = b->tunefreq + b->shift;
fl = flow + offset;
fh = fhigh + offset;
numpb = make_nbp (
b->nn,
b->active,
b->fcenter,
b->fwidth,
b->nlow,
b->nhigh,
min_notch_width(),
autoincr,
fl,
fh,
bplow,
bphigh,
&havnotch
);
// when tuning, no need to recalc filter if there were not and are not any notches in passband
if (hadnotch || havnotch)
{
for (int i = 0; i < numpb; i++)
{
bplow[i] -= offset;
bphigh[i] -= offset;
}
fir_mbandpass (
impulse,
nc,
numpb,
bplow.data(),
bphigh.data(),
rate,
gain / (float)(2 * size),
wintype
);
fircore->setImpulse(impulse, 1);
// print_impulse ("nbp.txt", size + 1, impulse, 1, 0);
}
hadnotch = havnotch;
}
else
hadnotch = 1;
}
void NBP::calc_impulse ()
{ // calculates impulse response; for create_fircore() and parameter changes
double fl;
double fh;
double offset;
NOTCHDB *b = notchdb;
if (fnfrun)
{
offset = b->tunefreq + b->shift;
fl = flow + offset;
fh = fhigh + offset;
numpb = make_nbp (
b->nn,
b->active,
b->fcenter,
b->fwidth,
b->nlow,
b->nhigh,
min_notch_width(),
autoincr,
fl,
fh,
bplow,
bphigh,
&havnotch
);
for (int i = 0; i < numpb; i++)
{
bplow[i] -= offset;
bphigh[i] -= offset;
}
fir_mbandpass (
impulse,
nc,
numpb,
bplow.data(),
bphigh.data(),
rate,
gain / (float)(2 * size),
wintype
);
}
else
{
FIR::fir_bandpass(
impulse,
nc,
flow,
fhigh,
rate,
wintype,
1,
gain / (float)(2 * size)
);
}
}
NBP::NBP(
int _run,
int _fnfrun,
int _position,
int _size,
int _nc,
int _mp,
float* _in,
float* _out,
double _flow,
double _fhigh,
int _rate,
int _wintype,
double _gain,
int _autoincr,
int _maxpb,
NOTCHDB* _notchdb
) :
run(_run),
fnfrun(_fnfrun),
position(_position),
size(_size),
nc(_nc),
mp(_mp),
rate((double) _rate),
wintype(_wintype),
gain(_gain),
in(_in),
out(_out),
autoincr(_autoincr),
flow(_flow),
fhigh(_fhigh),
maxpb(_maxpb),
notchdb(_notchdb)
{
bplow.resize(maxpb);
bphigh.resize(maxpb);
calc_impulse ();
fircore = new FIRCORE(size, in, out, mp, impulse);
}
NBP::~NBP()
{
delete fircore;
}
void NBP::flush()
{
fircore->flush();
}
void NBP::execute (int pos)
{
if (run && pos == position)
fircore->execute();
else if (in != out)
std::copy( in, in + size * 2, out);
}
void NBP::setBuffers(float* _in, float* _out)
{
in = _in;
out = _out;
fircore->setBuffers(in, out);
}
void NBP::setSamplerate(int _rate)
{
rate = _rate;
calc_impulse ();
fircore->setImpulse(impulse, 1);
}
void NBP::setSize(int _size)
{
// NOTE: 'size' must be <= 'nc'
size = _size;
fircore->setSize(size);
calc_impulse ();
fircore->setImpulse(impulse, 1);
}
void NBP::setNc()
{
calc_impulse();
fircore->setNc(impulse);
}
void NBP::setMp()
{
fircore->setMp(mp);
}
/********************************************************************************************************
* *
* Public Properties *
* *
********************************************************************************************************/
// FILTER PROPERTIES
void NBP::SetRun(int _run)
{
run = _run;
}
void NBP::SetFreqs(double _flow, double _fhigh)
{
if ((flow != _flow) || (fhigh != _fhigh))
{
flow = _flow;
fhigh = _fhigh;
calc_impulse();
fircore->setImpulse(impulse, 1);
}
}
void NBP::SetNC(int _nc)
{
// NOTE: 'nc' must be >= 'size'
if (nc != _nc)
{
nc = _nc;
setNc();
}
}
void NBP::SetMP(int _mp)
{
if (mp != _mp)
{
mp = _mp;
setMp();
}
}
void NBP::GetMinNotchWidth(double* minwidth) const
{
*minwidth = min_notch_width();
}
} // namespace WDSP