diff --git a/CMakeLists.txt b/CMakeLists.txt index a82cd5f7..45bca06d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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/") diff --git a/src/cdsp/demodulation.h b/src/cdsp/demodulation.h index 284f89b4..e4ab95d7 100644 --- a/src/cdsp/demodulation.h +++ b/src/cdsp/demodulation.h @@ -3,6 +3,28 @@ #include #include +#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* 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* 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 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* _input; + bool running; int _bufferSize; float _phase; float _phasorSpeed; std::thread _workerThread; }; + + + class AMDemodulator { + public: + AMDemodulator() { + + } + + AMDemodulator(stream* in, int bufferSize) : output(bufferSize * 2) { + running = false; + _input = in; + _bufferSize = bufferSize; + } + + void init(stream* 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 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* _input; + bool running; + int _bufferSize; + std::thread _workerThread; + }; }; \ No newline at end of file diff --git a/src/cdsp/filter.h b/src/cdsp/filter.h index fb81ee28..1b56cdb4 100644 --- a/src/cdsp/filter.h +++ b/src/cdsp/filter.h @@ -4,78 +4,338 @@ #include #include +#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& 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* input, std::vector taps, int bufferSize) : output(bufferSize * 2) { - _in = input; - _bufferSize = bufferSize; - _tapCount = taps.size(); - delayBuf = new complex_t[_tapCount]; - _taps = taps; - } - - void init(stream* input, std::vector taps, int bufferSize) { + DecimatingFIRFilter(stream* input, std::vector 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* input, std::vector& 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& 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* 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 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* _in; complex_t* delayBuf; int _bufferSize; int _tapCount = 0; - std::vector _taps; + float _decim; std::thread _workerThread; + float* _taps; + bool running; }; + class FloatDecimatingFIRFilter { + public: + FloatDecimatingFIRFilter() { + + } + + FloatDecimatingFIRFilter(stream* input, std::vector 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* input, std::vector& 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& 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* input) { + if (running) { + return; + } + _in = input; + } + + void setDecimation(float decimation) { + if (running) { + return; + } + _decim = decimation; + } + + stream 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* _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* input, int bufferSize) { + _in = input; + _bufferSize = bufferSize; + output_a.init(bufferSize); + output_b.init(bufferSize); + } + + void init(stream* input, int bufferSize) { + _in = input; + _bufferSize = bufferSize; + output_a.init(bufferSize); + output_b.init(bufferSize); + } + + void start() { + _workerThread = std::thread(_worker, this); + } + + stream output_a; + stream 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* _in; + int _bufferSize; + std::thread _workerThread; + }; }; \ No newline at end of file diff --git a/src/cdsp/generator.h b/src/cdsp/generator.h index 02d58b23..d64c0818 100644 --- a/src/cdsp/generator.h +++ b/src/cdsp/generator.h @@ -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 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; }; }; \ No newline at end of file diff --git a/src/cdsp/math.h b/src/cdsp/math.h index 3dd435d1..f9a810fa 100644 --- a/src/cdsp/math.h +++ b/src/cdsp/math.h @@ -2,6 +2,7 @@ #include #include #include +#include namespace cdsp { class Multiplier { @@ -16,10 +17,35 @@ namespace cdsp { _bufferSize = bufferSize; } + void init(stream* a, stream* 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 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* _a; stream* _b; int _bufferSize; + bool running = false; std::thread _workerThread; }; }; \ No newline at end of file diff --git a/src/cdsp/modulation.h b/src/cdsp/modulation.h index f58e148f..d1421187 100644 --- a/src/cdsp/modulation.h +++ b/src/cdsp/modulation.h @@ -7,6 +7,10 @@ namespace cdsp { class FMModulator { public: + FMModulator() { + + } + FMModulator(stream* 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* 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); } diff --git a/src/cdsp/resampling.h b/src/cdsp/resampling.h index eaad24b4..458d3998 100644 --- a/src/cdsp/resampling.h +++ b/src/cdsp/resampling.h @@ -2,20 +2,66 @@ #include #include #include +#include +#include + namespace cdsp { class Interpolator { public: + Interpolator() { + + } + Interpolator(stream* in, float interpolation, int bufferSize) : output(bufferSize * 2) { _input = in; _interpolation = interpolation; _bufferSize = bufferSize; + running = false; + } + + void init(stream* 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* in) { + if (running) { + return; + } + _input = in; + } + stream 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* _input; int _bufferSize; float _interpolation; std::thread _workerThread; + bool running; }; class IQInterpolator { public: + IQInterpolator() { + + } + IQInterpolator(stream* in, float interpolation, int bufferSize) : output(bufferSize * 2) { _input = in; _interpolation = interpolation; _bufferSize = bufferSize; + running = false; + } + + void init(stream* 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 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* _input; int _bufferSize; float _interpolation; std::thread _workerThread; + bool running; }; class BlockDecimator { public: + BlockDecimator() { + + } + BlockDecimator(stream* in, int skip, int bufferSize) : output(bufferSize * 2) { _input = in; _skip = skip; _bufferSize = bufferSize; } + void init(stream* 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* input, float inputSampleRate, float outputSampleRate, int bufferSize, float customCutoff = INFINITY) { + _input = input; + float lowestFreq = std::min(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(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(_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* input) { + if (running) { + return; + } + _input = input; + if (_interp != 1) { + interpolator.setInput(input); + decimator.setInput(&interpolator.output); + } + else { + decimator.setInput(input); + } + } + + stream* output; + + private: + Interpolator interpolator; + FloatDecimatingFIRFilter decimator; + std::vector decimTaps; + stream* _input; + int _interp; + int _decim; + int _bufferSize; + float _inputSampleRate; + float _outputSampleRate; + bool running; + }; }; \ No newline at end of file diff --git a/src/cdsp/stream.h b/src/cdsp/stream.h index fbdc5843..5b363487 100644 --- a/src/cdsp/stream.h +++ b/src/cdsp/stream.h @@ -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 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 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; diff --git a/src/main.cpp b/src/main.cpp index e24b481b..cb876fc8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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(); diff --git a/src/main_window.cpp b/src/main_window.cpp index b7ab5c93..a7a8190c 100644 --- a/src/main_window.cpp +++ b/src/main_window.cpp @@ -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 _data; +std::vector 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(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(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 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(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(); } \ No newline at end of file diff --git a/src/signal_path.cpp b/src/signal_path.cpp index 2581f693..747d7b4c 100644 --- a/src/signal_path.cpp +++ b/src/signal_path.cpp @@ -1,20 +1,100 @@ #include -SignalPath::SignalPath(cdsp::stream* 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 iftaps; +std::vector ataps; + +void SignalPath::init(uint64_t sampleRate, int fftRate, int fftSize, cdsp::stream* 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(); } \ No newline at end of file diff --git a/src/signal_path.h b/src/signal_path.h index 159ea78c..aa79e40e 100644 --- a/src/signal_path.h +++ b/src/signal_path.h @@ -3,24 +3,50 @@ #include #include #include +#include +#include +#include class SignalPath { public: SignalPath(); - SignalPath(cdsp::stream* 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* 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; }; \ No newline at end of file diff --git a/src/waterfall.cpp b/src/waterfall.cpp index 2dbf2c9a..8bc16766 100644 --- a/src/waterfall.cpp +++ b/src/waterfall.cpp @@ -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 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(mapped, 0.0f); mapped = std::min(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(); } }; diff --git a/src/waterfall.h b/src/waterfall.h index 1c987e94..619a310f 100644 --- a/src/waterfall.h +++ b/src/waterfall.h @@ -10,7 +10,7 @@ namespace ImGui { public: WaterFall(); - void draw(); + void draw(float* vfo); void pushFFT(std::vector 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> fftBuffer; bool newSamples; std::mutex buf_mtx; GLuint textureId; uint8_t* pixelBuffer; - + float* fftDrawBuffer; }; }; \ No newline at end of file