mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-11-27 02:09:14 -05:00
545 lines
13 KiB
C++
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
|