mirror of
https://github.com/AlexandreRouma/SDRPlusPlus.git
synced 2024-11-10 04:37:37 +01:00
new stuff
This commit is contained in:
parent
7120e848ef
commit
fa2f6a0a9b
@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.9)
|
||||
project(sdrpp)
|
||||
|
||||
# Compiler config
|
||||
set(CMAKE_CXX_FLAGS "-O2")
|
||||
set(CMAKE_CXX_FLAGS "-OFast /std:c++17")
|
||||
|
||||
# HackRF
|
||||
include_directories(sdrpp "C:/Program Files/PothosSDR/include/libhackrf/")
|
||||
|
@ -3,6 +3,28 @@
|
||||
#include <cdsp/stream.h>
|
||||
#include <cdsp/types.h>
|
||||
|
||||
#define FAST_ATAN2_COEF1 3.1415926535f / 4.0f
|
||||
#define FAST_ATAN2_COEF2 3.0f * FAST_ATAN2_COEF1
|
||||
|
||||
inline float fast_arctan2(float y, float x)
|
||||
{
|
||||
float abs_y = fabs(y)+1e-10;
|
||||
float r, angle;
|
||||
if (x>=0)
|
||||
{
|
||||
r = (x - abs_y) / (x + abs_y);
|
||||
angle = FAST_ATAN2_COEF1 - FAST_ATAN2_COEF1 * r;
|
||||
}
|
||||
else
|
||||
{
|
||||
r = (x + abs_y) / (abs_y - x);
|
||||
angle = FAST_ATAN2_COEF2 - FAST_ATAN2_COEF1 * r;
|
||||
}
|
||||
if (y < 0) {
|
||||
return -angle;
|
||||
}
|
||||
return angle;
|
||||
}
|
||||
|
||||
namespace cdsp {
|
||||
class FMDemodulator {
|
||||
@ -12,6 +34,7 @@ namespace cdsp {
|
||||
}
|
||||
|
||||
FMDemodulator(stream<complex_t>* in, float deviation, long sampleRate, int bufferSize) : output(bufferSize * 2) {
|
||||
running = false;
|
||||
_input = in;
|
||||
_bufferSize = bufferSize;
|
||||
_phase = 0.0f;
|
||||
@ -20,6 +43,7 @@ namespace cdsp {
|
||||
|
||||
void init(stream<complex_t>* in, float deviation, long sampleRate, int bufferSize) {
|
||||
output.init(bufferSize * 2);
|
||||
running = false;
|
||||
_input = in;
|
||||
_bufferSize = bufferSize;
|
||||
_phase = 0.0f;
|
||||
@ -27,9 +51,25 @@ namespace cdsp {
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
stream<float> output;
|
||||
|
||||
private:
|
||||
@ -39,21 +79,98 @@ namespace cdsp {
|
||||
float diff = 0;
|
||||
float currentPhase = 0;
|
||||
while (true) {
|
||||
_this->_input->read(inBuf, _this->_bufferSize);
|
||||
if (_this->_input->read(inBuf, _this->_bufferSize) < 0) { return; };
|
||||
for (int i = 0; i < _this->_bufferSize; i++) {
|
||||
currentPhase = atan2f(inBuf[i].i, inBuf[i].q);
|
||||
currentPhase = fast_arctan2(inBuf[i].i, inBuf[i].q);
|
||||
diff = currentPhase - _this->_phase;
|
||||
if (diff > 3.1415926535f) { diff -= 2 * 3.1415926535f; }
|
||||
else if (diff <= -3.1415926535f) { diff += 2 * 3.1415926535f; }
|
||||
outBuf[i] = diff / _this->_phasorSpeed;
|
||||
_this->_phase = currentPhase;
|
||||
}
|
||||
_this->output.write(outBuf, _this->_bufferSize);
|
||||
if (_this->output.write(outBuf, _this->_bufferSize) < 0) { return; };
|
||||
}
|
||||
}
|
||||
|
||||
stream<complex_t>* _input;
|
||||
bool running;
|
||||
int _bufferSize;
|
||||
float _phase;
|
||||
float _phasorSpeed;
|
||||
std::thread _workerThread;
|
||||
};
|
||||
|
||||
|
||||
class AMDemodulator {
|
||||
public:
|
||||
AMDemodulator() {
|
||||
|
||||
}
|
||||
|
||||
AMDemodulator(stream<complex_t>* in, int bufferSize) : output(bufferSize * 2) {
|
||||
running = false;
|
||||
_input = in;
|
||||
_bufferSize = bufferSize;
|
||||
}
|
||||
|
||||
void init(stream<complex_t>* in, int bufferSize) {
|
||||
output.init(bufferSize * 2);
|
||||
running = false;
|
||||
_input = in;
|
||||
_bufferSize = bufferSize;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
stream<float> output;
|
||||
|
||||
private:
|
||||
static void _worker(AMDemodulator* _this) {
|
||||
complex_t* inBuf = new complex_t[_this->_bufferSize];
|
||||
float* outBuf = new float[_this->_bufferSize];
|
||||
float min, max, amp;
|
||||
while (true) {
|
||||
if (_this->_input->read(inBuf, _this->_bufferSize) < 0) { return; };
|
||||
min = INFINITY;
|
||||
max = 0.0f;
|
||||
for (int i = 0; i < _this->_bufferSize; i++) {
|
||||
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];
|
||||
}
|
||||
}
|
||||
amp = (max - min);
|
||||
for (int i = 0; i < _this->_bufferSize; i++) {
|
||||
outBuf[i] = (outBuf[i] - min) / (max - min);
|
||||
}
|
||||
if (_this->output.write(outBuf, _this->_bufferSize) < 0) { return; };
|
||||
}
|
||||
}
|
||||
|
||||
stream<complex_t>* _input;
|
||||
bool running;
|
||||
int _bufferSize;
|
||||
std::thread _workerThread;
|
||||
};
|
||||
};
|
@ -4,78 +4,338 @@
|
||||
#include <cdsp/types.h>
|
||||
#include <vector>
|
||||
|
||||
#define GET_FROM_RIGHT_BUF(buffer, delayLine, delayLineSz, n) (((n) < 0) ? delayLine[(delayLineSz) + (n)] : buffer[(n)])
|
||||
#define M_PI 3.1415926535f
|
||||
|
||||
|
||||
inline void BlackmanWindow(std::vector<float>& taps, float sampleRate, float cutoff, float transWidth) {
|
||||
taps.clear();
|
||||
float fc = cutoff / sampleRate;
|
||||
int _M = 4.0f / (transWidth / sampleRate);
|
||||
if (_M % 2 == 0) { _M++; }
|
||||
float M = _M;
|
||||
float sum = 0.0f;
|
||||
for (int i = 0; i < _M; i++) {
|
||||
float 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;
|
||||
}
|
||||
}
|
||||
|
||||
namespace cdsp {
|
||||
class FIRFilter {
|
||||
class DecimatingFIRFilter {
|
||||
public:
|
||||
FIRFilter() {
|
||||
DecimatingFIRFilter() {
|
||||
|
||||
}
|
||||
|
||||
FIRFilter(stream<complex_t>* input, std::vector<float> taps, int bufferSize) : output(bufferSize * 2) {
|
||||
_in = input;
|
||||
_bufferSize = bufferSize;
|
||||
_tapCount = taps.size();
|
||||
delayBuf = new complex_t[_tapCount];
|
||||
_taps = taps;
|
||||
}
|
||||
|
||||
void init(stream<complex_t>* input, std::vector<float> taps, int bufferSize) {
|
||||
DecimatingFIRFilter(stream<complex_t>* input, std::vector<float> taps, int bufferSize, float decim) : output(bufferSize * 2) {
|
||||
output.init(bufferSize * 2);
|
||||
_in = input;
|
||||
_bufferSize = bufferSize;
|
||||
_tapCount = taps.size();
|
||||
delayBuf = new complex_t[_tapCount];
|
||||
_taps = taps;
|
||||
|
||||
_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 bufferSize, float decim) {
|
||||
output.init(bufferSize * 2);
|
||||
_in = input;
|
||||
_bufferSize = bufferSize;
|
||||
_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;
|
||||
_taps = new float[_tapCount];
|
||||
for (int i = 0; i < _tapCount; i++) {
|
||||
_taps[i] = taps[i];
|
||||
}
|
||||
}
|
||||
|
||||
void setInput(stream<complex_t>* input) {
|
||||
if (running) {
|
||||
return;
|
||||
}
|
||||
_in = input;
|
||||
}
|
||||
|
||||
void setDecimation(float decimation) {
|
||||
if (running) {
|
||||
return;
|
||||
}
|
||||
_decim = decimation;
|
||||
}
|
||||
|
||||
void setBufferSize(int bufferSize) {
|
||||
if (running) {
|
||||
return;
|
||||
}
|
||||
_bufferSize = bufferSize;
|
||||
}
|
||||
|
||||
stream<complex_t> output;
|
||||
|
||||
private:
|
||||
static void _worker(FIRFilter* _this) {
|
||||
static void _worker(DecimatingFIRFilter* _this) {
|
||||
int outputSize = _this->_bufferSize / _this->_decim;
|
||||
complex_t* inBuf = new complex_t[_this->_bufferSize];
|
||||
complex_t* outBuf = new complex_t[_this->_bufferSize];
|
||||
complex_t* outBuf = new complex_t[outputSize];
|
||||
float tap = 0.0f;
|
||||
int delayOff;
|
||||
void* delayStart = &inBuf[_this->_bufferSize - (_this->_tapCount - 1)];
|
||||
int delaySize = (_this->_tapCount - 1) * sizeof(complex_t);
|
||||
|
||||
int bufferSize = _this->_bufferSize;
|
||||
int outBufferLength = outputSize * sizeof(complex_t);
|
||||
int tapCount = _this->_tapCount;
|
||||
int decim = _this->_decim;
|
||||
complex_t* delayBuf = _this->delayBuf;
|
||||
int id = 0;
|
||||
|
||||
while (true) {
|
||||
_this->_in->read(inBuf, _this->_bufferSize);
|
||||
for (int i = _this->_tapCount; i < _this->_bufferSize - _this->_tapCount; i++) {
|
||||
outBuf[i].i = 0.0f;
|
||||
outBuf[i].q = 0.0f;
|
||||
}
|
||||
for (int t = 0; t < _this->_tapCount; t++) {
|
||||
if (_this->_in->read(inBuf, bufferSize) < 0) { break; };
|
||||
memset(outBuf, 0, outBufferLength);
|
||||
|
||||
for (int t = 0; t < tapCount; t++) {
|
||||
tap = _this->_taps[t];
|
||||
if (tap == 0.0f) {
|
||||
continue;
|
||||
}
|
||||
for (int i = 0; i < t; i++) {
|
||||
outBuf[i].i += tap * _this->delayBuf[_this->_tapCount - t - 1].i;
|
||||
outBuf[i].q += tap * _this->delayBuf[_this->_tapCount - t - 1].q;
|
||||
}
|
||||
for (int i = t; i < _this->_bufferSize; i++) {
|
||||
outBuf[i].i += tap * inBuf[i - t].i;
|
||||
outBuf[i].q += tap * inBuf[i - t].q;
|
||||
|
||||
delayOff = tapCount - t;
|
||||
id = 0;
|
||||
|
||||
for (int i = 0; i < bufferSize; i += decim) {
|
||||
if (i < t) {
|
||||
outBuf[id].i += tap * delayBuf[delayOff + i].i;
|
||||
outBuf[id].q += tap * delayBuf[delayOff + i].q;
|
||||
id++;
|
||||
continue;
|
||||
}
|
||||
outBuf[id].i += tap * inBuf[i - t].i;
|
||||
outBuf[id].q += tap * inBuf[i - t].q;
|
||||
id++;
|
||||
}
|
||||
}
|
||||
// for (int i = _this->_tapCount; i < _this->_bufferSize - _this->_tapCount; i++) {
|
||||
// outBuf[i].i /= (float)_this->_tapCount;
|
||||
// outBuf[i].q /= (float)_this->_tapCount;
|
||||
// }
|
||||
memcpy(_this->delayBuf, &inBuf[_this->_bufferSize - _this->_tapCount], _this->_tapCount * sizeof(complex_t));
|
||||
_this->output.write(outBuf, _this->_bufferSize);
|
||||
memcpy(delayBuf, delayStart, delaySize);
|
||||
if (_this->output.write(outBuf, outputSize) < 0) { break; };
|
||||
}
|
||||
delete[] inBuf;
|
||||
delete[] outBuf;
|
||||
}
|
||||
|
||||
stream<complex_t>* _in;
|
||||
complex_t* delayBuf;
|
||||
int _bufferSize;
|
||||
int _tapCount = 0;
|
||||
std::vector<float> _taps;
|
||||
float _decim;
|
||||
std::thread _workerThread;
|
||||
float* _taps;
|
||||
bool running;
|
||||
};
|
||||
|
||||
class FloatDecimatingFIRFilter {
|
||||
public:
|
||||
FloatDecimatingFIRFilter() {
|
||||
|
||||
}
|
||||
|
||||
FloatDecimatingFIRFilter(stream<float>* input, std::vector<float> taps, int bufferSize, float decim) : output(bufferSize * 2) {
|
||||
output.init(bufferSize * 2);
|
||||
_in = input;
|
||||
_bufferSize = bufferSize;
|
||||
_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 bufferSize, float decim) {
|
||||
output.init(bufferSize * 2);
|
||||
_in = input;
|
||||
_bufferSize = bufferSize;
|
||||
_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() {
|
||||
running = true;
|
||||
_workerThread = std::thread(_worker, this);
|
||||
}
|
||||
|
||||
void stop() {
|
||||
_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;
|
||||
_taps = new float[_tapCount];
|
||||
for (int i = 0; i < _tapCount; i++) {
|
||||
_taps[i] = taps[i];
|
||||
}
|
||||
}
|
||||
|
||||
void setInput(stream<float>* input) {
|
||||
if (running) {
|
||||
return;
|
||||
}
|
||||
_in = input;
|
||||
}
|
||||
|
||||
void setDecimation(float decimation) {
|
||||
if (running) {
|
||||
return;
|
||||
}
|
||||
_decim = decimation;
|
||||
}
|
||||
|
||||
stream<float> output;
|
||||
|
||||
private:
|
||||
static void _worker(FloatDecimatingFIRFilter* _this) {
|
||||
int outputSize = _this->_bufferSize / _this->_decim;
|
||||
float* inBuf = new float[_this->_bufferSize];
|
||||
float* outBuf = new float[outputSize];
|
||||
float tap = 0.0f;
|
||||
int delayOff;
|
||||
void* delayStart = &inBuf[_this->_bufferSize - (_this->_tapCount - 1)];
|
||||
int delaySize = (_this->_tapCount - 1) * sizeof(float);
|
||||
|
||||
int bufferSize = _this->_bufferSize;
|
||||
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, bufferSize) < 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 < bufferSize; 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 _bufferSize;
|
||||
int _tapCount = 0;
|
||||
float _decim;
|
||||
std::thread _workerThread;
|
||||
float* _taps;
|
||||
bool running;
|
||||
};
|
||||
|
||||
class DCBiasRemover {
|
||||
public:
|
||||
@ -173,4 +433,46 @@ namespace cdsp {
|
||||
std::thread _workerThread;
|
||||
void (*_handler)(complex_t*);
|
||||
};
|
||||
|
||||
class Splitter {
|
||||
public:
|
||||
Splitter() {
|
||||
|
||||
}
|
||||
|
||||
Splitter(stream<complex_t>* input, int bufferSize) {
|
||||
_in = input;
|
||||
_bufferSize = bufferSize;
|
||||
output_a.init(bufferSize);
|
||||
output_b.init(bufferSize);
|
||||
}
|
||||
|
||||
void init(stream<complex_t>* input, int bufferSize) {
|
||||
_in = input;
|
||||
_bufferSize = bufferSize;
|
||||
output_a.init(bufferSize);
|
||||
output_b.init(bufferSize);
|
||||
}
|
||||
|
||||
void start() {
|
||||
_workerThread = std::thread(_worker, this);
|
||||
}
|
||||
|
||||
stream<complex_t> output_a;
|
||||
stream<complex_t> output_b;
|
||||
|
||||
private:
|
||||
static void _worker(Splitter* _this) {
|
||||
complex_t* buf = new complex_t[_this->_bufferSize];
|
||||
while (true) {
|
||||
_this->_in->read(buf, _this->_bufferSize);
|
||||
_this->output_a.write(buf, _this->_bufferSize);
|
||||
_this->output_b.write(buf, _this->_bufferSize);
|
||||
}
|
||||
}
|
||||
|
||||
stream<complex_t>* _in;
|
||||
int _bufferSize;
|
||||
std::thread _workerThread;
|
||||
};
|
||||
};
|
@ -92,19 +92,41 @@ namespace cdsp {
|
||||
|
||||
ComplexSineSource(float frequency, long sampleRate, int bufferSize) : output(bufferSize * 2) {
|
||||
_bufferSize = bufferSize;
|
||||
_sampleRate = sampleRate;
|
||||
_phasorSpeed = (2 * 3.1415926535) / (sampleRate / frequency);
|
||||
_phase = 0;
|
||||
running = false;
|
||||
}
|
||||
|
||||
void init(float frequency, long sampleRate, int bufferSize) {
|
||||
output.init(bufferSize * 2);
|
||||
_sampleRate = sampleRate;
|
||||
_bufferSize = bufferSize;
|
||||
_phasorSpeed = (2 * 3.1415926535) / (sampleRate / frequency);
|
||||
_phase = 0;
|
||||
running = false;
|
||||
}
|
||||
|
||||
void start() {
|
||||
if (running) {
|
||||
return;
|
||||
}
|
||||
_workerThread = std::thread(_worker, this);
|
||||
running = true;
|
||||
}
|
||||
|
||||
void stop() {
|
||||
if (!running) {
|
||||
return;
|
||||
}
|
||||
output.stopWriter();
|
||||
_workerThread.join();
|
||||
output.clearWriteStop();
|
||||
running = false;
|
||||
}
|
||||
|
||||
void setFrequency(float frequency) {
|
||||
_phasorSpeed = (2 * 3.1415926535) / (_sampleRate / frequency);
|
||||
}
|
||||
|
||||
stream<complex_t> output;
|
||||
@ -119,13 +141,16 @@ namespace cdsp {
|
||||
outBuf[i].q = cos(_this->_phase);
|
||||
}
|
||||
_this->_phase = fmodf(_this->_phase, 2.0f * 3.1415926535);
|
||||
_this->output.write(outBuf, _this->_bufferSize);
|
||||
if (_this->output.write(outBuf, _this->_bufferSize) < 0) { break; };
|
||||
}
|
||||
delete[] outBuf;
|
||||
}
|
||||
|
||||
int _bufferSize;
|
||||
float _phasorSpeed;
|
||||
float _phase;
|
||||
long _sampleRate;
|
||||
std::thread _workerThread;
|
||||
bool running;
|
||||
};
|
||||
};
|
@ -2,6 +2,7 @@
|
||||
#include <thread>
|
||||
#include <cdsp/stream.h>
|
||||
#include <cdsp/types.h>
|
||||
#include <cdsp/fast_math.h>
|
||||
|
||||
namespace cdsp {
|
||||
class Multiplier {
|
||||
@ -16,10 +17,35 @@ namespace cdsp {
|
||||
_bufferSize = bufferSize;
|
||||
}
|
||||
|
||||
void init(stream<complex_t>* a, stream<complex_t>* b, int bufferSize) {
|
||||
output.init(bufferSize * 2);
|
||||
_a = a;
|
||||
_b = b;
|
||||
_bufferSize = bufferSize;
|
||||
}
|
||||
|
||||
void start() {
|
||||
if (running) {
|
||||
return;
|
||||
}
|
||||
running = true;
|
||||
_workerThread = std::thread(_worker, this);
|
||||
}
|
||||
|
||||
void stop() {
|
||||
if (!running) {
|
||||
return;
|
||||
}
|
||||
_a->stopReader();
|
||||
_b->stopReader();
|
||||
output.stopWriter();
|
||||
_workerThread.join();
|
||||
running = false;
|
||||
_a->clearReadStop();
|
||||
_b->clearReadStop();
|
||||
output.clearWriteStop();
|
||||
}
|
||||
|
||||
stream<complex_t> output;
|
||||
|
||||
private:
|
||||
@ -28,19 +54,17 @@ namespace cdsp {
|
||||
complex_t* bBuf = new complex_t[_this->_bufferSize];
|
||||
complex_t* outBuf = new complex_t[_this->_bufferSize];
|
||||
while (true) {
|
||||
_this->_a->read(aBuf, _this->_bufferSize);
|
||||
_this->_b->read(bBuf, _this->_bufferSize);
|
||||
for (int i = 0; i < _this->_bufferSize; i++) {
|
||||
outBuf[i].i = (aBuf[i].q * bBuf[i].i) + (bBuf[i].q * aBuf[i].i); // BC + AD
|
||||
outBuf[i].q = (aBuf[i].q * bBuf[i].q) - (aBuf[i].i * bBuf[i].i);
|
||||
}
|
||||
_this->output.write(outBuf, _this->_bufferSize);
|
||||
if (_this->_a->read(aBuf, _this->_bufferSize) < 0) { printf("A Received stop signal\n"); return; };
|
||||
if (_this->_b->read(bBuf, _this->_bufferSize) < 0) { printf("B Received stop signal\n"); return; };
|
||||
do_mul(aBuf, bBuf, _this->_bufferSize);
|
||||
if (_this->output.write(aBuf, _this->_bufferSize) < 0) { printf("OUT Received stop signal\n"); return; };
|
||||
}
|
||||
}
|
||||
|
||||
stream<complex_t>* _a;
|
||||
stream<complex_t>* _b;
|
||||
int _bufferSize;
|
||||
bool running = false;
|
||||
std::thread _workerThread;
|
||||
};
|
||||
};
|
@ -7,6 +7,10 @@
|
||||
namespace cdsp {
|
||||
class FMModulator {
|
||||
public:
|
||||
FMModulator() {
|
||||
|
||||
}
|
||||
|
||||
FMModulator(stream<float>* in, float deviation, long sampleRate, int bufferSize) : output(bufferSize * 2) {
|
||||
_input = in;
|
||||
_bufferSize = bufferSize;
|
||||
@ -14,6 +18,14 @@ namespace cdsp {
|
||||
_phasorSpeed = (2 * 3.1415926535) / (sampleRate / deviation);
|
||||
}
|
||||
|
||||
void init(stream<float>* in, float deviation, long sampleRate, int bufferSize) {
|
||||
output.init(bufferSize * 2);
|
||||
_input = in;
|
||||
_bufferSize = bufferSize;
|
||||
_phase = 0.0f;
|
||||
_phasorSpeed = (2 * 3.1415926535) / (sampleRate / deviation);
|
||||
}
|
||||
|
||||
void start() {
|
||||
_workerThread = std::thread(_worker, this);
|
||||
}
|
||||
|
@ -2,20 +2,66 @@
|
||||
#include <thread>
|
||||
#include <cdsp/stream.h>
|
||||
#include <cdsp/types.h>
|
||||
#include <cdsp/filter.h>
|
||||
#include <numeric>
|
||||
|
||||
|
||||
namespace cdsp {
|
||||
class Interpolator {
|
||||
public:
|
||||
Interpolator() {
|
||||
|
||||
}
|
||||
|
||||
Interpolator(stream<float>* in, float interpolation, int bufferSize) : output(bufferSize * 2) {
|
||||
_input = in;
|
||||
_interpolation = interpolation;
|
||||
_bufferSize = bufferSize;
|
||||
running = false;
|
||||
}
|
||||
|
||||
void init(stream<float>* in, float interpolation, int bufferSize) {
|
||||
output.init(bufferSize * 2);
|
||||
_input = in;
|
||||
_interpolation = interpolation;
|
||||
_bufferSize = bufferSize;
|
||||
running = false;
|
||||
}
|
||||
|
||||
void start() {
|
||||
if (running) {
|
||||
return;
|
||||
}
|
||||
running = true;
|
||||
_workerThread = std::thread(_worker, this);
|
||||
}
|
||||
|
||||
void stop() {
|
||||
if (!running) {
|
||||
return;
|
||||
}
|
||||
running = false;
|
||||
_input->stopReader();
|
||||
output.stopWriter();
|
||||
_workerThread.join();
|
||||
_input->clearReadStop();
|
||||
output.clearWriteStop();
|
||||
}
|
||||
|
||||
void setInterpolation(float interpolation) {
|
||||
if (running) {
|
||||
return;
|
||||
}
|
||||
_interpolation = interpolation;
|
||||
}
|
||||
|
||||
void setInput(stream<float>* in) {
|
||||
if (running) {
|
||||
return;
|
||||
}
|
||||
_input = in;
|
||||
}
|
||||
|
||||
stream<float> output;
|
||||
|
||||
private:
|
||||
@ -23,62 +69,116 @@ namespace cdsp {
|
||||
float* inBuf = new float[(int)((float)_this->_bufferSize / _this->_interpolation)];
|
||||
float* outBuf = new float[_this->_bufferSize];
|
||||
while (true) {
|
||||
_this->_input->read(inBuf, (int)((float)_this->_bufferSize / _this->_interpolation));
|
||||
if (_this->_input->read(inBuf, (int)((float)_this->_bufferSize / _this->_interpolation)) < 0) { break; };
|
||||
for (int i = 0; i < _this->_bufferSize; i++) {
|
||||
outBuf[i] = inBuf[(int)((float)i / _this->_interpolation)];
|
||||
}
|
||||
_this->output.write(outBuf, _this->_bufferSize);
|
||||
if (_this->output.write(outBuf, _this->_bufferSize) < 0) { break; };
|
||||
}
|
||||
delete[] inBuf;
|
||||
delete[] outBuf;
|
||||
}
|
||||
|
||||
stream<float>* _input;
|
||||
int _bufferSize;
|
||||
float _interpolation;
|
||||
std::thread _workerThread;
|
||||
bool running;
|
||||
};
|
||||
|
||||
|
||||
class IQInterpolator {
|
||||
public:
|
||||
IQInterpolator() {
|
||||
|
||||
}
|
||||
|
||||
IQInterpolator(stream<complex_t>* in, float interpolation, int bufferSize) : output(bufferSize * 2) {
|
||||
_input = in;
|
||||
_interpolation = interpolation;
|
||||
_bufferSize = bufferSize;
|
||||
running = false;
|
||||
}
|
||||
|
||||
void init(stream<complex_t>* in, float interpolation, int bufferSize) {
|
||||
output.init(bufferSize * 2);
|
||||
_input = in;
|
||||
_interpolation = interpolation;
|
||||
_bufferSize = bufferSize;
|
||||
running = false;
|
||||
}
|
||||
|
||||
void start() {
|
||||
if (running) {
|
||||
return;
|
||||
}
|
||||
_workerThread = std::thread(_worker, this);
|
||||
running = true;
|
||||
}
|
||||
|
||||
void stop() {
|
||||
if (!running) {
|
||||
return;
|
||||
}
|
||||
_input->stopReader();
|
||||
output.stopWriter();
|
||||
_workerThread.join();
|
||||
_input->clearReadStop();
|
||||
output.clearWriteStop();
|
||||
running = false;
|
||||
}
|
||||
|
||||
void setInterpolation(float interpolation) {
|
||||
if (running) {
|
||||
return;
|
||||
}
|
||||
_interpolation = interpolation;
|
||||
}
|
||||
|
||||
stream<complex_t> output;
|
||||
|
||||
private:
|
||||
static void _worker(IQInterpolator* _this) {
|
||||
complex_t* inBuf = new complex_t[(int)((float)_this->_bufferSize / _this->_interpolation)];
|
||||
complex_t* outBuf = new complex_t[_this->_bufferSize];
|
||||
complex_t* inBuf = new complex_t[_this->_bufferSize];
|
||||
complex_t* outBuf = new complex_t[_this->_bufferSize * _this->_interpolation];
|
||||
int outCount = _this->_bufferSize * _this->_interpolation;
|
||||
while (true) {
|
||||
_this->_input->read(inBuf, (int)((float)_this->_bufferSize / _this->_interpolation));
|
||||
for (int i = 0; i < _this->_bufferSize; i++) {
|
||||
if (_this->_input->read(inBuf, _this->_bufferSize) < 0) { break; };
|
||||
for (int i = 0; i < outCount; i++) {
|
||||
outBuf[i] = inBuf[(int)((float)i / _this->_interpolation)];
|
||||
}
|
||||
_this->output.write(outBuf, _this->_bufferSize);
|
||||
if (_this->output.write(outBuf, _this->_bufferSize) < 0) { break; };
|
||||
}
|
||||
delete[] inBuf;
|
||||
delete[] outBuf;
|
||||
}
|
||||
|
||||
stream<complex_t>* _input;
|
||||
int _bufferSize;
|
||||
float _interpolation;
|
||||
std::thread _workerThread;
|
||||
bool running;
|
||||
};
|
||||
|
||||
class BlockDecimator {
|
||||
public:
|
||||
BlockDecimator() {
|
||||
|
||||
}
|
||||
|
||||
BlockDecimator(stream<complex_t>* in, int skip, int bufferSize) : output(bufferSize * 2) {
|
||||
_input = in;
|
||||
_skip = skip;
|
||||
_bufferSize = bufferSize;
|
||||
}
|
||||
|
||||
void init(stream<complex_t>* in, int skip, int bufferSize) {
|
||||
output.init(bufferSize * 2);
|
||||
_input = in;
|
||||
_skip = skip;
|
||||
_bufferSize = bufferSize;
|
||||
}
|
||||
|
||||
void start() {
|
||||
_workerThread = std::thread(_worker, this);
|
||||
}
|
||||
@ -99,4 +199,119 @@ namespace cdsp {
|
||||
int _skip;
|
||||
std::thread _workerThread;
|
||||
};
|
||||
|
||||
class FractionalResampler {
|
||||
public:
|
||||
FractionalResampler() {
|
||||
|
||||
}
|
||||
|
||||
void init(stream<float>* input, float inputSampleRate, float outputSampleRate, int bufferSize, float customCutoff = INFINITY) {
|
||||
_input = input;
|
||||
float lowestFreq = std::min<float>(inputSampleRate, outputSampleRate);
|
||||
int _gcd = std::gcd((int)inputSampleRate, (int)outputSampleRate);
|
||||
_interp = outputSampleRate / _gcd;
|
||||
_decim = inputSampleRate / _gcd;
|
||||
_inputSampleRate = inputSampleRate;
|
||||
_outputSampleRate = outputSampleRate;
|
||||
running = false;
|
||||
|
||||
interpolator.init(input, _interp, bufferSize);
|
||||
|
||||
BlackmanWindow(decimTaps, inputSampleRate * _interp, lowestFreq / 2.0f, lowestFreq / 2.0f);
|
||||
|
||||
if (_interp != 1) {
|
||||
printf("FR Interpolation needed\n");
|
||||
decimator.init(&interpolator.output, decimTaps, bufferSize * _interp, _decim);
|
||||
}
|
||||
else {
|
||||
decimator.init(input, decimTaps, bufferSize, _decim);
|
||||
printf("FR Interpolation NOT needed: %d %d %d\n", bufferSize / _decim, _decim, _interp);
|
||||
}
|
||||
|
||||
output = &decimator.output;
|
||||
}
|
||||
|
||||
void start() {
|
||||
if (_interp != 1) {
|
||||
interpolator.start();
|
||||
}
|
||||
decimator.start();
|
||||
running = true;
|
||||
}
|
||||
|
||||
void stop() {
|
||||
interpolator.stop();
|
||||
decimator.stop();
|
||||
running = false;
|
||||
}
|
||||
|
||||
void setInputSampleRate(float inputSampleRate) {
|
||||
if (running) {
|
||||
return;
|
||||
}
|
||||
float lowestFreq = std::min<float>(inputSampleRate, _outputSampleRate);
|
||||
int _gcd = std::gcd((int)inputSampleRate, (int)_outputSampleRate);
|
||||
_interp = _outputSampleRate / _gcd;
|
||||
_decim = inputSampleRate / _gcd;
|
||||
|
||||
// TODO: Add checks from VFO to remove the need to stop both
|
||||
interpolator.setInterpolation(_interp);
|
||||
decimator.setDecimation(_decim);
|
||||
if (_interp != 1) {
|
||||
decimator.setInput(&interpolator.output);
|
||||
}
|
||||
else {
|
||||
decimator.setInput(_input);
|
||||
}
|
||||
}
|
||||
|
||||
void setOutputSampleRate(float outputSampleRate) {
|
||||
if (running) {
|
||||
return;
|
||||
}
|
||||
float lowestFreq = std::min<float>(_inputSampleRate, outputSampleRate);
|
||||
int _gcd = std::gcd((int)_inputSampleRate, (int)outputSampleRate);
|
||||
_interp = outputSampleRate / _gcd;
|
||||
_decim = _inputSampleRate / _gcd;
|
||||
|
||||
// TODO: Add checks from VFO to remove the need to stop both
|
||||
interpolator.setInterpolation(_interp);
|
||||
decimator.setDecimation(_decim);
|
||||
if (_interp != 1) {
|
||||
decimator.setInput(&interpolator.output);
|
||||
}
|
||||
else {
|
||||
decimator.setInput(_input);
|
||||
}
|
||||
}
|
||||
|
||||
void setInput(stream<float>* input) {
|
||||
if (running) {
|
||||
return;
|
||||
}
|
||||
_input = input;
|
||||
if (_interp != 1) {
|
||||
interpolator.setInput(input);
|
||||
decimator.setInput(&interpolator.output);
|
||||
}
|
||||
else {
|
||||
decimator.setInput(input);
|
||||
}
|
||||
}
|
||||
|
||||
stream<float>* output;
|
||||
|
||||
private:
|
||||
Interpolator interpolator;
|
||||
FloatDecimatingFIRFilter decimator;
|
||||
std::vector<float> decimTaps;
|
||||
stream<float>* _input;
|
||||
int _interp;
|
||||
int _decim;
|
||||
int _bufferSize;
|
||||
float _inputSampleRate;
|
||||
float _outputSampleRate;
|
||||
bool running;
|
||||
};
|
||||
};
|
@ -13,6 +13,8 @@ namespace cdsp {
|
||||
|
||||
stream(int size) {
|
||||
_buffer = new T[size];
|
||||
_stopReader = false;
|
||||
_stopWriter = false;
|
||||
this->size = size;
|
||||
writec = 0;
|
||||
readc = size - 1;
|
||||
@ -20,15 +22,27 @@ namespace cdsp {
|
||||
|
||||
void init(int size) {
|
||||
_buffer = new T[size];
|
||||
_stopReader = false;
|
||||
_stopWriter = false;
|
||||
this->size = size;
|
||||
writec = 0;
|
||||
readc = size - 1;
|
||||
}
|
||||
|
||||
void read(T* data, int len) {
|
||||
int read(T* data, int len) {
|
||||
int dataRead = 0;
|
||||
while (dataRead < len) {
|
||||
int canRead = waitUntilReadable();
|
||||
if (canRead < 0) {
|
||||
if (_stopReader) {
|
||||
printf("Stop reader set");
|
||||
}
|
||||
else {
|
||||
printf("Stop not set");
|
||||
}
|
||||
clearReadStop();
|
||||
return -1;
|
||||
}
|
||||
int toRead = std::min(canRead, len - dataRead);
|
||||
|
||||
int len1 = (toRead >= (size - readc) ? (size - readc) : (toRead));
|
||||
@ -45,10 +59,14 @@ namespace cdsp {
|
||||
}
|
||||
}
|
||||
|
||||
void readAndSkip(T* data, int len, int skip) {
|
||||
int readAndSkip(T* data, int len, int skip) {
|
||||
int dataRead = 0;
|
||||
while (dataRead < len) {
|
||||
int canRead = waitUntilReadable();
|
||||
if (canRead < 0) {
|
||||
clearReadStop();
|
||||
return -1;
|
||||
}
|
||||
int toRead = std::min(canRead, len - dataRead);
|
||||
|
||||
int len1 = (toRead >= (size - readc) ? (size - readc) : (toRead));
|
||||
@ -85,7 +103,10 @@ namespace cdsp {
|
||||
return canRead;
|
||||
}
|
||||
std::unique_lock<std::mutex> lck(writec_mtx);
|
||||
canReadVar.wait(lck, [=](){ return (this->readable(false) > 0); });
|
||||
canReadVar.wait(lck, [=](){ return ((this->readable(false) > 0) || this->getReadStop()); });
|
||||
if (this->getReadStop()) {
|
||||
return -1;
|
||||
}
|
||||
return this->readable(false);
|
||||
}
|
||||
|
||||
@ -100,10 +121,14 @@ namespace cdsp {
|
||||
return readable - 1;
|
||||
}
|
||||
|
||||
void write(T* data, int len) {
|
||||
int write(T* data, int len) {
|
||||
int dataWrite = 0;
|
||||
while (dataWrite < len) {
|
||||
int canWrite = waitUntilWriteable();
|
||||
if (canWrite < 0) {
|
||||
clearWriteStop();
|
||||
return -1;
|
||||
}
|
||||
int toWrite = std::min(canWrite, len - dataWrite);
|
||||
|
||||
int len1 = (toWrite >= (size - writec) ? (size - writec) : (toWrite));
|
||||
@ -118,6 +143,7 @@ namespace cdsp {
|
||||
writec_mtx.unlock();
|
||||
canReadVar.notify_one();
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
int waitUntilWriteable() {
|
||||
@ -126,7 +152,10 @@ namespace cdsp {
|
||||
return canWrite;
|
||||
}
|
||||
std::unique_lock<std::mutex> lck(readc_mtx);
|
||||
canWriteVar.wait(lck, [=](){ return (this->writeable(false) > 0); });
|
||||
canWriteVar.wait(lck, [=](){ return ((this->writeable(false) > 0) || this->getWriteStop()); });
|
||||
if (this->getWriteStop()) {
|
||||
return -1;
|
||||
}
|
||||
return this->writeable(false);
|
||||
}
|
||||
|
||||
@ -141,11 +170,39 @@ namespace cdsp {
|
||||
return writeable - 1;
|
||||
}
|
||||
|
||||
void stopReader() {
|
||||
_stopReader = true;
|
||||
canReadVar.notify_one();
|
||||
}
|
||||
|
||||
void stopWriter() {
|
||||
_stopWriter = true;
|
||||
canWriteVar.notify_one();
|
||||
}
|
||||
|
||||
bool getReadStop() {
|
||||
return _stopReader;
|
||||
}
|
||||
|
||||
bool getWriteStop() {
|
||||
return _stopWriter;
|
||||
}
|
||||
|
||||
void clearReadStop() {
|
||||
_stopReader = false;
|
||||
}
|
||||
|
||||
void clearWriteStop() {
|
||||
_stopWriter = false;
|
||||
}
|
||||
|
||||
private:
|
||||
T* _buffer;
|
||||
int size;
|
||||
int readc;
|
||||
int writec;
|
||||
bool _stopReader;
|
||||
bool _stopWriter;
|
||||
std::mutex readc_mtx;
|
||||
std::mutex writec_mtx;
|
||||
std::condition_variable canReadVar;
|
||||
|
@ -70,7 +70,7 @@ int main() {
|
||||
glfwGetWindowSize(window, &wwidth, &wheight);
|
||||
ImGui::SetNextWindowPos(ImVec2(0, 0));
|
||||
ImGui::SetNextWindowSize(ImVec2(wwidth, wheight));
|
||||
ImGui::Begin("", NULL, WINDOW_FLAGS);
|
||||
ImGui::Begin("Main", NULL, WINDOW_FLAGS);
|
||||
|
||||
drawWindow();
|
||||
|
||||
|
@ -19,75 +19,84 @@ ImGui::WaterFall wtf;
|
||||
hackrf_device* dev;
|
||||
fftwf_complex *fft_in, *fft_out;
|
||||
fftwf_plan p;
|
||||
float* tempData;
|
||||
|
||||
int fftSize = 8192 * 8;
|
||||
|
||||
bool dcbias = true;
|
||||
|
||||
cdsp::HackRFSource src;
|
||||
SignalPath sigPath;
|
||||
std::vector<float> _data;
|
||||
std::vector<float> fftTaps;
|
||||
void fftHandler(cdsp::complex_t* samples) {
|
||||
fftwf_execute(p);
|
||||
int half = fftSize / 2;
|
||||
|
||||
for (int i = 0; i < half; i++) {
|
||||
_data.push_back(log10(std::abs(std::complex<float>(fft_out[half + i][0], fft_out[half + i][1])) / (float)fftSize) * 10.0f);
|
||||
}
|
||||
for (int i = 0; i < half; i++) {
|
||||
_data.push_back(log10(std::abs(std::complex<float>(fft_out[i][0], fft_out[i][1])) / (float)fftSize) * 10.0f);
|
||||
}
|
||||
|
||||
for (int i = 5; i < fftSize; i++) {
|
||||
_data[i] = (_data[i - 4] + _data[i - 3] + _data[i - 2] + _data[i - 1] + _data[i]) / 5.0f;
|
||||
}
|
||||
|
||||
wtf.pushFFT(_data, fftSize);
|
||||
_data.clear();
|
||||
}
|
||||
|
||||
void windowInit() {
|
||||
int fftSize = 8192;
|
||||
wtf.bandWidth = 8000000;
|
||||
int sampleRate = 2000000;
|
||||
wtf.bandWidth = sampleRate;
|
||||
wtf.range = 500000;
|
||||
wtf.centerFrequency = 90500000;
|
||||
printf("fft taps: %d\n", fftTaps.size());
|
||||
|
||||
fft_in = (fftwf_complex*) fftw_malloc(sizeof(fftw_complex) * fftSize);
|
||||
fft_out = (fftwf_complex*) fftw_malloc(sizeof(fftw_complex) * fftSize);
|
||||
fft_in = (fftwf_complex*) fftw_malloc(sizeof(fftwf_complex) * fftSize);
|
||||
fft_out = (fftwf_complex*) fftw_malloc(sizeof(fftwf_complex) * fftSize);
|
||||
p = fftwf_plan_dft_1d(fftSize, fft_in, fft_out, FFTW_FORWARD, FFTW_ESTIMATE);
|
||||
|
||||
worker = std::thread([=]() {
|
||||
std::vector<float> data;
|
||||
printf("Starting DSP Thread!\n");
|
||||
printf("Starting DSP Thread!\n");
|
||||
|
||||
hackrf_init();
|
||||
hackrf_device_list_t* list = hackrf_device_list();
|
||||
|
||||
int err = hackrf_device_list_open(list, 0, &dev);
|
||||
if (err != 0) {
|
||||
printf("Error while opening HackRF: %d\n", err);
|
||||
return -1;
|
||||
}
|
||||
hackrf_set_freq(dev, 98000000);
|
||||
//hackrf_set_txvga_gain(dev, 10);
|
||||
hackrf_set_amp_enable(dev, 0);
|
||||
hackrf_set_lna_gain(dev, 24);
|
||||
hackrf_set_vga_gain(dev, 20);
|
||||
hackrf_set_sample_rate(dev, 8000000);
|
||||
hackrf_init();
|
||||
hackrf_device_list_t* list = hackrf_device_list();
|
||||
|
||||
int err = hackrf_device_list_open(list, 0, &dev);
|
||||
if (err != 0) {
|
||||
printf("Error while opening HackRF: %d\n", err);
|
||||
return;
|
||||
}
|
||||
hackrf_set_freq(dev, 90500000);
|
||||
//hackrf_set_txvga_gain(dev, 10);
|
||||
hackrf_set_amp_enable(dev, 1);
|
||||
hackrf_set_lna_gain(dev, 24);
|
||||
hackrf_set_vga_gain(dev, 20);
|
||||
hackrf_set_baseband_filter_bandwidth(dev, sampleRate);
|
||||
hackrf_set_sample_rate(dev, sampleRate);
|
||||
|
||||
cdsp::HackRFSource src(dev, 64000);
|
||||
cdsp::DCBiasRemover bias(&src.output, 64000);
|
||||
cdsp::ComplexSineSource sinsrc(4000000.0f, 8000000, 64000);
|
||||
cdsp::BlockDecimator dec(&bias.output, 566603 - fftSize, fftSize);
|
||||
cdsp::Multiplier mul(&dec.output, &sinsrc.output, fftSize);
|
||||
|
||||
src.start();
|
||||
bias.start();
|
||||
sinsrc.start();
|
||||
dec.start();
|
||||
mul.start();
|
||||
src.init(dev, 64000);
|
||||
|
||||
while (true) {
|
||||
mul.output.read((cdsp::complex_t*)fft_in, fftSize);
|
||||
|
||||
fftwf_execute(p);
|
||||
|
||||
for (int i = 0; i < fftSize ; i++) {
|
||||
data.push_back(log10(std::abs(std::complex<float>(fft_out[i][0], fft_out[i][1])) / (float)fftSize) * 10.0f);
|
||||
}
|
||||
|
||||
for (int i = 5; i < fftSize; i++) {
|
||||
data[i] = (data[i - 3] + data[i - 2] + data[i - 1] + data[i]) / 4.0f;
|
||||
}
|
||||
|
||||
bias.bypass = !dcbias;
|
||||
|
||||
wtf.pushFFT(data, fftSize);
|
||||
data.clear();
|
||||
}
|
||||
});
|
||||
sigPath.init(sampleRate, 20, fftSize, &src.output, (cdsp::complex_t*)fft_in, fftHandler);
|
||||
sigPath.start();
|
||||
}
|
||||
|
||||
int Current = 0;
|
||||
bool showExample = false;
|
||||
|
||||
int freq = 98000;
|
||||
int _freq = 98000;
|
||||
int freq = 90500;
|
||||
int _freq = 90500;
|
||||
|
||||
bool state = false;
|
||||
bool mulstate = true;
|
||||
|
||||
float vfoFreq = 92000000.0f;
|
||||
float lastVfoFreq = 92000000.0f;
|
||||
|
||||
float volume = 1.0f;
|
||||
float lastVolume = 1.0f;
|
||||
|
||||
void drawWindow() {
|
||||
if (freq != _freq) {
|
||||
@ -96,6 +105,16 @@ void drawWindow() {
|
||||
hackrf_set_freq(dev, freq * 1000);
|
||||
}
|
||||
|
||||
if (vfoFreq != lastVfoFreq) {
|
||||
lastVfoFreq = vfoFreq;
|
||||
sigPath.setVFOFrequency(vfoFreq - (freq * 1000));
|
||||
}
|
||||
|
||||
if (volume != lastVolume) {
|
||||
lastVolume = volume;
|
||||
sigPath.setVolume(volume);
|
||||
}
|
||||
|
||||
|
||||
if (ImGui::BeginMenuBar())
|
||||
{
|
||||
@ -130,7 +149,16 @@ void drawWindow() {
|
||||
|
||||
if (ImGui::CollapsingHeader("Source")) {
|
||||
|
||||
ImGui::Combo("Source", &Current, "HackRF One\0RTL-SDR");
|
||||
//ImGui::Combo("Source", &Current, "HackRF One\0RTL-SDR");
|
||||
ImGui::SliderFloat("Volume", &volume, 0.0f, 1.0f);
|
||||
if (ImGui::Button("Start") && !state) {
|
||||
state = true;
|
||||
src.start();
|
||||
}
|
||||
if (ImGui::Button("Stop") && state) {
|
||||
state = false;
|
||||
src.stop();
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::CollapsingHeader("Radio")) {
|
||||
@ -165,6 +193,13 @@ void drawWindow() {
|
||||
if(ImGui::CollapsingHeader("Debug")) {
|
||||
ImGui::Text("Frame time: %.3f ms/frame", 1000.0f / ImGui::GetIO().Framerate);
|
||||
ImGui::Text("Framerate: %.1f FPS", ImGui::GetIO().Framerate);
|
||||
|
||||
if (ImGui::Button("FM demod")) {
|
||||
sigPath.setDemodulator(SignalPath::DEMOD_FM);
|
||||
}
|
||||
if (ImGui::Button("AM demod")) {
|
||||
sigPath.setDemodulator(SignalPath::DEMOD_AM);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
@ -173,6 +208,6 @@ void drawWindow() {
|
||||
ImGui::NextColumn();
|
||||
|
||||
ImGui::BeginChild("Waterfall");
|
||||
wtf.draw();
|
||||
wtf.draw(&vfoFreq);
|
||||
ImGui::EndChild();
|
||||
}
|
@ -1,20 +1,100 @@
|
||||
#include <signal_path.h>
|
||||
|
||||
SignalPath::SignalPath(cdsp::stream<cdsp::complex_t>* source, float sampleRate, float fftRate, int fftSize, cdsp::complex_t* fftBuffer, void fftHandler(cdsp::complex_t*)) :
|
||||
dcBiasRemover(source, 64000),
|
||||
fftBlockDec(&dcBiasRemover.output, (sampleRate / fftRate) - fftSize, fftSize),
|
||||
fftSineSource(sampleRate / 2.0f, sampleRate, fftSize),
|
||||
fftMul(&fftBlockDec.output, &fftSineSource.output, fftSize),
|
||||
fftHandler(&fftMul.output, fftBuffer, fftSize, fftHandler) {
|
||||
SignalPath::SignalPath() {
|
||||
|
||||
}
|
||||
|
||||
std::vector<float> iftaps;
|
||||
std::vector<float> ataps;
|
||||
|
||||
void SignalPath::init(uint64_t sampleRate, int fftRate, int fftSize, cdsp::stream<cdsp::complex_t>* input, cdsp::complex_t* fftBuffer, void fftHandler(cdsp::complex_t*)) {
|
||||
this->sampleRate = sampleRate;
|
||||
this->fftRate = fftRate;
|
||||
this->fftSize = fftSize;
|
||||
|
||||
BlackmanWindow(iftaps, sampleRate, 100000, 200000);
|
||||
BlackmanWindow(ataps, 200000, 20000, 10000);
|
||||
|
||||
// for (int i = 0; i < iftaps.size(); i++) {
|
||||
// printf("%f\n", iftaps[i]);
|
||||
// }
|
||||
|
||||
_demod = DEMOD_FM;
|
||||
|
||||
printf("%d\n", iftaps.size());
|
||||
printf("%d\n", ataps.size());
|
||||
|
||||
dcBiasRemover.init(input, 32000);
|
||||
dcBiasRemover.bypass = true;
|
||||
split.init(&dcBiasRemover.output, 32000);
|
||||
|
||||
fftBlockDec.init(&split.output_a, (sampleRate / fftRate) - fftSize, fftSize);
|
||||
fftHandlerSink.init(&fftBlockDec.output, fftBuffer, fftSize, fftHandler);
|
||||
|
||||
mainVFO.init(&split.output_b, 0, sampleRate, 200000, 32000);
|
||||
|
||||
demod.init(mainVFO.output, 100000, 200000, 800);
|
||||
amDemod.init(mainVFO.output, 800);
|
||||
|
||||
audioResamp.init(&demod.output, 200000, 40000, 800, 20000);
|
||||
audio.init(audioResamp.output, 160);
|
||||
}
|
||||
|
||||
void SignalPath::setVFOFrequency(long frequency) {
|
||||
mainVFO.setOffset(frequency);
|
||||
}
|
||||
|
||||
void SignalPath::setVolume(float volume) {
|
||||
audio.setVolume(volume);
|
||||
}
|
||||
|
||||
void SignalPath::setDemodulator(int demId) {
|
||||
if (demId < 0 || demId >= _DEMOD_COUNT) {
|
||||
return;
|
||||
}
|
||||
|
||||
audioResamp.stop();
|
||||
|
||||
// Stop current demodulator
|
||||
if (_demod == DEMOD_FM) {
|
||||
printf("Stopping FM demodulator\n");
|
||||
demod.stop();
|
||||
}
|
||||
else if (_demod == DEMOD_AM) {
|
||||
printf("Stopping AM demodulator\n");
|
||||
amDemod.stop();
|
||||
}
|
||||
_demod = demId;
|
||||
|
||||
// Set input of the audio resampler
|
||||
if (demId == DEMOD_FM) {
|
||||
printf("Starting FM demodulator\n");
|
||||
mainVFO.setBandwidth(200000);
|
||||
audioResamp.setInput(&demod.output);
|
||||
audioResamp.setInputSampleRate(200000);
|
||||
demod.start();
|
||||
}
|
||||
else if (demId == DEMOD_AM) {
|
||||
printf("Starting AM demodulator\n");
|
||||
mainVFO.setBandwidth(12000);
|
||||
audioResamp.setInput(&amDemod.output);
|
||||
audioResamp.setInputSampleRate(12000);
|
||||
amDemod.start();
|
||||
}
|
||||
|
||||
audioResamp.start();
|
||||
}
|
||||
|
||||
void SignalPath::start() {
|
||||
dcBiasRemover.start();
|
||||
split.start();
|
||||
|
||||
fftBlockDec.start();
|
||||
fftSineSource.start();
|
||||
fftMul.start();
|
||||
fftHandler.start();
|
||||
fftHandlerSink.start();
|
||||
|
||||
mainVFO.start();
|
||||
demod.start();
|
||||
|
||||
audioResamp.start();
|
||||
audio.start();
|
||||
}
|
@ -3,24 +3,50 @@
|
||||
#include <cdsp/resampling.h>
|
||||
#include <cdsp/generator.h>
|
||||
#include <cdsp/math.h>
|
||||
#include <cdsp/demodulation.h>
|
||||
#include <cdsp/audio.h>
|
||||
#include <vfo.h>
|
||||
|
||||
class SignalPath {
|
||||
public:
|
||||
SignalPath();
|
||||
SignalPath(cdsp::stream<cdsp::complex_t>* source, float sampleRate, float fftRate, int fftSize, cdsp::complex_t* fftBuffer, void fftHandler(cdsp::complex_t*));
|
||||
void init(uint64_t sampleRate, int fftRate, int fftSize, cdsp::stream<cdsp::complex_t>* input, cdsp::complex_t* fftBuffer, void fftHandler(cdsp::complex_t*));
|
||||
void start();
|
||||
void setSampleRate(float sampleRate);
|
||||
void setDCBiasCorrection(bool enabled);
|
||||
void setFFTRate(float rate);
|
||||
|
||||
void setVFOFrequency(long frequency);
|
||||
void setVolume(float volume);
|
||||
|
||||
void setDemodulator(int demod);
|
||||
|
||||
enum {
|
||||
DEMOD_FM,
|
||||
DEMOD_AM,
|
||||
_DEMOD_COUNT
|
||||
};
|
||||
|
||||
private:
|
||||
cdsp::DCBiasRemover dcBiasRemover;
|
||||
cdsp::Splitter split;
|
||||
|
||||
// FFT
|
||||
cdsp::BlockDecimator fftBlockDec;
|
||||
cdsp::ComplexSineSource fftSineSource;
|
||||
cdsp::Multiplier fftMul;
|
||||
cdsp::HandlerSink fftHandler;
|
||||
cdsp::HandlerSink fftHandlerSink;
|
||||
|
||||
// VFO
|
||||
VFO mainVFO;
|
||||
|
||||
cdsp::FMDemodulator demod;
|
||||
cdsp::AMDemodulator amDemod;
|
||||
|
||||
//cdsp::FloatDecimatingFIRFilter audioDecFilt;
|
||||
cdsp::FractionalResampler audioResamp;
|
||||
cdsp::AudioSink audio;
|
||||
|
||||
float sampleRate;
|
||||
float fftRate;
|
||||
int fftSize;
|
||||
int _demod;
|
||||
};
|
@ -19,7 +19,17 @@ const float COLOR_MAP[][3] = {
|
||||
{0x00, 0x00, 0x20}
|
||||
};
|
||||
|
||||
bool isInArea(ImVec2 pos, ImVec2 min, ImVec2 max) {
|
||||
return (pos.x >= min.x && pos.y >= min.y && pos.x < max.x && pos.y < max.y);
|
||||
}
|
||||
|
||||
namespace ImGui {
|
||||
|
||||
uint32_t* img = NULL;
|
||||
int lastW = 0;
|
||||
int lastH = 0;
|
||||
|
||||
|
||||
WaterFall::WaterFall() {
|
||||
std::vector<float> base;
|
||||
for (int i = 0; i < 1024; i++) {
|
||||
@ -30,17 +40,19 @@ namespace ImGui {
|
||||
glGenTextures(1, &textureId);
|
||||
}
|
||||
|
||||
void WaterFall::drawFFT(ImGuiWindow* window, int width, int height, ImVec2 pos) {
|
||||
float lineHeight = (float)(height - 20 - 30) / 7.0f;
|
||||
void WaterFall::drawFFT(ImGuiWindow* window, int width, int height, ImVec2 pos, float* vfo) {
|
||||
float lines = 6.0f;
|
||||
int w = width - 10;
|
||||
float lineHeight = (float)(height - 20 - 30) / lines;
|
||||
char buf[100];
|
||||
int fftWidth = width - 50;
|
||||
|
||||
// Vertical scale
|
||||
for (int i = 0; i < 8; i++) {
|
||||
for (int i = 0; i < (lines + 1); i++) {
|
||||
sprintf(buf, "%d", -i * 10);
|
||||
ImVec2 txtSz = ImGui::CalcTextSize(buf);
|
||||
window->DrawList->AddText(ImVec2(pos.x + 30 - txtSz.x, pos.y + (i * lineHeight) + 2), IM_COL32( 255, 255, 255, 255 ), buf);
|
||||
if (i == 7) {
|
||||
if (i == lines) { // Last line
|
||||
window->DrawList->AddLine(ImVec2(pos.x + 40, pos.y + (i * lineHeight) + 10),
|
||||
ImVec2(pos.x + width - 10, pos.y + (i * lineHeight) + 10),
|
||||
IM_COL32( 255, 255, 255, 255 ), 1.0f);
|
||||
@ -60,24 +72,30 @@ namespace ImGui {
|
||||
int count = 0;
|
||||
for (; start < end; start += range) {
|
||||
window->DrawList->AddLine(ImVec2(pos.x + pixelOffset + (pixelWidth * count) + 40, pos.y + 10),
|
||||
ImVec2(pos.x + pixelOffset + (pixelWidth * count) + 40, pos.y + (7 * lineHeight) + 10),
|
||||
ImVec2(pos.x + pixelOffset + (pixelWidth * count) + 40, pos.y + (lines * lineHeight) + 10),
|
||||
IM_COL32( 70, 70, 70, 255 ), 1.0f);
|
||||
|
||||
window->DrawList->AddLine(ImVec2(pos.x + pixelOffset + (pixelWidth * count) + 40, pos.y + (7 * lineHeight) + 10),
|
||||
ImVec2(pos.x + pixelOffset + (pixelWidth * count) + 40, pos.y + (7 * lineHeight) + 20),
|
||||
window->DrawList->AddLine(ImVec2(pos.x + pixelOffset + (pixelWidth * count) + 40, pos.y + (lines * lineHeight) + 10),
|
||||
ImVec2(pos.x + pixelOffset + (pixelWidth * count) + 40, pos.y + (lines * lineHeight) + 20),
|
||||
IM_COL32( 255, 255, 255, 255 ), 1.0f);
|
||||
|
||||
sprintf(buf, "%.1fM", start / 1000000.0f);
|
||||
|
||||
ImVec2 txtSz = ImGui::CalcTextSize(buf);
|
||||
|
||||
window->DrawList->AddText(ImVec2(pos.x + pixelOffset + (pixelWidth * count) + 40 - (txtSz.x / 2.0f), pos.y + (7 * lineHeight) + 25), IM_COL32( 255, 255, 255, 255 ), buf);
|
||||
window->DrawList->AddText(ImVec2(pos.x + pixelOffset + (pixelWidth * count) + 40 - (txtSz.x / 2.0f), pos.y + (lines * lineHeight) + 25), IM_COL32( 255, 255, 255, 255 ), buf);
|
||||
|
||||
count++;
|
||||
}
|
||||
|
||||
int dataCount = fftBuffer[0].size();
|
||||
float multiplier = (float)dataCount / (float)fftWidth;
|
||||
if (lastW != w) {
|
||||
if (fftDrawBuffer != NULL) {
|
||||
delete[] fftDrawBuffer;
|
||||
}
|
||||
fftDrawBuffer = new float[fftWidth];
|
||||
}
|
||||
for (int i = 1; i < fftWidth; i++) {
|
||||
float a = (fftBuffer[0][(int)((float)(i - 1) * multiplier)] / 10.0f) * lineHeight;
|
||||
float b = (fftBuffer[0][(int)((float)i * multiplier)] / 10.0f) * lineHeight;
|
||||
@ -86,7 +104,7 @@ namespace ImGui {
|
||||
IM_COL32( 0, 255, 255, 255 ), 1.0f);
|
||||
|
||||
window->DrawList->AddLine(ImVec2(pos.x + i + 39, pos.y - a),
|
||||
ImVec2(pos.x + i + 39, pos.y + (7.0f * lineHeight) + 9),
|
||||
ImVec2(pos.x + i + 39, pos.y + (lines * lineHeight) + 9),
|
||||
IM_COL32( 0, 255, 255, 50 ), 1.0f);
|
||||
}
|
||||
|
||||
@ -94,17 +112,24 @@ namespace ImGui {
|
||||
// ImVec2(pos.x + (i * spacing) + 40, pos.y - b),
|
||||
// IM_COL32( 0, 255, 255, 255 ), 1.0f);
|
||||
|
||||
window->DrawList->AddLine(ImVec2(pos.x + 40, pos.y + 10), ImVec2(pos.x + 40, pos.y + (7 * lineHeight) + 10), IM_COL32( 255, 255, 255, 255 ), 1.0f);
|
||||
window->DrawList->AddLine(ImVec2(pos.x + 40, pos.y + 10), ImVec2(pos.x + 40, pos.y + (lines * lineHeight) + 10), IM_COL32( 255, 255, 255, 255 ), 1.0f);
|
||||
|
||||
ImVec2 mPos = ImGui::GetMousePos();
|
||||
window->DrawList->AddRectFilled(ImVec2(mPos.x - 20, pos.y + 1), ImVec2(mPos.x + 20, pos.y + (7 * lineHeight) + 10), IM_COL32( 255, 255, 255, 50 ));
|
||||
window->DrawList->AddLine(ImVec2(mPos.x, pos.y + 1), ImVec2(mPos.x, pos.y + (7 * lineHeight) + 10), IM_COL32( 255, 0, 0, 255 ), 1.0f);
|
||||
// window->DrawList->AddRectFilled(ImVec2(mPos.x - 20, pos.y + 11), ImVec2(mPos.x + 20, pos.y + (lines * lineHeight) + 10), IM_COL32( 255, 255, 255, 50 ));
|
||||
// window->DrawList->AddLine(ImVec2(mPos.x, pos.y + 11), ImVec2(mPos.x, pos.y + (lines * lineHeight) + 10), IM_COL32( 255, 0, 0, 255 ), 1.0f);
|
||||
|
||||
|
||||
float vfoPos = (((*vfo - centerFrequency) + (bandWidth / 2.0f)) / bandWidth) * (float)fftWidth + 40;
|
||||
|
||||
if (ImGui::IsMouseClicked(ImGuiMouseButton_Left) && isInArea(mPos, ImVec2(pos.x + 40, pos.y + 10), ImVec2(pos.x + fftWidth + 40, pos.y + (lines * lineHeight) + 10))) {
|
||||
*vfo = (((((mPos.x - pos.x) - 40) / (float)fftWidth) * bandWidth) - (bandWidth / 2.0f)) + centerFrequency;
|
||||
//*vfo = roundf(*vfo / 100000.0f) * 100000.0f;
|
||||
}
|
||||
window->DrawList->AddRectFilled(ImVec2(pos.x + vfoPos - 20, pos.y + 11), ImVec2(pos.x + vfoPos + 20, pos.y + (lines * lineHeight) + 10), IM_COL32( 255, 255, 255, 50 ));
|
||||
window->DrawList->AddLine(ImVec2(pos.x + vfoPos, pos.y + 11), ImVec2(pos.x + vfoPos, pos.y + (lines * lineHeight) + 10), IM_COL32( 255, 0, 0, 255 ), 1.0f);
|
||||
}
|
||||
|
||||
uint32_t mapColor(float val) {
|
||||
float mapped = MAP_VAL(-50.0f, 0.0f, 0, 12, val);
|
||||
float mapped = MAP_VAL(-50.0f, -15.0f, 0, 12, val);
|
||||
mapped = std::max<float>(mapped, 0.0f);
|
||||
mapped = std::min<float>(mapped, 12.0f);
|
||||
int floored = floorf(mapped);
|
||||
@ -119,10 +144,6 @@ namespace ImGui {
|
||||
return ((uint32_t)255 << 24) | ((uint32_t)b << 16) | ((uint32_t)g << 8) | (uint32_t)r;
|
||||
}
|
||||
|
||||
uint32_t* img = NULL;
|
||||
int lastW = 0;
|
||||
int lastH = 0;
|
||||
|
||||
void WaterFall::drawWaterfall(ImGuiWindow* window, int width, int height, ImVec2 pos) {
|
||||
int w = width - 10;
|
||||
int h = height;
|
||||
@ -131,6 +152,7 @@ namespace ImGui {
|
||||
bool newSize = false;
|
||||
|
||||
if (lastW != w || lastH != h) {
|
||||
|
||||
newSize = true;
|
||||
lastW = w;
|
||||
lastH = h;
|
||||
@ -175,7 +197,7 @@ namespace ImGui {
|
||||
window->DrawList->AddImage((void*)(intptr_t)textureId, ImVec2(pos.x + 40, pos.y), ImVec2(pos.x + w, pos.y + h));
|
||||
}
|
||||
|
||||
void WaterFall::draw() {
|
||||
void WaterFall::draw(float* vfo) {
|
||||
ImGuiWindow* window = GetCurrentWindow();
|
||||
ImVec2 vMin = ImGui::GetWindowContentRegionMin();
|
||||
ImVec2 vMax = ImGui::GetWindowContentRegionMax();
|
||||
@ -190,10 +212,10 @@ namespace ImGui {
|
||||
window->DrawList->AddLine(ImVec2(vMin.x, vMin.y + 300), ImVec2(vMin.x + width, vMin.y + 300), IM_COL32( 50, 50, 50, 255 ), 1.0f);
|
||||
|
||||
buf_mtx.lock();
|
||||
if (fftBuffer.size() > height - 302) {
|
||||
if (fftBuffer.size() > height) {
|
||||
fftBuffer.resize(height - 302);
|
||||
}
|
||||
drawFFT(window, width, 300, vMin);
|
||||
drawFFT(window, width, 300, vMin, vfo);
|
||||
drawWaterfall(window, width - 2, height - 302, ImVec2(vMin.x + 1, vMin.y + 301));
|
||||
buf_mtx.unlock();
|
||||
}
|
||||
@ -202,6 +224,7 @@ namespace ImGui {
|
||||
buf_mtx.lock();
|
||||
fftBuffer.insert(fftBuffer.begin(), data);
|
||||
newSamples = true;
|
||||
fftDrawBuffer = NULL;
|
||||
buf_mtx.unlock();
|
||||
}
|
||||
};
|
||||
|
@ -10,7 +10,7 @@ namespace ImGui {
|
||||
public:
|
||||
WaterFall();
|
||||
|
||||
void draw();
|
||||
void draw(float* vfo);
|
||||
void pushFFT(std::vector<float> data, int n);
|
||||
|
||||
float centerFrequency;
|
||||
@ -19,14 +19,14 @@ namespace ImGui {
|
||||
|
||||
private:
|
||||
void drawWaterfall(ImGuiWindow* window, int width, int height, ImVec2 pos);
|
||||
void drawFFT(ImGuiWindow* window, int width, int height, ImVec2 pos);
|
||||
void drawFFT(ImGuiWindow* window, int width, int height, ImVec2 pos, float* vfo);
|
||||
|
||||
std::vector<std::vector<float>> fftBuffer;
|
||||
bool newSamples;
|
||||
std::mutex buf_mtx;
|
||||
GLuint textureId;
|
||||
uint8_t* pixelBuffer;
|
||||
|
||||
float* fftDrawBuffer;
|
||||
|
||||
};
|
||||
};
|
Loading…
Reference in New Issue
Block a user