SDRPlusPlus/core/src/dsp/filter.h

489 wiersze
14 KiB
C++

#pragma once
#include <thread>
#include <dsp/stream.h>
#include <dsp/types.h>
#include <vector>
#include <dsp/math.h>
#include <spdlog/spdlog.h>
#define GET_FROM_RIGHT_BUF(buffer, delayLine, delayLineSz, n) (((n) < 0) ? delayLine[(delayLineSz) + (n)] : buffer[(n)])
namespace dsp {
inline void BlackmanWindow(std::vector<float>& taps, float sampleRate, float cutoff, float transWidth, int addedTaps = 0) {
taps.clear();
float fc = cutoff / sampleRate;
if (fc > 1.0f) {
fc = 1.0f;
}
int _M = (4.0f / (transWidth / sampleRate)) + (float)addedTaps;
if (_M < 4) {
_M = 4;
}
if (_M % 2 == 0) { _M++; }
float M = _M;
float sum = 0.0f;
float val;
for (int i = 0; i < _M; i++) {
val = (sin(2.0f * M_PI * fc * ((float)i - (M / 2))) / ((float)i - (M / 2))) * (0.42f - (0.5f * cos(2.0f * M_PI / M)) + (0.8f * cos(4.0f * M_PI / M)));
taps.push_back(val);
sum += val;
}
for (int i = 0; i < M; i++) {
taps[i] /= sum;
}
}
class DecimatingFIRFilter {
public:
DecimatingFIRFilter() {
}
DecimatingFIRFilter(stream<complex_t>* input, std::vector<float> taps, int blockSize, float decim) {
output.init((blockSize * 2) / decim);
_in = input;
_blockSize = blockSize;
_tapCount = taps.size();
delayBuf = new complex_t[_tapCount];
_taps = new float[_tapCount];
for (int i = 0; i < _tapCount; i++) {
_taps[i] = taps[i];
}
_decim = decim;
for (int i = 0; i < _tapCount; i++) {
delayBuf[i].i = 0.0f;
delayBuf[i].q = 0.0f;
}
running = false;
}
void init(stream<complex_t>* input, std::vector<float>& taps, int blockSize, float decim) {
output.init((blockSize * 2) / decim);
_in = input;
_blockSize = blockSize;
_tapCount = taps.size();
delayBuf = new complex_t[_tapCount];
_taps = new float[_tapCount];
for (int i = 0; i < _tapCount; i++) {
_taps[i] = taps[i];
}
_decim = decim;
for (int i = 0; i < _tapCount; i++) {
delayBuf[i].i = 0.0f;
delayBuf[i].q = 0.0f;
}
running = false;
}
void start() {
if (running) {
return;
}
running = true;
_workerThread = std::thread(_worker, this);
}
void stop() {
if (!running) {
return;
}
_in->stopReader();
output.stopWriter();
_workerThread.join();
_in->clearReadStop();
output.clearWriteStop();
running = false;
}
void setTaps(std::vector<float>& taps) {
if (running) {
return;
}
_tapCount = taps.size();
delete[] _taps;
delete[] delayBuf;
_taps = new float[_tapCount];
delayBuf = new complex_t[_tapCount];
for (int i = 0; i < _tapCount; i++) {
_taps[i] = taps[i];
delayBuf[i].i = 0;
delayBuf[i].q = 0;
}
}
void setInput(stream<complex_t>* input) {
if (running) {
return;
}
_in = input;
}
void setDecimation(float decimation) {
if (running) {
return;
}
_decim = decimation;
output.setMaxLatency((_blockSize * 2) / _decim);
}
void setBlockSize(int blockSize) {
if (running) {
return;
}
_blockSize = blockSize;
output.setMaxLatency(getOutputBlockSize() * 2);
}
int getOutputBlockSize() {
return _blockSize / _decim;
}
stream<complex_t> output;
private:
static void _worker(DecimatingFIRFilter* _this) {
int outputSize = _this->_blockSize / _this->_decim;
complex_t* inBuf = new complex_t[_this->_blockSize];
complex_t* outBuf = new complex_t[outputSize];
float tap = 0.0f;
int delayOff;
void* delayStart = &inBuf[_this->_blockSize - (_this->_tapCount - 1)];
int delaySize = (_this->_tapCount - 1) * sizeof(complex_t);
int blockSize = _this->_blockSize;
int outBufferLength = outputSize * sizeof(complex_t);
int tapCount = _this->_tapCount;
int decim = _this->_decim;
complex_t* delayBuf = _this->delayBuf;
int id = 0;
while (true) {
if (_this->_in->read(inBuf, blockSize) < 0) { break; };
memset(outBuf, 0, outBufferLength);
for (int t = 0; t < tapCount; t++) {
tap = _this->_taps[t];
if (tap == 0.0f) {
continue;
}
delayOff = tapCount - t;
id = 0;
for (int i = 0; i < blockSize; i += decim) {
if (i < t) {
outBuf[id].i += tap * delayBuf[delayOff + i].i;
outBuf[id].q += tap * delayBuf[delayOff + i].q;
}
else {
outBuf[id].i += tap * inBuf[i - t].i;
outBuf[id].q += tap * inBuf[i - t].q;
}
id++;
}
}
memcpy(delayBuf, delayStart, delaySize);
if (_this->output.write(outBuf, outputSize) < 0) { break; };
}
delete[] inBuf;
delete[] outBuf;
}
stream<complex_t>* _in;
complex_t* delayBuf;
int _blockSize;
int _tapCount = 0;
float _decim;
std::thread _workerThread;
float* _taps;
bool running;
};
class FloatDecimatingFIRFilter {
public:
FloatDecimatingFIRFilter() {
}
FloatDecimatingFIRFilter(stream<float>* input, std::vector<float> taps, int blockSize, float decim) {
output.init((blockSize * 2) / decim);
_in = input;
_blockSize = blockSize;
_tapCount = taps.size();
delayBuf = new float[_tapCount];
_taps = new float[_tapCount];
for (int i = 0; i < _tapCount; i++) {
_taps[i] = taps[i];
}
_decim = decim;
for (int i = 0; i < _tapCount; i++) {
delayBuf[i] = 0.0f;
}
running = false;
}
void init(stream<float>* input, std::vector<float>& taps, int blockSize, float decim) {
output.init((blockSize * 2) / decim);
_in = input;
_blockSize = blockSize;
_tapCount = taps.size();
delayBuf = new float[_tapCount];
_taps = new float[_tapCount];
for (int i = 0; i < _tapCount; i++) {
_taps[i] = taps[i];
}
_decim = decim;
for (int i = 0; i < _tapCount; i++) {
delayBuf[i] = 0.0f;
}
running = false;
}
void start() {
if (running) {
return;
}
running = true;
_workerThread = std::thread(_worker, this);
}
void stop() {
if (!running) {
return;
}
_in->stopReader();
output.stopWriter();
_workerThread.join();
_in->clearReadStop();
output.clearWriteStop();
running = false;
}
void setTaps(std::vector<float>& taps) {
if (running) {
return;
}
_tapCount = taps.size();
delete[] _taps;
delete[] delayBuf;
_taps = new float[_tapCount];
delayBuf = new float[_tapCount];
for (int i = 0; i < _tapCount; i++) {
_taps[i] = taps[i];
delayBuf[i] = 0;
}
}
void setInput(stream<float>* input) {
if (running) {
return;
}
_in = input;
}
void setDecimation(float decimation) {
if (running) {
return;
}
_decim = decimation;
output.setMaxLatency((_blockSize * 2) / _decim);
}
void setBlockSize(int blockSize) {
if (running) {
return;
}
_blockSize = blockSize;
output.setMaxLatency((_blockSize * 2) / _decim);
}
int getOutputBlockSize() {
return _blockSize / _decim;
}
stream<float> output;
private:
static void _worker(FloatDecimatingFIRFilter* _this) {
int outputSize = _this->_blockSize / _this->_decim;
float* inBuf = new float[_this->_blockSize];
float* outBuf = new float[outputSize];
float tap = 0.0f;
int delayOff;
void* delayStart = &inBuf[_this->_blockSize - (_this->_tapCount - 1)];
int delaySize = (_this->_tapCount - 1) * sizeof(float);
int blockSize = _this->_blockSize;
int outBufferLength = outputSize * sizeof(float);
int tapCount = _this->_tapCount;
int decim = _this->_decim;
float* delayBuf = _this->delayBuf;
int id = 0;
while (true) {
if (_this->_in->read(inBuf, blockSize) < 0) { break; };
memset(outBuf, 0, outBufferLength);
for (int t = 0; t < tapCount; t++) {
tap = _this->_taps[t];
if (tap == 0.0f) {
continue;
}
delayOff = tapCount - t;
id = 0;
for (int i = 0; i < blockSize; i += decim) {
if (i < t) {
outBuf[id] += tap * delayBuf[delayOff + i];
id++;
continue;
}
outBuf[id] += tap * inBuf[i - t];
id++;
}
}
memcpy(delayBuf, delayStart, delaySize);
if (_this->output.write(outBuf, outputSize) < 0) { break; };
}
delete[] inBuf;
delete[] outBuf;
}
stream<float>* _in;
float* delayBuf;
int _blockSize;
int _tapCount = 0;
float _decim;
std::thread _workerThread;
float* _taps;
bool running;
};
class FMDeemphasis {
public:
FMDeemphasis() {
}
FMDeemphasis(stream<float>* input, int bufferSize, float tau, float sampleRate) : output(bufferSize * 2) {
_in = input;
_bufferSize = bufferSize;
bypass = false;
_tau = tau;
_sampleRate = sampleRate;
}
void init(stream<float>* input, int bufferSize, float tau, float sampleRate) {
output.init(bufferSize * 2);
_in = input;
_bufferSize = bufferSize;
bypass = false;
_tau = tau;
_sampleRate = sampleRate;
}
void start() {
if (running) {
return;
}
_workerThread = std::thread(_worker, this);
running = true;
}
void stop() {
if (!running) {
return;
}
_in->stopReader();
output.stopWriter();
_workerThread.join();
_in->clearReadStop();
output.clearWriteStop();
running = false;
}
void setBlockSize(int blockSize) {
if (running) {
return;
}
_bufferSize = blockSize;
output.setMaxLatency(blockSize * 2);
}
void setSamplerate(float sampleRate) {
if (running) {
return;
}
_sampleRate = sampleRate;
}
void setTau(float tau) {
if (running) {
return;
}
_tau = tau;
}
stream<float> output;
bool bypass;
private:
static void _worker(FMDeemphasis* _this) {
float* inBuf = new float[_this->_bufferSize];
float* outBuf = new float[_this->_bufferSize];
int count = _this->_bufferSize;
float lastOut = 0.0f;
float dt = 1.0f / _this->_sampleRate;
float alpha = dt / (_this->_tau + dt);
while (true) {
if (_this->_in->read(inBuf, count) < 0) { break; };
if (_this->bypass) {
if (_this->output.write(inBuf, count) < 0) { break; };
continue;
}
if (isnan(lastOut)) {
lastOut = 0.0f;
}
outBuf[0] = (alpha * inBuf[0]) + ((1-alpha) * lastOut);
for (int i = 1; i < count; i++) {
outBuf[i] = (alpha * inBuf[i]) + ((1 - alpha) * outBuf[i - 1]);
}
lastOut = outBuf[count - 1];
if (_this->output.write(outBuf, count) < 0) { break; };
}
delete[] inBuf;
delete[] outBuf;
}
stream<float>* _in;
int _bufferSize;
std::thread _workerThread;
bool running = false;
float _sampleRate;
float _tau;
};
};