2020-06-10 04:13:56 +02:00
|
|
|
#pragma once
|
|
|
|
#include <thread>
|
2020-06-22 16:45:57 +02:00
|
|
|
#include <dsp/stream.h>
|
|
|
|
#include <dsp/types.h>
|
2020-07-19 15:59:44 +02:00
|
|
|
#include <dsp/source.h>
|
|
|
|
#include <dsp/math.h>
|
2020-06-22 16:45:57 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
TODO:
|
|
|
|
- Add a sample rate ajustment function to all demodulators
|
|
|
|
*/
|
2020-06-10 04:13:56 +02:00
|
|
|
|
2020-06-15 15:53:45 +02:00
|
|
|
#define FAST_ATAN2_COEF1 3.1415926535f / 4.0f
|
|
|
|
#define FAST_ATAN2_COEF2 3.0f * FAST_ATAN2_COEF1
|
|
|
|
|
2020-06-22 16:45:57 +02:00
|
|
|
inline float fast_arctan2(float y, float x) {
|
2020-08-10 02:30:25 +02:00
|
|
|
float abs_y = fabs(y) + (1e-10);
|
2020-06-15 15:53:45 +02:00
|
|
|
float r, angle;
|
2020-08-10 02:30:25 +02:00
|
|
|
if (x>=0) {
|
2020-06-15 15:53:45 +02:00
|
|
|
r = (x - abs_y) / (x + abs_y);
|
|
|
|
angle = FAST_ATAN2_COEF1 - FAST_ATAN2_COEF1 * r;
|
|
|
|
}
|
2020-08-10 02:30:25 +02:00
|
|
|
else {
|
2020-06-15 15:53:45 +02:00
|
|
|
r = (x + abs_y) / (abs_y - x);
|
|
|
|
angle = FAST_ATAN2_COEF2 - FAST_ATAN2_COEF1 * r;
|
|
|
|
}
|
|
|
|
if (y < 0) {
|
|
|
|
return -angle;
|
|
|
|
}
|
|
|
|
return angle;
|
|
|
|
}
|
2020-06-10 04:13:56 +02:00
|
|
|
|
2020-06-22 16:45:57 +02:00
|
|
|
namespace dsp {
|
2020-06-10 04:13:56 +02:00
|
|
|
class FMDemodulator {
|
|
|
|
public:
|
2020-06-10 18:52:07 +02:00
|
|
|
FMDemodulator() {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-06-22 16:45:57 +02:00
|
|
|
FMDemodulator(stream<complex_t>* in, float deviation, long sampleRate, int blockSize) : output(blockSize * 2) {
|
2020-06-15 15:53:45 +02:00
|
|
|
running = false;
|
2020-06-10 04:13:56 +02:00
|
|
|
_input = in;
|
2020-06-22 16:45:57 +02:00
|
|
|
_blockSize = blockSize;
|
2020-06-10 04:13:56 +02:00
|
|
|
_phase = 0.0f;
|
2020-07-19 15:59:44 +02:00
|
|
|
_deviation = deviation;
|
|
|
|
_sampleRate = sampleRate;
|
2020-06-10 04:13:56 +02:00
|
|
|
_phasorSpeed = (2 * 3.1415926535) / (sampleRate / deviation);
|
|
|
|
}
|
|
|
|
|
2020-06-22 16:45:57 +02:00
|
|
|
void init(stream<complex_t>* in, float deviation, long sampleRate, int blockSize) {
|
|
|
|
output.init(blockSize * 2);
|
2020-06-15 15:53:45 +02:00
|
|
|
running = false;
|
2020-06-10 18:52:07 +02:00
|
|
|
_input = in;
|
2020-06-22 16:45:57 +02:00
|
|
|
_blockSize = blockSize;
|
2020-06-10 18:52:07 +02:00
|
|
|
_phase = 0.0f;
|
|
|
|
_phasorSpeed = (2 * 3.1415926535) / (sampleRate / deviation);
|
|
|
|
}
|
|
|
|
|
2020-06-10 04:13:56 +02:00
|
|
|
void start() {
|
2020-06-15 15:53:45 +02:00
|
|
|
if (running) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
running = true;
|
2020-06-10 04:13:56 +02:00
|
|
|
_workerThread = std::thread(_worker, this);
|
|
|
|
}
|
|
|
|
|
2020-06-15 15:53:45 +02:00
|
|
|
void stop() {
|
|
|
|
if (!running) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
_input->stopReader();
|
|
|
|
output.stopWriter();
|
|
|
|
_workerThread.join();
|
|
|
|
running = false;
|
|
|
|
_input->clearReadStop();
|
|
|
|
output.clearWriteStop();
|
|
|
|
}
|
|
|
|
|
2020-06-22 16:45:57 +02:00
|
|
|
void setBlockSize(int blockSize) {
|
|
|
|
if (running) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
_blockSize = blockSize;
|
|
|
|
output.setMaxLatency(_blockSize * 2);
|
|
|
|
}
|
|
|
|
|
2020-07-19 15:59:44 +02:00
|
|
|
void setSampleRate(float sampleRate) {
|
|
|
|
_sampleRate = sampleRate;
|
|
|
|
_phasorSpeed = (2 * 3.1415926535) / (sampleRate / _deviation);
|
|
|
|
}
|
|
|
|
|
|
|
|
void setDeviation(float deviation) {
|
|
|
|
_deviation = deviation;
|
|
|
|
_phasorSpeed = (2 * 3.1415926535) / (_sampleRate / _deviation);
|
|
|
|
}
|
|
|
|
|
2020-06-10 04:13:56 +02:00
|
|
|
stream<float> output;
|
|
|
|
|
|
|
|
private:
|
|
|
|
static void _worker(FMDemodulator* _this) {
|
2020-06-22 16:45:57 +02:00
|
|
|
complex_t* inBuf = new complex_t[_this->_blockSize];
|
|
|
|
float* outBuf = new float[_this->_blockSize];
|
2020-06-10 04:13:56 +02:00
|
|
|
float diff = 0;
|
|
|
|
float currentPhase = 0;
|
|
|
|
while (true) {
|
2020-06-22 16:45:57 +02:00
|
|
|
if (_this->_input->read(inBuf, _this->_blockSize) < 0) { return; };
|
|
|
|
for (int i = 0; i < _this->_blockSize; i++) {
|
2020-06-15 15:53:45 +02:00
|
|
|
currentPhase = fast_arctan2(inBuf[i].i, inBuf[i].q);
|
2020-06-10 04:13:56 +02:00
|
|
|
diff = currentPhase - _this->_phase;
|
2020-06-15 15:53:45 +02:00
|
|
|
if (diff > 3.1415926535f) { diff -= 2 * 3.1415926535f; }
|
|
|
|
else if (diff <= -3.1415926535f) { diff += 2 * 3.1415926535f; }
|
2020-06-10 04:13:56 +02:00
|
|
|
outBuf[i] = diff / _this->_phasorSpeed;
|
|
|
|
_this->_phase = currentPhase;
|
|
|
|
}
|
2020-06-22 16:45:57 +02:00
|
|
|
if (_this->output.write(outBuf, _this->_blockSize) < 0) { return; };
|
2020-06-10 04:13:56 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
stream<complex_t>* _input;
|
2020-06-15 15:53:45 +02:00
|
|
|
bool running;
|
2020-06-22 16:45:57 +02:00
|
|
|
int _blockSize;
|
2020-06-10 04:13:56 +02:00
|
|
|
float _phase;
|
|
|
|
float _phasorSpeed;
|
2020-07-19 15:59:44 +02:00
|
|
|
float _deviation;
|
|
|
|
float _sampleRate;
|
2020-06-10 04:13:56 +02:00
|
|
|
std::thread _workerThread;
|
|
|
|
};
|
2020-06-15 15:53:45 +02:00
|
|
|
|
|
|
|
|
|
|
|
class AMDemodulator {
|
|
|
|
public:
|
|
|
|
AMDemodulator() {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-06-22 16:45:57 +02:00
|
|
|
AMDemodulator(stream<complex_t>* in, int blockSize) : output(blockSize * 2) {
|
2020-06-15 15:53:45 +02:00
|
|
|
running = false;
|
|
|
|
_input = in;
|
2020-06-22 16:45:57 +02:00
|
|
|
_blockSize = blockSize;
|
2020-06-15 15:53:45 +02:00
|
|
|
}
|
|
|
|
|
2020-06-22 16:45:57 +02:00
|
|
|
void init(stream<complex_t>* in, int blockSize) {
|
|
|
|
output.init(blockSize * 2);
|
2020-06-15 15:53:45 +02:00
|
|
|
running = false;
|
|
|
|
_input = in;
|
2020-06-22 16:45:57 +02:00
|
|
|
_blockSize = blockSize;
|
2020-06-15 15:53:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void start() {
|
|
|
|
if (running) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
running = true;
|
|
|
|
_workerThread = std::thread(_worker, this);
|
|
|
|
}
|
|
|
|
|
|
|
|
void stop() {
|
|
|
|
if (!running) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
_input->stopReader();
|
|
|
|
output.stopWriter();
|
|
|
|
_workerThread.join();
|
|
|
|
running = false;
|
|
|
|
_input->clearReadStop();
|
|
|
|
output.clearWriteStop();
|
|
|
|
}
|
|
|
|
|
2020-06-22 16:45:57 +02:00
|
|
|
void setBlockSize(int blockSize) {
|
|
|
|
if (running) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
_blockSize = blockSize;
|
|
|
|
output.setMaxLatency(_blockSize * 2);
|
|
|
|
}
|
|
|
|
|
2020-06-15 15:53:45 +02:00
|
|
|
stream<float> output;
|
|
|
|
|
|
|
|
private:
|
|
|
|
static void _worker(AMDemodulator* _this) {
|
2020-06-22 16:45:57 +02:00
|
|
|
complex_t* inBuf = new complex_t[_this->_blockSize];
|
|
|
|
float* outBuf = new float[_this->_blockSize];
|
2020-06-15 15:53:45 +02:00
|
|
|
float min, max, amp;
|
|
|
|
while (true) {
|
2020-06-22 16:45:57 +02:00
|
|
|
if (_this->_input->read(inBuf, _this->_blockSize) < 0) { break; };
|
2020-06-15 15:53:45 +02:00
|
|
|
min = INFINITY;
|
|
|
|
max = 0.0f;
|
2020-06-22 16:45:57 +02:00
|
|
|
for (int i = 0; i < _this->_blockSize; i++) {
|
2020-06-15 15:53:45 +02:00
|
|
|
outBuf[i] = sqrt((inBuf[i].i*inBuf[i].i) + (inBuf[i].q*inBuf[i].q));
|
|
|
|
if (outBuf[i] < min) {
|
|
|
|
min = outBuf[i];
|
|
|
|
}
|
|
|
|
if (outBuf[i] > max) {
|
|
|
|
max = outBuf[i];
|
|
|
|
}
|
|
|
|
}
|
2020-07-19 21:26:37 +02:00
|
|
|
amp = (max - min) / 2.0f;
|
2020-06-22 16:45:57 +02:00
|
|
|
for (int i = 0; i < _this->_blockSize; i++) {
|
2020-07-19 21:26:37 +02:00
|
|
|
outBuf[i] = (outBuf[i] - min - amp) / amp;
|
2020-06-15 15:53:45 +02:00
|
|
|
}
|
2020-06-22 16:45:57 +02:00
|
|
|
if (_this->output.write(outBuf, _this->_blockSize) < 0) { break; };
|
2020-06-15 15:53:45 +02:00
|
|
|
}
|
2020-06-22 16:45:57 +02:00
|
|
|
delete[] inBuf;
|
|
|
|
delete[] outBuf;
|
2020-06-15 15:53:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
stream<complex_t>* _input;
|
|
|
|
bool running;
|
2020-06-22 16:45:57 +02:00
|
|
|
int _blockSize;
|
2020-06-15 15:53:45 +02:00
|
|
|
std::thread _workerThread;
|
|
|
|
};
|
2020-07-19 15:59:44 +02:00
|
|
|
|
|
|
|
class SSBDemod {
|
|
|
|
public:
|
|
|
|
SSBDemod() {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void init(stream<complex_t>* input, float sampleRate, float bandWidth, int blockSize) {
|
|
|
|
_blockSize = blockSize;
|
|
|
|
_bandWidth = bandWidth;
|
|
|
|
_mode = MODE_USB;
|
|
|
|
output.init(blockSize * 2);
|
|
|
|
lo.init(bandWidth / 2.0f, sampleRate, blockSize);
|
|
|
|
mixer.init(input, &lo.output, blockSize);
|
|
|
|
lo.start();
|
|
|
|
}
|
|
|
|
|
|
|
|
void start() {
|
|
|
|
mixer.start();
|
|
|
|
_workerThread = std::thread(_worker, this);
|
|
|
|
running = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void stop() {
|
|
|
|
mixer.stop();
|
|
|
|
mixer.output.stopReader();
|
|
|
|
output.stopWriter();
|
|
|
|
_workerThread.join();
|
|
|
|
mixer.output.clearReadStop();
|
|
|
|
output.clearWriteStop();
|
|
|
|
running = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void setBlockSize(int blockSize) {
|
|
|
|
if (running) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
_blockSize = blockSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
void setMode(int mode) {
|
|
|
|
if (mode < 0 && mode >= _MODE_COUNT) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
_mode = mode;
|
|
|
|
if (mode == MODE_USB) {
|
|
|
|
lo.setFrequency(_bandWidth / 2.0f);
|
|
|
|
}
|
|
|
|
else if (mode == MODE_LSB) {
|
|
|
|
lo.setFrequency(-_bandWidth / 2.0f);
|
|
|
|
}
|
2020-08-20 18:29:23 +02:00
|
|
|
else if (mode == MODE_LSB) {
|
|
|
|
lo.setFrequency(0);
|
|
|
|
}
|
2020-07-19 15:59:44 +02:00
|
|
|
}
|
|
|
|
|
2020-08-21 15:34:50 +02:00
|
|
|
void setBandwidth(float bandwidth) {
|
|
|
|
_bandWidth = bandwidth;
|
|
|
|
if (_mode == MODE_USB) {
|
|
|
|
lo.setFrequency(_bandWidth / 2.0f);
|
|
|
|
}
|
|
|
|
else if (_mode == MODE_LSB) {
|
|
|
|
lo.setFrequency(-_bandWidth / 2.0f);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-19 15:59:44 +02:00
|
|
|
stream<float> output;
|
|
|
|
|
|
|
|
enum {
|
|
|
|
MODE_USB,
|
|
|
|
MODE_LSB,
|
2020-08-20 18:29:23 +02:00
|
|
|
MODE_DSB,
|
2020-07-19 15:59:44 +02:00
|
|
|
_MODE_COUNT
|
|
|
|
};
|
|
|
|
|
|
|
|
private:
|
|
|
|
static void _worker(SSBDemod* _this) {
|
|
|
|
complex_t* inBuf = new complex_t[_this->_blockSize];
|
|
|
|
float* outBuf = new float[_this->_blockSize];
|
|
|
|
|
|
|
|
float min, max, factor;
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
if (_this->mixer.output.read(inBuf, _this->_blockSize) < 0) { break; };
|
|
|
|
min = INFINITY;
|
|
|
|
max = -INFINITY;
|
|
|
|
for (int i = 0; i < _this->_blockSize; i++) {
|
|
|
|
outBuf[i] = inBuf[i].q;
|
|
|
|
if (inBuf[i].q < min) {
|
|
|
|
min = inBuf[i].q;
|
|
|
|
}
|
|
|
|
if (inBuf[i].q > max) {
|
|
|
|
max = inBuf[i].q;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
factor = (max - min) / 2;
|
|
|
|
for (int i = 0; i < _this->_blockSize; i++) {
|
|
|
|
outBuf[i] /= factor;
|
|
|
|
}
|
|
|
|
if (_this->output.write(outBuf, _this->_blockSize) < 0) { break; };
|
|
|
|
}
|
|
|
|
|
|
|
|
delete[] inBuf;
|
|
|
|
delete[] outBuf;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::thread _workerThread;
|
|
|
|
SineSource lo;
|
|
|
|
Multiplier mixer;
|
|
|
|
int _blockSize;
|
|
|
|
float _bandWidth;
|
|
|
|
int _mode;
|
|
|
|
bool running = false;
|
|
|
|
};
|
2020-08-20 18:29:23 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// class CWDemod {
|
|
|
|
// public:
|
|
|
|
// CWDemod() {
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// void init(stream<complex_t>* input, float sampleRate, float bandWidth, int blockSize) {
|
|
|
|
// _blockSize = blockSize;
|
|
|
|
// _bandWidth = bandWidth;
|
|
|
|
// _mode = MODE_USB;
|
|
|
|
// output.init(blockSize * 2);
|
|
|
|
// lo.init(bandWidth / 2.0f, sampleRate, blockSize);
|
|
|
|
// mixer.init(input, &lo.output, blockSize);
|
|
|
|
// lo.start();
|
|
|
|
// }
|
|
|
|
|
|
|
|
// void start() {
|
|
|
|
// mixer.start();
|
|
|
|
// _workerThread = std::thread(_worker, this);
|
|
|
|
// running = true;
|
|
|
|
// }
|
|
|
|
|
|
|
|
// void stop() {
|
|
|
|
// mixer.stop();
|
|
|
|
// mixer.output.stopReader();
|
|
|
|
// output.stopWriter();
|
|
|
|
// _workerThread.join();
|
|
|
|
// mixer.output.clearReadStop();
|
|
|
|
// output.clearWriteStop();
|
|
|
|
// running = false;
|
|
|
|
// }
|
|
|
|
|
|
|
|
// void setBlockSize(int blockSize) {
|
|
|
|
// if (running) {
|
|
|
|
// return;
|
|
|
|
// }
|
|
|
|
// _blockSize = blockSize;
|
|
|
|
// }
|
|
|
|
|
|
|
|
// void setMode(int mode) {
|
|
|
|
// if (mode < 0 && mode >= _MODE_COUNT) {
|
|
|
|
// return;
|
|
|
|
// }
|
|
|
|
// _mode = mode;
|
|
|
|
// if (mode == MODE_USB) {
|
|
|
|
// lo.setFrequency(_bandWidth / 2.0f);
|
|
|
|
// }
|
|
|
|
// else if (mode == MODE_LSB) {
|
|
|
|
// lo.setFrequency(-_bandWidth / 2.0f);
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
|
|
|
|
// stream<float> output;
|
|
|
|
|
|
|
|
// private:
|
|
|
|
// static void _worker(CWDemod* _this) {
|
|
|
|
// complex_t* inBuf = new complex_t[_this->_blockSize];
|
|
|
|
// float* outBuf = new float[_this->_blockSize];
|
|
|
|
|
|
|
|
// float min, max, factor;
|
|
|
|
|
|
|
|
// while (true) {
|
|
|
|
// if (_this->mixer.output.read(inBuf, _this->_blockSize) < 0) { break; };
|
|
|
|
// min = INFINITY;
|
|
|
|
// max = -INFINITY;
|
|
|
|
// for (int i = 0; i < _this->_blockSize; i++) {
|
|
|
|
// outBuf[i] = inBuf[i].q;
|
|
|
|
// if (inBuf[i].q < min) {
|
|
|
|
// min = inBuf[i].q;
|
|
|
|
// }
|
|
|
|
// if (inBuf[i].q > max) {
|
|
|
|
// max = inBuf[i].q;
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// factor = (max - min) / 2;
|
|
|
|
// for (int i = 0; i < _this->_blockSize; i++) {
|
|
|
|
// outBuf[i] /= factor;
|
|
|
|
// }
|
|
|
|
// if (_this->output.write(outBuf, _this->_blockSize) < 0) { break; };
|
|
|
|
// }
|
|
|
|
|
|
|
|
// delete[] inBuf;
|
|
|
|
// delete[] outBuf;
|
|
|
|
// }
|
|
|
|
|
|
|
|
// std::thread _workerThread;
|
|
|
|
// SineSource lo;
|
|
|
|
// Multiplier mixer;
|
|
|
|
// int _blockSize;
|
|
|
|
// float _bandWidth;
|
|
|
|
// int _mode;
|
|
|
|
// bool running = false;
|
|
|
|
// };
|
2020-06-10 04:13:56 +02:00
|
|
|
};
|