diff --git a/CMakeLists.txt b/CMakeLists.txt index 45bca06d..43ad028d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,13 +2,21 @@ cmake_minimum_required(VERSION 3.9) project(sdrpp) # Compiler config -set(CMAKE_CXX_FLAGS "-OFast /std:c++17") +set(CMAKE_CXX_FLAGS "-O2 /std:c++17") # HackRF include_directories(sdrpp "C:/Program Files/PothosSDR/include/libhackrf/") link_directories(sdrpp "C:/Program Files/PothosSDR/lib/") link_libraries(hackrf) +# Volk +include_directories(sdrpp "C:/Program Files/PothosSDR/include/volk/") +link_libraries(volk) + +# SoapySDR +include_directories(sdrpp "C:/Program Files/PothosSDR/include/") +link_libraries(SoapySDR) + # Main code include_directories(sdrpp "src/") include_directories(sdrpp "src/imgui") diff --git a/lib/SoapySDR.lib b/lib/SoapySDR.lib new file mode 100644 index 00000000..3c34688f Binary files /dev/null and b/lib/SoapySDR.lib differ diff --git a/src/cdsp/fast_math.h b/src/cdsp/fast_math.h deleted file mode 100644 index eb08e9ed..00000000 --- a/src/cdsp/fast_math.h +++ /dev/null @@ -1,84 +0,0 @@ -#ifdef _MSC_VER -#include -#else -#include -#endif - -#include - - -inline void cm_mul(__m128& ab, const __m128& xy) -{ - //const __m128 aa = _mm_shuffle_ps(ab, ab, _MM_SHUFFLE(2, 2, 0, 0)); - const __m128 aa = _mm_moveldup_ps(ab); - const __m128 bb = _mm_movehdup_ps(ab); - //const __m128 bb = _mm_shuffle_ps(ab, ab, _MM_SHUFFLE(3, 3, 1, 1)); - const __m128 yx = _mm_shuffle_ps(xy, xy, _MM_SHUFFLE(2, 3, 0, 1)); - - const __m128 tmp = _mm_addsub_ps(_mm_mul_ps(aa, xy), _mm_mul_ps(bb, yx)); - ab = tmp; -} - -inline void do_mul(cdsp::complex_t* a, const cdsp::complex_t* b, int n) -{ - const int vector_size = 16; - int simd_iterations = n - (n % vector_size); - //assert(simd_iterations % vector_size == 0); - - for (int i = 0; i < simd_iterations; i += vector_size) - { - //__builtin_prefetch(a + i*4 + 64, 0); - //__builtin_prefetch(b + i*4 + 64, 0); - - __m128 vec_a = _mm_load_ps((float*)&a[i]); - __m128 vec_b = _mm_load_ps((float*)&b[i]); - - __m128 vec_a2 = _mm_load_ps((float*)&a[i+2]); - __m128 vec_b2 = _mm_load_ps((float*)&b[i+2]); - - __m128 vec_a3 = _mm_load_ps((float*)&a[i+4]); - __m128 vec_b3 = _mm_load_ps((float*)&b[i+4]); - - __m128 vec_a4 = _mm_load_ps((float*)&a[i+6]); - __m128 vec_b4 = _mm_load_ps((float*)&b[i+6]); - - __m128 vec_a5 = _mm_load_ps((float*)&a[i+8]); - __m128 vec_b5 = _mm_load_ps((float*)&b[i+8]); - - __m128 vec_a6 = _mm_load_ps((float*)&a[i+10]); - __m128 vec_b6 = _mm_load_ps((float*)&b[i+10]); - - __m128 vec_a7 = _mm_load_ps((float*)&a[i+12]); - __m128 vec_b7 = _mm_load_ps((float*)&b[i+12]); - - __m128 vec_a8 = _mm_load_ps((float*)&a[i+14]); - __m128 vec_b8 = _mm_load_ps((float*)&b[i+14]); - - cm_mul(vec_a, vec_b); - _mm_store_ps((float*)&a[i], vec_a); - cm_mul(vec_a2, vec_b2); - _mm_store_ps((float*)&a[i+2], vec_a2); - cm_mul(vec_a3, vec_b3); - _mm_store_ps((float*)&a[i+4], vec_a3); - cm_mul(vec_a4, vec_b4); - _mm_store_ps((float*)&a[i+6], vec_a4); - - cm_mul(vec_a5, vec_b5); - _mm_store_ps((float*)&a[i+8], vec_a5); - cm_mul(vec_a6, vec_b6); - _mm_store_ps((float*)&a[i+10], vec_a6); - cm_mul(vec_a7, vec_b7); - _mm_store_ps((float*)&a[i+12], vec_a7); - cm_mul(vec_a8, vec_b8); - _mm_store_ps((float*)&a[i+14], vec_a8); - } - - // finish with scalar - for (int i = simd_iterations; i < n; i++) - { - cdsp::complex_t cm; - cm.q = a[i].q*b[i].q - a[i].i*b[i].i; - cm.i = a[i].q*b[i].i + b[i].q*a[i].i; - a[i] = cm; - } -} \ No newline at end of file diff --git a/src/cdsp/fft_math.h b/src/cdsp/fft_math.h deleted file mode 100644 index 1ec6193b..00000000 --- a/src/cdsp/fft_math.h +++ /dev/null @@ -1,127 +0,0 @@ -#pragma once - -// Code by: Stellaris - -#include -#include -#include -#include - -#define M_PI 3.14159265359 - -#define R2(n) n, n + 2*64, n + 1*64, n + 3*64 -#define R4(n) R2(n), R2(n + 2*16), R2(n + 1*16), R2(n + 3*16) -#define R6(n) R4(n), R4(n + 2*4 ), R4(n + 1*4 ), R4(n + 3*4 ) - -// Lookup table that store the reverse of each table -uint8_t lut[256] = { R6(0), R6(2), R6(1), R6(3) }; - -inline uint16_t reverse_16(uint16_t val) -{ - uint8_t lo = lut[val&0xFF]; - uint8_t hi = lut[(val>>8)&0xFF]; - - return (lo << 8) | hi; -} - -static size_t reverse_bits(size_t x, int n) { - size_t result = 0; - for (int i = 0; i < n; i++, x >>= 1) - result = (result << 1) | (x & 1U); - return result; -} - -// struct complex -// { -// float re; -// float im; -// }; - -inline void bit_reverse_swap_aos(cdsp::complex_t* data, int n) -{ - assert(n < 65536); // only up to 16-bit size - int power2 = 0; - for (size_t temp = n; temp > 1U; temp >>= 1) - power2++; - - power2 = 16 - power2; - - for (int i = 0; i < n; i++) { - int j = reverse_16(i << power2); - if (j > i) { - std::swap(data[i], data[j]); - } - } -} - -struct trig_table -{ - float* cos_table; - float* sin_table; -}; -trig_table tables[14]; - -trig_table get_trig_table(int power2) -{ - assert(power2 < 14); - trig_table& table = tables[power2]; - if (table.cos_table == 0) - { - int n = 1 << (power2); - - table.cos_table = (float*)malloc((n/2) * sizeof(float)); - table.sin_table = (float*)malloc((n/2) * sizeof(float)); - - for (size_t i = 0; i < n / 2; i++) { - table.cos_table[i] = cos(2 * M_PI * i / n); - table.sin_table[i] = sin(2 * M_PI * i / n); - } - } - - return table; -} - -void fft_aos(cdsp::complex_t* data, int n) { - - int power2 = 0; - for (size_t temp = n; temp > 1U; temp >>= 1) - power2++; - - float* cos_table; float* sin_table; - trig_table table = get_trig_table(power2); - cos_table = table.cos_table; sin_table = table.sin_table; - - size_t size = (n / 2) * sizeof(float); - - // Bit-reversed addressing permutation - bit_reverse_swap_aos(data, n); - - // Cooley-Tukey decimation-in-time radix-2 FFT - for (size_t size = 2; size <= n; size *= 2) { - size_t halfsize = size / 2; - size_t tablestep = n / size; - for (size_t i = 0; i < n; i += size) { - for (size_t j = i, k = 0; j < i + halfsize; j++, k += tablestep) { - size_t l = j + halfsize; - float tpre = data[l].q * cos_table[k] + data[l].i * sin_table[k]; - float tpim = data[l].i * cos_table[k] - data[l].q * sin_table[k]; - data[l].q = data[j].q - tpre; - data[l].i = data[j].i - tpim; - data[j].q += tpre; - data[j].i += tpim; - } - } - } -} - -// for (int i = 0; i < 327680; i++) { -// complex cm; -// cm.q = complexes[i].q*sineGen[i].q - complexes[i].i*sineGen[i].i; -// cm.i = complexes[i].q*sineGen[i].i + sineGen[i].q*complexes[i].i; -// complexes[i] = cm; -// } - -// ImGui::Begin("FFT"); -// ImGui::PlotLines("I", [](void*data, int idx) { return ((float*)data)[idx]; }, endData, 1024, 0, 0, -1, 12, ImVec2(1024, 200)); -// ImGui::InputFloat("Freq", &frequency, 100000.0f, 100000.0f); -// ImGui::End(); \ No newline at end of file diff --git a/src/cdsp/file.h b/src/cdsp/file.h deleted file mode 100644 index ad57bd33..00000000 --- a/src/cdsp/file.h +++ /dev/null @@ -1,104 +0,0 @@ -#pragma once -#include -#include -#include -#include - -namespace cdsp { - #pragma pack(push, 1) - struct audio_sample_t { - int16_t l; - int16_t r; - }; - #pragma pack(pop) - - class RawFileSource { - public: - RawFileSource() { - - } - - RawFileSource(std::string path, int bufferSize) : output(bufferSize * 2) { - _bufferSize = bufferSize; - _file = std::ifstream(path.c_str(), std::ios::in | std::ios::binary); - } - - void init(std::string path, int bufferSize) { - output.init(bufferSize * 2); - _bufferSize = bufferSize; - _file = std::ifstream(path.c_str(), std::ios::in | std::ios::binary); - } - - void start() { - _workerThread = std::thread(_worker, this); - } - - stream output; - - private: - static void _worker(RawFileSource* _this) { - audio_sample_t* inBuf = new audio_sample_t[_this->_bufferSize]; - float* outBuf = new float[_this->_bufferSize]; - while (true) { - //printf("%d\n", _this->_bufferSize * sizeof(audio_sample_t)); - _this->_file.read((char*)inBuf, _this->_bufferSize * sizeof(audio_sample_t)); - for (int i = 0; i < _this->_bufferSize; i++) { - outBuf[i] = ((float)inBuf[i].l + (float)inBuf[i].r) / (float)0xFFFF; - } - //printf("Writing file samples\n"); - _this->output.write(outBuf, _this->_bufferSize); - } - } - - int _bufferSize; - std::ifstream _file; - std::thread _workerThread; - }; - - - - - class RawFileSink { - public: - RawFileSink() { - - } - - RawFileSink(std::string path, stream* in, int bufferSize) { - _bufferSize = bufferSize; - _input = in; - _file = std::ofstream(path.c_str(), std::ios::out | std::ios::binary); - } - - void init(std::string path, stream* in, int bufferSize) { - _bufferSize = bufferSize; - _input = in; - _file = std::ofstream(path.c_str(), std::ios::out | std::ios::binary); - } - - void start() { - _workerThread = std::thread(_worker, this); - } - - private: - static void _worker(RawFileSink* _this) { - float* inBuf = new float[_this->_bufferSize]; - audio_sample_t* outBuf = new audio_sample_t[_this->_bufferSize]; - while (true) { - //printf("%d\n", _this->_bufferSize * sizeof(audio_sample_t)); - _this->_input->read(inBuf, _this->_bufferSize); - for (int i = 0; i < _this->_bufferSize; i++) { - outBuf[i].l = inBuf[i] * 0x7FFF; - outBuf[i].r = outBuf[i].l; - } - //printf("Writing file samples\n"); - _this->_file.write((char*)outBuf, _this->_bufferSize * sizeof(audio_sample_t)); - } - } - - int _bufferSize; - std::ofstream _file; - stream* _input; - std::thread _workerThread; - }; -}; \ No newline at end of file diff --git a/src/cdsp/generator.h b/src/cdsp/generator.h deleted file mode 100644 index d64c0818..00000000 --- a/src/cdsp/generator.h +++ /dev/null @@ -1,156 +0,0 @@ -#pragma once -#include -#include -#include - -namespace cdsp { - class SineSource { - public: - SineSource() { - - } - - SineSource(float frequency, long sampleRate, int bufferSize) : output(bufferSize * 2) { - _bufferSize = bufferSize; - _phasorSpeed = (2 * 3.1415926535) / (sampleRate / frequency); - _phase = 0; - } - - void init(float frequency, long sampleRate, int bufferSize) { - output.init(bufferSize * 2); - _bufferSize = bufferSize; - _phasorSpeed = (2 * 3.1415926535) / (sampleRate / frequency); - _phase = 0; - } - - void start() { - _workerThread = std::thread(_worker, this); - } - - stream output; - - private: - static void _worker(SineSource* _this) { - float* outBuf = new float[_this->_bufferSize]; - while (true) { - for (int i = 0; i < _this->_bufferSize; i++) { - _this->_phase += _this->_phasorSpeed; - outBuf[i] = cos(_this->_phase); - } - _this->_phase = fmodf(_this->_phase, 2.0f * 3.1415926535); - _this->output.write(outBuf, _this->_bufferSize); - } - } - - int _bufferSize; - float _phasorSpeed; - float _phase; - std::thread _workerThread; - }; - - class RandomSource { - public: - RandomSource() { - - } - - RandomSource(float frequency, long sampleRate, int bufferSize) : output(bufferSize * 2) { - _bufferSize = bufferSize; - } - - void init(float frequency, long sampleRate, int bufferSize) { - output.init(bufferSize * 2); - _bufferSize = bufferSize; - } - - void start() { - _workerThread = std::thread(_worker, this); - } - - stream output; - - private: - static void _worker(RandomSource* _this) { - float* outBuf = new float[_this->_bufferSize]; - while (true) { - for (int i = 0; i < _this->_bufferSize; i++) { - outBuf[i] = ((float)rand() / ((float)RAND_MAX / 2.0f)) - 1.0f; - } - _this->output.write(outBuf, _this->_bufferSize); - } - } - - int _bufferSize; - std::thread _workerThread; - }; - - class ComplexSineSource { - public: - ComplexSineSource() { - - } - - 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; - - private: - static void _worker(ComplexSineSource* _this) { - complex_t* outBuf = new complex_t[_this->_bufferSize]; - while (true) { - for (int i = 0; i < _this->_bufferSize; i++) { - _this->_phase += _this->_phasorSpeed; - outBuf[i].i = sin(_this->_phase); - outBuf[i].q = cos(_this->_phase); - } - _this->_phase = fmodf(_this->_phase, 2.0f * 3.1415926535); - 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/hackrf.h b/src/cdsp/hackrf.h deleted file mode 100644 index 329e1478..00000000 --- a/src/cdsp/hackrf.h +++ /dev/null @@ -1,194 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include - -namespace cdsp { - #pragma pack(push, 1) - struct hackrf_sample_t { - int8_t q; - int8_t i; - }; - #pragma pack(pop) - - class Complex2HackRF { - public: - Complex2HackRF() { - - } - - Complex2HackRF(stream* in, int bufferSize) : output(bufferSize * 2) { - _input = in; - _bufferSize = bufferSize; - } - - void init(stream* in, int bufferSize) { - output.init(bufferSize * 2); - _input = in; - _bufferSize = bufferSize; - } - - stream output; - - void start() { - _workerThread = std::thread(_worker, this); - } - - private: - static void _worker(Complex2HackRF* _this) { - complex_t* inBuf = new complex_t[_this->_bufferSize]; - hackrf_sample_t* outBuf = new hackrf_sample_t[_this->_bufferSize]; - while (true) { - _this->_input->read(inBuf, _this->_bufferSize); - for (int i = 0; i < _this->_bufferSize; i++) { - outBuf[i].i = inBuf[i].i * 127.0f; - outBuf[i].q = inBuf[i].q * 127.0f; - } - _this->output.write(outBuf, _this->_bufferSize); - } - } - - int _bufferSize; - stream* _input; - std::thread _workerThread; - }; - - class HackRF2Complex { - public: - HackRF2Complex() { - - } - - HackRF2Complex(stream* out, int bufferSize) : input(bufferSize * 2) { - _output = out; - _bufferSize = bufferSize; - } - - void init(stream* out, int bufferSize) { - input.init(bufferSize * 2); - _output = out; - _bufferSize = bufferSize; - } - - void start() { - _workerThread = std::thread(_worker, this); - } - - stream input; - - private: - static void _worker(HackRF2Complex* _this) { - hackrf_sample_t* inBuf = new hackrf_sample_t[_this->_bufferSize]; - complex_t* outBuf = new complex_t[_this->_bufferSize]; - while (true) { - - _this->input.read(inBuf, _this->_bufferSize); - for (int i = 0; i < _this->_bufferSize; i++) { - outBuf[i].i = (float)inBuf[i].i / 127.0f; - outBuf[i].q = (float)inBuf[i].q / 127.0f; - } - _this->_output->write(outBuf, _this->_bufferSize); - } - } - - int _bufferSize; - stream* _output; - std::thread _workerThread; - }; - - class HackRFSink { - public: - HackRFSink() { - - } - - HackRFSink(hackrf_device* dev, int bufferSize, stream* input) : gen(input, bufferSize) { - _input = input; - _dev = dev; - gen.start(); - } - - void init(hackrf_device* dev, int bufferSize, stream* input) { - gen.init(input, bufferSize); - _input = input; - _dev = dev; - gen.start(); - } - - void start() { - streaming = true; - hackrf_start_tx(_dev, _worker, this); - } - - void stop() { - streaming = false; - Sleep(500); - hackrf_stop_tx(_dev); - } - - private: - static int _worker(hackrf_transfer* transfer) { - if (!((HackRFSink*)transfer->tx_ctx)->streaming) { - return -1; - } - hackrf_sample_t* buf = (hackrf_sample_t*)transfer->buffer; - ((HackRFSink*)transfer->tx_ctx)->gen.output.read(buf, transfer->buffer_length / 2); - return 0; - } - - Complex2HackRF gen; - bool streaming; - stream* _input; - hackrf_device* _dev; - }; - - class HackRFSource { - public: - HackRFSource() { - - } - - HackRFSource(hackrf_device* dev, int bufferSize) : output(bufferSize * 2), gen(&output, bufferSize) { - _dev = dev; - gen.start(); - } - - void init(hackrf_device* dev, int bufferSize) { - output.init(bufferSize * 2); - gen.init(&output, bufferSize); - _dev = dev; - gen.start(); - } - - void start() { - streaming = true; - hackrf_start_rx(_dev, _worker, this); - } - - void stop() { - streaming = false; - Sleep(500); - hackrf_stop_rx(_dev); - } - - stream output; - - private: - static int _worker(hackrf_transfer* transfer) { - if (!((HackRFSource*)transfer->rx_ctx)->streaming) { - return -1; - } - hackrf_sample_t* buf = (hackrf_sample_t*)transfer->buffer; - //printf("Writing samples\n"); - ((HackRFSource*)transfer->rx_ctx)->gen.input.write(buf, transfer->buffer_length / 2); - return 0; - } - - HackRF2Complex gen; - bool streaming; - hackrf_device* _dev; - }; -}; \ No newline at end of file diff --git a/src/cdsp/math.h b/src/cdsp/math.h deleted file mode 100644 index f9a810fa..00000000 --- a/src/cdsp/math.h +++ /dev/null @@ -1,70 +0,0 @@ -#pragma once -#include -#include -#include -#include - -namespace cdsp { - class Multiplier { - public: - Multiplier() { - - } - - Multiplier(stream* a, stream* b, int bufferSize) : output(bufferSize * 2) { - _a = a; - _b = b; - _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: - static void _worker(Multiplier* _this) { - complex_t* aBuf = new complex_t[_this->_bufferSize]; - complex_t* bBuf = new complex_t[_this->_bufferSize]; - complex_t* outBuf = new complex_t[_this->_bufferSize]; - while (true) { - 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 deleted file mode 100644 index d1421187..00000000 --- a/src/cdsp/modulation.h +++ /dev/null @@ -1,57 +0,0 @@ -#pragma once -#include -#include -#include -#include - -namespace cdsp { - class FMModulator { - public: - FMModulator() { - - } - - FMModulator(stream* in, float deviation, long sampleRate, int bufferSize) : output(bufferSize * 2) { - _input = in; - _bufferSize = bufferSize; - _phase = 0.0f; - _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); - } - - stream output; - - private: - static void _worker(FMModulator* _this) { - float* inBuf = new float[_this->_bufferSize]; - complex_t* outBuf = new complex_t[_this->_bufferSize]; - while (true) { - _this->_input->read(inBuf, _this->_bufferSize); - for (int i = 0; i < _this->_bufferSize; i++) { - _this->_phase += inBuf[i] * _this->_phasorSpeed; - outBuf[i].i = std::sinf(_this->_phase); - outBuf[i].q = std::cosf(_this->_phase); - } - _this->_phase = fmodf(_this->_phase, 2.0f * 3.1415926535); - _this->output.write(outBuf, _this->_bufferSize); - } - } - - stream* _input; - int _bufferSize; - float _phase; - float _phasorSpeed; - std::thread _workerThread; - }; -}; \ No newline at end of file diff --git a/src/cdsp/resampling.h b/src/cdsp/resampling.h deleted file mode 100644 index 458d3998..00000000 --- a/src/cdsp/resampling.h +++ /dev/null @@ -1,317 +0,0 @@ -#pragma once -#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: - static void _worker(Interpolator* _this) { - float* inBuf = new float[(int)((float)_this->_bufferSize / _this->_interpolation)]; - float* outBuf = new float[_this->_bufferSize]; - while (true) { - 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)]; - } - 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[_this->_bufferSize]; - complex_t* outBuf = new complex_t[_this->_bufferSize * _this->_interpolation]; - int outCount = _this->_bufferSize * _this->_interpolation; - while (true) { - if (_this->_input->read(inBuf, _this->_bufferSize) < 0) { break; }; - for (int i = 0; i < outCount; i++) { - outBuf[i] = inBuf[(int)((float)i / _this->_interpolation)]; - } - 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); - } - - stream output; - - private: - static void _worker(BlockDecimator* _this) { - complex_t* buf = new complex_t[_this->_bufferSize]; - while (true) { - _this->_input->readAndSkip(buf, _this->_bufferSize, _this->_skip); - _this->output.write(buf, _this->_bufferSize); - } - } - - stream* _input; - int _bufferSize; - 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/dsp/correction.h b/src/dsp/correction.h new file mode 100644 index 00000000..f1c4f9ee --- /dev/null +++ b/src/dsp/correction.h @@ -0,0 +1,63 @@ +#pragma once +#include +#include +#include +#include + +namespace dsp { + class DCBiasRemover { + public: + DCBiasRemover() { + + } + + DCBiasRemover(stream* input, int bufferSize) : output(bufferSize * 2) { + _in = input; + _bufferSize = bufferSize; + bypass = false; + } + + void init(stream* input, int bufferSize) { + output.init(bufferSize * 2); + _in = input; + _bufferSize = bufferSize; + bypass = false; + } + + void start() { + _workerThread = std::thread(_worker, this); + } + + stream output; + bool bypass; + + private: + static void _worker(DCBiasRemover* _this) { + complex_t* buf = new complex_t[_this->_bufferSize]; + float ibias = 0.0f; + float qbias = 0.0f; + while (true) { + _this->_in->read(buf, _this->_bufferSize); + if (_this->bypass) { + _this->output.write(buf, _this->_bufferSize); + continue; + } + for (int i = 0; i < _this->_bufferSize; i++) { + ibias += buf[i].i; + qbias += buf[i].q; + } + ibias /= _this->_bufferSize; + qbias /= _this->_bufferSize; + for (int i = 0; i < _this->_bufferSize; i++) { + buf[i].i -= ibias; + buf[i].q -= qbias; + } + _this->output.write(buf, _this->_bufferSize); + } + } + + stream* _in; + int _bufferSize; + std::thread _workerThread; + }; +}; \ No newline at end of file diff --git a/src/cdsp/demodulation.h b/src/dsp/demodulator.h similarity index 66% rename from src/cdsp/demodulation.h rename to src/dsp/demodulator.h index e4ab95d7..cef14e78 100644 --- a/src/cdsp/demodulation.h +++ b/src/dsp/demodulator.h @@ -1,13 +1,17 @@ #pragma once #include -#include -#include +#include +#include + +/* + TODO: + - Add a sample rate ajustment function to all demodulators +*/ #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) -{ +inline float fast_arctan2(float y, float x) { float abs_y = fabs(y)+1e-10; float r, angle; if (x>=0) @@ -26,26 +30,26 @@ inline float fast_arctan2(float y, float x) return angle; } -namespace cdsp { +namespace dsp { class FMDemodulator { public: FMDemodulator() { } - FMDemodulator(stream* in, float deviation, long sampleRate, int bufferSize) : output(bufferSize * 2) { + FMDemodulator(stream* in, float deviation, long sampleRate, int blockSize) : output(blockSize * 2) { running = false; _input = in; - _bufferSize = bufferSize; + _blockSize = blockSize; _phase = 0.0f; _phasorSpeed = (2 * 3.1415926535) / (sampleRate / deviation); } - void init(stream* in, float deviation, long sampleRate, int bufferSize) { - output.init(bufferSize * 2); + void init(stream* in, float deviation, long sampleRate, int blockSize) { + output.init(blockSize * 2); running = false; _input = in; - _bufferSize = bufferSize; + _blockSize = blockSize; _phase = 0.0f; _phasorSpeed = (2 * 3.1415926535) / (sampleRate / deviation); } @@ -70,17 +74,25 @@ namespace cdsp { output.clearWriteStop(); } + void setBlockSize(int blockSize) { + if (running) { + return; + } + _blockSize = blockSize; + output.setMaxLatency(_blockSize * 2); + } + stream output; private: static void _worker(FMDemodulator* _this) { - complex_t* inBuf = new complex_t[_this->_bufferSize]; - float* outBuf = new float[_this->_bufferSize]; + complex_t* inBuf = new complex_t[_this->_blockSize]; + float* outBuf = new float[_this->_blockSize]; float diff = 0; float currentPhase = 0; while (true) { - if (_this->_input->read(inBuf, _this->_bufferSize) < 0) { return; }; - for (int i = 0; i < _this->_bufferSize; i++) { + if (_this->_input->read(inBuf, _this->_blockSize) < 0) { return; }; + for (int i = 0; i < _this->_blockSize; i++) { currentPhase = fast_arctan2(inBuf[i].i, inBuf[i].q); diff = currentPhase - _this->_phase; if (diff > 3.1415926535f) { diff -= 2 * 3.1415926535f; } @@ -88,13 +100,13 @@ namespace cdsp { outBuf[i] = diff / _this->_phasorSpeed; _this->_phase = currentPhase; } - if (_this->output.write(outBuf, _this->_bufferSize) < 0) { return; }; + if (_this->output.write(outBuf, _this->_blockSize) < 0) { return; }; } } stream* _input; bool running; - int _bufferSize; + int _blockSize; float _phase; float _phasorSpeed; std::thread _workerThread; @@ -107,17 +119,17 @@ namespace cdsp { } - AMDemodulator(stream* in, int bufferSize) : output(bufferSize * 2) { + AMDemodulator(stream* in, int blockSize) : output(blockSize * 2) { running = false; _input = in; - _bufferSize = bufferSize; + _blockSize = blockSize; } - void init(stream* in, int bufferSize) { - output.init(bufferSize * 2); + void init(stream* in, int blockSize) { + output.init(blockSize * 2); running = false; _input = in; - _bufferSize = bufferSize; + _blockSize = blockSize; } void start() { @@ -140,18 +152,26 @@ namespace cdsp { output.clearWriteStop(); } + void setBlockSize(int blockSize) { + if (running) { + return; + } + _blockSize = blockSize; + output.setMaxLatency(_blockSize * 2); + } + stream output; private: static void _worker(AMDemodulator* _this) { - complex_t* inBuf = new complex_t[_this->_bufferSize]; - float* outBuf = new float[_this->_bufferSize]; + complex_t* inBuf = new complex_t[_this->_blockSize]; + float* outBuf = new float[_this->_blockSize]; float min, max, amp; while (true) { - if (_this->_input->read(inBuf, _this->_bufferSize) < 0) { return; }; + if (_this->_input->read(inBuf, _this->_blockSize) < 0) { break; }; min = INFINITY; max = 0.0f; - for (int i = 0; i < _this->_bufferSize; i++) { + for (int i = 0; i < _this->_blockSize; i++) { outBuf[i] = sqrt((inBuf[i].i*inBuf[i].i) + (inBuf[i].q*inBuf[i].q)); if (outBuf[i] < min) { min = outBuf[i]; @@ -161,16 +181,18 @@ namespace cdsp { } } amp = (max - min); - for (int i = 0; i < _this->_bufferSize; i++) { + for (int i = 0; i < _this->_blockSize; i++) { outBuf[i] = (outBuf[i] - min) / (max - min); } - if (_this->output.write(outBuf, _this->_bufferSize) < 0) { return; }; + if (_this->output.write(outBuf, _this->_blockSize) < 0) { break; }; } + delete[] inBuf; + delete[] outBuf; } stream* _input; bool running; - int _bufferSize; + int _blockSize; std::thread _workerThread; }; }; \ No newline at end of file diff --git a/src/cdsp/filter.h b/src/dsp/filter.h similarity index 57% rename from src/cdsp/filter.h rename to src/dsp/filter.h index 1b56cdb4..575529a5 100644 --- a/src/cdsp/filter.h +++ b/src/dsp/filter.h @@ -1,41 +1,40 @@ #pragma once #include -#include -#include +#include +#include #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; +namespace dsp { + 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; + } } - for (int i = 0; i < M; i++) { - taps[i] /= sum; - } -} -namespace cdsp { class DecimatingFIRFilter { public: DecimatingFIRFilter() { } - DecimatingFIRFilter(stream* input, std::vector taps, int bufferSize, float decim) : output(bufferSize * 2) { - output.init(bufferSize * 2); + DecimatingFIRFilter(stream* input, std::vector taps, int blockSize, float decim) : output(blockSize * 2) { + output.init(blockSize * 2); _in = input; - _bufferSize = bufferSize; + _blockSize = blockSize; _tapCount = taps.size(); delayBuf = new complex_t[_tapCount]; @@ -54,10 +53,10 @@ namespace cdsp { running = false; } - void init(stream* input, std::vector& taps, int bufferSize, float decim) { - output.init(bufferSize * 2); + void init(stream* input, std::vector& taps, int blockSize, float decim) { + output.init(blockSize * 2); _in = input; - _bufferSize = bufferSize; + _blockSize = blockSize; _tapCount = taps.size(); delayBuf = new complex_t[_tapCount]; @@ -101,8 +100,11 @@ namespace cdsp { return; } _tapCount = taps.size(); + printf("[%d]\n", _tapCount); delete[] _taps; + delete[] delayBuf; _taps = new float[_tapCount]; + delayBuf = new complex_t[_tapCount]; for (int i = 0; i < _tapCount; i++) { _taps[i] = taps[i]; } @@ -120,28 +122,30 @@ namespace cdsp { return; } _decim = decimation; + output.setMaxLatency((_blockSize * 2) / _decim); } - void setBufferSize(int bufferSize) { + void setBlockSize(int blockSize) { if (running) { return; } - _bufferSize = bufferSize; + _blockSize = blockSize; + output.setMaxLatency((_blockSize * 2) / _decim); } stream output; private: static void _worker(DecimatingFIRFilter* _this) { - int outputSize = _this->_bufferSize / _this->_decim; - complex_t* inBuf = new complex_t[_this->_bufferSize]; + int outputSize = _this->_blockSize / _this->_decim; + complex_t* inBuf = new complex_t[_this->_blockSize]; complex_t* outBuf = new complex_t[outputSize]; float tap = 0.0f; int delayOff; - void* delayStart = &inBuf[_this->_bufferSize - (_this->_tapCount - 1)]; + void* delayStart = &inBuf[_this->_blockSize - (_this->_tapCount - 1)]; int delaySize = (_this->_tapCount - 1) * sizeof(complex_t); - int bufferSize = _this->_bufferSize; + int blockSize = _this->_blockSize; int outBufferLength = outputSize * sizeof(complex_t); int tapCount = _this->_tapCount; int decim = _this->_decim; @@ -149,7 +153,7 @@ namespace cdsp { int id = 0; while (true) { - if (_this->_in->read(inBuf, bufferSize) < 0) { break; }; + if (_this->_in->read(inBuf, blockSize) < 0) { break; }; memset(outBuf, 0, outBufferLength); for (int t = 0; t < tapCount; t++) { @@ -161,7 +165,7 @@ namespace cdsp { delayOff = tapCount - t; id = 0; - for (int i = 0; i < bufferSize; i += decim) { + for (int i = 0; i < blockSize; i += decim) { if (i < t) { outBuf[id].i += tap * delayBuf[delayOff + i].i; outBuf[id].q += tap * delayBuf[delayOff + i].q; @@ -182,7 +186,7 @@ namespace cdsp { stream* _in; complex_t* delayBuf; - int _bufferSize; + int _blockSize; int _tapCount = 0; float _decim; std::thread _workerThread; @@ -190,16 +194,17 @@ namespace cdsp { bool running; }; + class FloatDecimatingFIRFilter { public: FloatDecimatingFIRFilter() { } - FloatDecimatingFIRFilter(stream* input, std::vector taps, int bufferSize, float decim) : output(bufferSize * 2) { - output.init(bufferSize * 2); + FloatDecimatingFIRFilter(stream* input, std::vector taps, int blockSize, float decim) : output(blockSize * 2) { + output.init(blockSize * 2); _in = input; - _bufferSize = bufferSize; + _blockSize = blockSize; _tapCount = taps.size(); delayBuf = new float[_tapCount]; @@ -217,10 +222,10 @@ namespace cdsp { running = false; } - void init(stream* input, std::vector& taps, int bufferSize, float decim) { - output.init(bufferSize * 2); + void init(stream* input, std::vector& taps, int blockSize, float decim) { + output.init(blockSize * 2); _in = input; - _bufferSize = bufferSize; + _blockSize = blockSize; _tapCount = taps.size(); delayBuf = new float[_tapCount]; @@ -239,11 +244,17 @@ namespace cdsp { } void start() { + if (running) { + return; + } running = true; _workerThread = std::thread(_worker, this); } void stop() { + if (!running) { + return; + } _in->stopReader(); output.stopWriter(); _workerThread.join(); @@ -258,7 +269,9 @@ namespace cdsp { } _tapCount = taps.size(); delete[] _taps; + delete[] delayBuf; _taps = new float[_tapCount]; + delayBuf = new float[_tapCount]; for (int i = 0; i < _tapCount; i++) { _taps[i] = taps[i]; } @@ -276,29 +289,38 @@ namespace cdsp { return; } _decim = decimation; + output.setMaxLatency((_blockSize * 2) / _decim); + } + + void setBlockSize(int blockSize) { + if (running) { + return; + } + _blockSize = blockSize; + output.setMaxLatency((_blockSize * 2) / _decim); } stream output; private: static void _worker(FloatDecimatingFIRFilter* _this) { - int outputSize = _this->_bufferSize / _this->_decim; - float* inBuf = new float[_this->_bufferSize]; + int outputSize = _this->_blockSize / _this->_decim; + float* inBuf = new float[_this->_blockSize]; float* outBuf = new float[outputSize]; float tap = 0.0f; int delayOff; - void* delayStart = &inBuf[_this->_bufferSize - (_this->_tapCount - 1)]; + void* delayStart = &inBuf[_this->_blockSize - (_this->_tapCount - 1)]; int delaySize = (_this->_tapCount - 1) * sizeof(float); - int bufferSize = _this->_bufferSize; - int outBufferLength = outputSize * sizeof(float); + int blockSize = _this->_blockSize; + int outBufferLength = outputSize * sizeof(complex_t); 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; }; + if (_this->_in->read(inBuf, blockSize) < 0) { break; }; memset(outBuf, 0, outBufferLength); for (int t = 0; t < tapCount; t++) { @@ -310,7 +332,7 @@ namespace cdsp { delayOff = tapCount - t; id = 0; - for (int i = 0; i < bufferSize; i += decim) { + for (int i = 0; i < blockSize; i += decim) { if (i < t) { outBuf[id] += tap * delayBuf[delayOff + i]; id++; @@ -329,150 +351,11 @@ namespace cdsp { stream* _in; float* delayBuf; - int _bufferSize; + int _blockSize; int _tapCount = 0; float _decim; std::thread _workerThread; float* _taps; bool running; }; - - class DCBiasRemover { - public: - DCBiasRemover() { - - } - - DCBiasRemover(stream* input, int bufferSize) : output(bufferSize * 2) { - _in = input; - _bufferSize = bufferSize; - bypass = false; - } - - void init(stream* input, int bufferSize) { - output.init(bufferSize * 2); - _in = input; - _bufferSize = bufferSize; - bypass = false; - } - - void start() { - _workerThread = std::thread(_worker, this); - } - - stream output; - bool bypass; - - private: - static void _worker(DCBiasRemover* _this) { - complex_t* buf = new complex_t[_this->_bufferSize]; - float ibias = 0.0f; - float qbias = 0.0f; - while (true) { - _this->_in->read(buf, _this->_bufferSize); - if (_this->bypass) { - _this->output.write(buf, _this->_bufferSize); - continue; - } - for (int i = 0; i < _this->_bufferSize; i++) { - ibias += buf[i].i; - qbias += buf[i].q; - } - ibias /= _this->_bufferSize; - qbias /= _this->_bufferSize; - for (int i = 0; i < _this->_bufferSize; i++) { - buf[i].i -= ibias; - buf[i].q -= qbias; - } - _this->output.write(buf, _this->_bufferSize); - } - } - - stream* _in; - int _bufferSize; - std::thread _workerThread; - }; - - class HandlerSink { - public: - HandlerSink() { - - } - - HandlerSink(stream* input, complex_t* buffer, int bufferSize, void handler(complex_t*)) { - _in = input; - _bufferSize = bufferSize; - _buffer = buffer; - _handler = handler; - } - - void init(stream* input, complex_t* buffer, int bufferSize, void handler(complex_t*)) { - _in = input; - _bufferSize = bufferSize; - _buffer = buffer; - _handler = handler; - } - - void start() { - _workerThread = std::thread(_worker, this); - } - - bool bypass; - - private: - static void _worker(HandlerSink* _this) { - while (true) { - _this->_in->read(_this->_buffer, _this->_bufferSize); - _this->_handler(_this->_buffer); - } - } - - stream* _in; - int _bufferSize; - complex_t* _buffer; - 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/dsp/math.h b/src/dsp/math.h new file mode 100644 index 00000000..e8fdbdc3 --- /dev/null +++ b/src/dsp/math.h @@ -0,0 +1,85 @@ +#pragma once +#include +#include +#include +#include + +#ifndef M_PI +#define M_PI 3.1415926535f +#endif + +namespace dsp { + class Multiplier { + public: + Multiplier() { + + } + + Multiplier(stream* a, stream* b, int blockSize) : output(blockSize * 2) { + _a = a; + _b = b; + _blockSize = blockSize; + } + + void init(stream* a, stream* b, int blockSize) { + output.init(blockSize * 2); + _a = a; + _b = b; + _blockSize = blockSize; + } + + 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(); + } + + void setBlockSize(int blockSize) { + if (running) { + return; + } + _blockSize = blockSize; + output.setMaxLatency(blockSize * 2); + } + + stream output; + + private: + static void _worker(Multiplier* _this) { + complex_t* aBuf = (complex_t*)volk_malloc(sizeof(complex_t) * _this->_blockSize, volk_get_alignment()); + complex_t* bBuf = (complex_t*)volk_malloc(sizeof(complex_t) * _this->_blockSize, volk_get_alignment()); + complex_t* outBuf = (complex_t*)volk_malloc(sizeof(complex_t) * _this->_blockSize, volk_get_alignment()); + while (true) { + if (_this->_a->read(aBuf, _this->_blockSize) < 0) { break; }; + if (_this->_b->read(bBuf, _this->_blockSize) < 0) { break; }; + volk_32fc_x2_multiply_32fc((lv_32fc_t*)outBuf, (lv_32fc_t*)aBuf, (lv_32fc_t*)bBuf, _this->_blockSize); + if (_this->output.write(outBuf, _this->_blockSize) < 0) { break; }; + } + volk_free(aBuf); + volk_free(bBuf); + volk_free(outBuf); + } + + stream* _a; + stream* _b; + int _blockSize; + bool running = false; + std::thread _workerThread; + }; +}; \ No newline at end of file diff --git a/src/dsp/resampling.h b/src/dsp/resampling.h new file mode 100644 index 00000000..eb8e1b6f --- /dev/null +++ b/src/dsp/resampling.h @@ -0,0 +1,425 @@ +#pragma once +#include +#include +#include +#include +#include + + +namespace dsp { + template + class Interpolator { + public: + Interpolator() { + + } + + Interpolator(stream* in, float interpolation, int blockSize) : output(blockSize * interpolation * 2) { + _input = in; + _interpolation = interpolation; + _blockSize = blockSize; + } + + void init(stream* in, float interpolation, int blockSize) { + output.init(blockSize * 2); + _input = in; + _interpolation = interpolation; + _blockSize = blockSize; + } + + 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; + output.setMaxLatency(_blockSize * _interpolation * 2); + } + + void setBlockSize(int blockSize) { + if (running) { + return; + } + _blockSize = blockSize; + output.setMaxLatency(_blockSize * _interpolation * 2); + } + + void setInput(stream* input) { + if (running) { + return; + } + _input = input; + } + + stream output; + + private: + static void _worker(Interpolator* _this) { + T* inBuf = new T[_this->_blockSize]; + T* outBuf = new T[_this->_blockSize * _this->_interpolation]; + int outCount = _this->_blockSize * _this->_interpolation; + while (true) { + if (_this->_input->read(inBuf, _this->_blockSize) < 0) { break; }; + for (int i = 0; i < outCount; i++) { + outBuf[i] = inBuf[(int)((float)i / _this->_interpolation)]; + } + if (_this->output.write(outBuf, outCount) < 0) { break; }; + } + delete[] inBuf; + delete[] outBuf; + } + + stream* _input; + int _blockSize; + float _interpolation; + std::thread _workerThread; + bool running = false; + }; + + class BlockDecimator { + public: + BlockDecimator() { + + } + + BlockDecimator(stream* in, int skip, int blockSize) : output(blockSize * 2) { + _input = in; + _skip = skip; + _blockSize = blockSize; + } + + void init(stream* in, int skip, int blockSize) { + output.init(blockSize * 2); + _input = in; + _skip = skip; + _blockSize = blockSize; + } + + void start() { + if (running) { + return; + } + _workerThread = std::thread(_worker, this); + } + + void stop() { + if (!running) { + return; + } + _input->stopReader(); + output.stopWriter(); + _workerThread.join(); + _input->clearReadStop(); + output.clearWriteStop(); + running = false; + } + + void setBlockSize(int blockSize) { + if (running) { + return; + } + _blockSize = blockSize; + output.setMaxLatency(blockSize * 2); + } + + void setSkip(int skip) { + if (running) { + return; + } + _skip = skip; + } + + stream output; + + private: + static void _worker(BlockDecimator* _this) { + complex_t* buf = new complex_t[_this->_blockSize]; + while (true) { + _this->_input->readAndSkip(buf, _this->_blockSize, _this->_skip); + _this->output.write(buf, _this->_blockSize); + } + } + + stream* _input; + int _blockSize; + int _skip; + std::thread _workerThread; + bool running = false; + }; + + class Resampler { + public: + Resampler() { + + } + + void init(stream* in, float inputSampleRate, float outputSampleRate, float bandWidth, int blockSize) { + _input = in; + _outputSampleRate = outputSampleRate; + _inputSampleRate = inputSampleRate; + int _gcd = std::gcd((int)inputSampleRate, (int)outputSampleRate); + _interp = outputSampleRate / _gcd; + _decim = inputSampleRate / _gcd; + _blockSize = blockSize; + output = &decim.output; + + dsp::BlackmanWindow(_taps, inputSampleRate * _interp, outputSampleRate / 2.0f, outputSampleRate / 2.0f); + + interp.init(in, _interp, blockSize); + if (_interp == 1) { + decim.init(in, _taps, blockSize, _decim); + } + else { + decim.init(&interp.output, _taps, blockSize * _interp, _decim); + } + } + + void start() { + if (_interp != 1) { + interp.start(); + } + decim.start(); + running = true; + } + + void stop() { + interp.stop(); + decim.stop(); + running = false; + } + + void setInputSampleRate(float inputSampleRate, int blockSize = -1) { + stop(); + _inputSampleRate = inputSampleRate; + int _gcd = std::gcd((int)inputSampleRate, (int)_outputSampleRate); + _interp = _outputSampleRate / _gcd; + _decim = inputSampleRate / _gcd; + + dsp::BlackmanWindow(_taps, inputSampleRate * _interp, _outputSampleRate / 2.0f, _outputSampleRate / 2.0f); + decim.setTaps(_taps); + + interp.setInterpolation(_interp); + decim.setDecimation(_decim); + if (blockSize > 0) { + _blockSize = blockSize; + interp.setBlockSize(_blockSize); + } + decim.setBlockSize(_blockSize * _interp); + + if (_interp == 1) { + decim.setInput(_input); + } + else { + decim.setInput(&interp.output); + interp.start(); + } + start(); + } + + void setOutputSampleRate(float outputSampleRate) { + stop(); + _outputSampleRate = outputSampleRate; + int _gcd = std::gcd((int)_inputSampleRate, (int)outputSampleRate); + _interp = outputSampleRate / _gcd; + _decim = _inputSampleRate / _gcd; + + dsp::BlackmanWindow(_taps, _inputSampleRate * _interp, outputSampleRate / 2.0f, outputSampleRate / 2.0f); + decim.setTaps(_taps); + + interp.setInterpolation(_interp); + decim.setDecimation(_decim); + decim.setBlockSize(_blockSize * _interp); + + if (_interp == 1) { + decim.setInput(_input); + } + else { + decim.setInput(&interp.output); + } + start(); + } + + void setBlockSize(int blockSize) { + stop(); + _blockSize = blockSize; + interp.setBlockSize(_blockSize); + decim.setBlockSize(_blockSize * _interp); + start(); + } + + void setInput(stream* input) { + if (running) { + return; + } + _input = input; + interp.setInput(_input); + if (_interp == 1) { + decim.setInput(_input); + } + } + + stream* output; + + private: + Interpolator interp; + DecimatingFIRFilter decim; + stream* _input; + + std::vector _taps; + int _interp; + int _decim; + float _outputSampleRate; + float _inputSampleRate; + float _blockSize; + bool running = false; + }; + + + + class FloatResampler { + public: + FloatResampler() { + + } + + void init(stream* in, float inputSampleRate, float outputSampleRate, float bandWidth, int blockSize) { + _input = in; + _outputSampleRate = outputSampleRate; + _inputSampleRate = inputSampleRate; + int _gcd = std::gcd((int)inputSampleRate, (int)outputSampleRate); + _interp = outputSampleRate / _gcd; + _decim = inputSampleRate / _gcd; + _blockSize = blockSize; + output = &decim.output; + + dsp::BlackmanWindow(_taps, inputSampleRate * _interp, outputSampleRate / 2.0f, outputSampleRate / 2.0f); + + interp.init(in, _interp, blockSize); + if (_interp == 1) { + decim.init(in, _taps, blockSize, _decim); + } + else { + decim.init(&interp.output, _taps, blockSize * _interp, _decim); + } + } + + void start() { + if (_interp != 1) { + interp.start(); + } + decim.start(); + running = true; + } + + void stop() { + interp.stop(); + decim.stop(); + running = false; + } + + void setInputSampleRate(float inputSampleRate, int blockSize = -1) { + stop(); + _inputSampleRate = inputSampleRate; + int _gcd = std::gcd((int)inputSampleRate, (int)_outputSampleRate); + _interp = _outputSampleRate / _gcd; + _decim = inputSampleRate / _gcd; + + dsp::BlackmanWindow(_taps, inputSampleRate * _interp, _outputSampleRate / 2.0f, _outputSampleRate / 2.0f); + decim.setTaps(_taps); + + interp.setInterpolation(_interp); + decim.setDecimation(_decim); + if (blockSize > 0) { + _blockSize = blockSize; + interp.setBlockSize(_blockSize); + } + decim.setBlockSize(_blockSize * _interp); + + if (_interp == 1) { + decim.setInput(_input); + } + else { + decim.setInput(&interp.output); + } + start(); + } + + void setOutputSampleRate(float outputSampleRate) { + stop(); + _outputSampleRate = outputSampleRate; + int _gcd = std::gcd((int)_inputSampleRate, (int)outputSampleRate); + _interp = outputSampleRate / _gcd; + _decim = _inputSampleRate / _gcd; + + dsp::BlackmanWindow(_taps, _inputSampleRate * _interp, outputSampleRate / 2.0f, outputSampleRate / 2.0f); + decim.setTaps(_taps); + + interp.setInterpolation(_interp); + decim.setDecimation(_decim); + decim.setBlockSize(_blockSize * _interp); + + if (_interp == 1) { + decim.setInput(_input); + } + else { + decim.setInput(&interp.output); + } + start(); + } + + void setBlockSize(int blockSize) { + stop(); + _blockSize = blockSize; + interp.setBlockSize(_blockSize); + decim.setBlockSize(_blockSize * _interp); + start(); + } + + void setInput(stream* input) { + if (running) { + return; + } + _input = input; + interp.setInput(_input); + if (_interp == 1) { + decim.setInput(_input); + } + } + + stream* output; + + private: + Interpolator interp; + FloatDecimatingFIRFilter decim; + stream* _input; + + std::vector _taps; + int _interp; + int _decim; + float _outputSampleRate; + float _inputSampleRate; + float _blockSize; + bool running = false; + }; + + +}; \ No newline at end of file diff --git a/src/dsp/routing.h b/src/dsp/routing.h new file mode 100644 index 00000000..e1d61f29 --- /dev/null +++ b/src/dsp/routing.h @@ -0,0 +1,49 @@ +#pragma once +#include +#include +#include +#include + +namespace dsp { + 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/dsp/sink.h b/src/dsp/sink.h new file mode 100644 index 00000000..e585a796 --- /dev/null +++ b/src/dsp/sink.h @@ -0,0 +1,118 @@ +#pragma once +#include +#include +#include +#include + +namespace dsp { + class HandlerSink { + public: + HandlerSink() { + + } + + HandlerSink(stream* input, complex_t* buffer, int bufferSize, void handler(complex_t*)) { + _in = input; + _bufferSize = bufferSize; + _buffer = buffer; + _handler = handler; + } + + void init(stream* input, complex_t* buffer, int bufferSize, void handler(complex_t*)) { + _in = input; + _bufferSize = bufferSize; + _buffer = buffer; + _handler = handler; + } + + void start() { + _workerThread = std::thread(_worker, this); + } + + bool bypass; + + private: + static void _worker(HandlerSink* _this) { + while (true) { + _this->_in->read(_this->_buffer, _this->_bufferSize); + _this->_handler(_this->_buffer); + } + } + + stream* _in; + int _bufferSize; + complex_t* _buffer; + std::thread _workerThread; + void (*_handler)(complex_t*); + }; + + class NullSink { + public: + NullSink() { + + } + + NullSink(stream* input, int bufferSize) { + _in = input; + _bufferSize = bufferSize; + } + + void init(stream* input, int bufferSize) { + _in = input; + _bufferSize = bufferSize; + } + + void start() { + _workerThread = std::thread(_worker, this); + } + + bool bypass; + + private: + static void _worker(NullSink* _this) { + complex_t* buf = new complex_t[_this->_bufferSize]; + while (true) { + _this->_in->read(buf, _this->_bufferSize); + } + } + + stream* _in; + int _bufferSize; + std::thread _workerThread; + }; + + class FloatNullSink { + public: + FloatNullSink() { + + } + + FloatNullSink(stream* input, int bufferSize) { + _in = input; + _bufferSize = bufferSize; + } + + void init(stream* input, int bufferSize) { + _in = input; + _bufferSize = bufferSize; + } + + void start() { + _workerThread = std::thread(_worker, this); + } + + bool bypass; + + private: + static void _worker(FloatNullSink* _this) { + float* buf = new float[_this->_bufferSize]; + while (true) { + _this->_in->read(buf, _this->_bufferSize); + } + } + + stream* _in; + int _bufferSize; + std::thread _workerThread; + }; +}; \ No newline at end of file diff --git a/src/dsp/source.h b/src/dsp/source.h new file mode 100644 index 00000000..ac12a5ef --- /dev/null +++ b/src/dsp/source.h @@ -0,0 +1,82 @@ +#pragma once +#include +#include +#include +#include + +namespace dsp { + class SineSource { + public: + SineSource() { + + } + + SineSource(float frequency, long sampleRate, int blockSize) : output(blockSize * 2) { + _blockSize = blockSize; + _sampleRate = sampleRate; + _phasorSpeed = (2 * 3.1415926535) / (sampleRate / frequency); + _phase = 0; + } + + void init(float frequency, long sampleRate, int blockSize) { + output.init(blockSize * 2); + _sampleRate = sampleRate; + _blockSize = blockSize; + _phasorSpeed = (2 * 3.1415926535) / (sampleRate / frequency); + _phase = 0; + } + + 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); + } + + void setBlockSize(int blockSize) { + if (running) { + return; + } + _blockSize = blockSize; + } + + stream output; + + private: + static void _worker(SineSource* _this) { + complex_t* outBuf = new complex_t[_this->_blockSize]; + while (true) { + for (int i = 0; i < _this->_blockSize; i++) { + _this->_phase += _this->_phasorSpeed; + outBuf[i].i = sin(_this->_phase); + outBuf[i].q = cos(_this->_phase); + } + _this->_phase = fmodf(_this->_phase, 2.0f * 3.1415926535); + if (_this->output.write(outBuf, _this->_blockSize) < 0) { break; }; + } + delete[] outBuf; + } + + int _blockSize; + float _phasorSpeed; + float _phase; + long _sampleRate; + std::thread _workerThread; + bool running = false; + }; +}; \ No newline at end of file diff --git a/src/cdsp/stream.h b/src/dsp/stream.h similarity index 92% rename from src/cdsp/stream.h rename to src/dsp/stream.h index 5b363487..21daf512 100644 --- a/src/cdsp/stream.h +++ b/src/dsp/stream.h @@ -3,7 +3,9 @@ #include #include -namespace cdsp { +#define STREAM_BUF_SZ 1000000 + +namespace dsp { template class stream { public: @@ -11,20 +13,22 @@ namespace cdsp { } - stream(int size) { + stream(int maxLatency) { + size = STREAM_BUF_SZ; _buffer = new T[size]; _stopReader = false; _stopWriter = false; - this->size = size; + this->maxLatency = maxLatency; writec = 0; readc = size - 1; } - void init(int size) { + void init(int maxLatency) { + size = STREAM_BUF_SZ; _buffer = new T[size]; _stopReader = false; _stopWriter = false; - this->size = size; + this->maxLatency = maxLatency; writec = 0; readc = size - 1; } @@ -57,6 +61,7 @@ namespace cdsp { readc_mtx.unlock(); canWriteVar.notify_one(); } + return len; } int readAndSkip(T* data, int len, int skip) { @@ -95,6 +100,7 @@ namespace cdsp { readc_mtx.unlock(); canWriteVar.notify_one(); } + return len; } int waitUntilReadable() { @@ -167,7 +173,7 @@ namespace cdsp { if (_rc < writec) { writeable = (this->size + writeable); } - return writeable - 1; + return std::min(writeable - 1, maxLatency - readable(false) - 1); } void stopReader() { @@ -196,11 +202,16 @@ namespace cdsp { _stopWriter = false; } + void setMaxLatency(int maxLatency) { + this->maxLatency = maxLatency; + } + private: T* _buffer; int size; int readc; int writec; + int maxLatency; bool _stopReader; bool _stopWriter; std::mutex readc_mtx; diff --git a/src/cdsp/types.h b/src/dsp/types.h similarity index 82% rename from src/cdsp/types.h rename to src/dsp/types.h index 0ff92ca8..bc7347f9 100644 --- a/src/cdsp/types.h +++ b/src/dsp/types.h @@ -1,6 +1,6 @@ #pragma once -namespace cdsp { +namespace dsp { struct complex_t { float q; float i; diff --git a/src/dsp/vfo.h b/src/dsp/vfo.h new file mode 100644 index 00000000..9b9c9ef5 --- /dev/null +++ b/src/dsp/vfo.h @@ -0,0 +1,158 @@ +#pragma once +#include +#include +#include +#include + +namespace dsp { + class VFO { + public: + VFO() { + + } + + void init(stream* in, float inputSampleRate, float outputSampleRate, float bandWidth, float offset, int blockSize) { + _input = in; + _outputSampleRate = outputSampleRate; + _inputSampleRate = inputSampleRate; + int _gcd = std::gcd((int)inputSampleRate, (int)outputSampleRate); + _interp = outputSampleRate / _gcd; + _decim = inputSampleRate / _gcd; + _bandWidth = bandWidth; + _blockSize = blockSize; + output = &decim.output; + + dsp::BlackmanWindow(_taps, inputSampleRate * _interp, bandWidth / 2.0f, bandWidth / 2.0f); + + lo.init(offset, inputSampleRate, blockSize); + mixer.init(in, &lo.output, blockSize); + interp.init(&mixer.output, _interp, blockSize); + if (_interp == 1) { + decim.init(&mixer.output, _taps, blockSize, _decim); + } + else { + decim.init(&interp.output, _taps, blockSize * _interp, _decim); + } + } + + void start() { + lo.start(); + mixer.start(); + if (_interp != 1) { + printf("UH OH INTERPOLATOR STARTED :/\n"); + interp.start(); + } + decim.start(); + } + + void stop() { + lo.stop(); + mixer.stop(); + interp.stop(); + decim.stop(); + } + + void setInputSampleRate(float inputSampleRate, int blockSize = -1) { + interp.stop(); + decim.stop(); + + _inputSampleRate = inputSampleRate; + int _gcd = std::gcd((int)inputSampleRate, (int)_outputSampleRate); + _interp = _outputSampleRate / _gcd; + _decim = inputSampleRate / _gcd; + + dsp::BlackmanWindow(_taps, inputSampleRate * _interp, _bandWidth / 2.0f, _bandWidth / 2.0f); + + interp.setInterpolation(_interp); + decim.setDecimation(_decim); + if (blockSize > 0) { + lo.stop(); + mixer.stop(); + _blockSize = blockSize; + lo.setBlockSize(_blockSize); + mixer.setBlockSize(_blockSize); + interp.setBlockSize(_blockSize); + lo.start(); + mixer.start(); + } + decim.setBlockSize(_blockSize * _interp); + + if (_interp == 1) { + decim.setInput(&mixer.output); + } + else { + decim.setInput(&interp.output); + interp.start(); + } + decim.start(); + } + + void setOutputSampleRate(float outputSampleRate, float bandWidth = -1) { + interp.stop(); + decim.stop(); + + if (bandWidth > 0) { + _bandWidth = bandWidth; + } + + _outputSampleRate = outputSampleRate; + int _gcd = std::gcd((int)_inputSampleRate, (int)outputSampleRate); + _interp = outputSampleRate / _gcd; + _decim = _inputSampleRate / _gcd; + + dsp::BlackmanWindow(_taps, _inputSampleRate * _interp, _bandWidth / 2.0f, _bandWidth / 2.0f); + decim.setTaps(_taps); + + interp.setInterpolation(_interp); + decim.setDecimation(_decim); + decim.setBlockSize(_blockSize * _interp); + + if (_interp == 1) { + decim.setInput(&mixer.output); + } + else { + decim.setInput(&interp.output); + interp.start(); + } + decim.start(); + } + + void setBandwidth(float bandWidth) { + decim.stop(); + dsp::BlackmanWindow(_taps, _inputSampleRate * _interp, _bandWidth / 2.0f, _bandWidth / 2.0f); + decim.setTaps(_taps); + decim.start(); + } + + void setOffset(float offset) { + lo.setFrequency(-offset); + } + + void setBlockSize(int blockSize) { + stop(); + _blockSize = blockSize; + lo.setBlockSize(_blockSize); + mixer.setBlockSize(_blockSize); + interp.setBlockSize(_blockSize); + decim.setBlockSize(_blockSize * _interp); + start(); + } + + stream* output; + + private: + SineSource lo; + Multiplier mixer; + Interpolator interp; + DecimatingFIRFilter decim; + stream* _input; + + std::vector _taps; + int _interp; + int _decim; + float _outputSampleRate; + float _inputSampleRate; + float _bandWidth; + float _blockSize; + }; +}; \ No newline at end of file diff --git a/src/cdsp/audio.h b/src/io/audio.h similarity index 91% rename from src/cdsp/audio.h rename to src/io/audio.h index bc82ee1b..25b4c637 100644 --- a/src/cdsp/audio.h +++ b/src/io/audio.h @@ -1,18 +1,18 @@ #pragma once #include -#include -#include +#include +#include #include #include -namespace cdsp { +namespace io { class AudioSink { public: AudioSink() { } - AudioSink(stream* in, int bufferSize) { + AudioSink(dsp::stream* in, int bufferSize) { _bufferSize = bufferSize; _input = in; buffer = new float[_bufferSize * 2]; @@ -20,7 +20,7 @@ namespace cdsp { Pa_Initialize(); } - void init(stream* in, int bufferSize) { + void init(dsp::stream* in, int bufferSize) { _bufferSize = bufferSize; _input = in; buffer = new float[_bufferSize * 2]; @@ -67,7 +67,7 @@ namespace cdsp { } int _bufferSize; - stream* _input; + dsp::stream* _input; float* buffer; float _volume; PaStream *stream; diff --git a/src/io/soapy.h b/src/io/soapy.h new file mode 100644 index 00000000..9e7f6216 --- /dev/null +++ b/src/io/soapy.h @@ -0,0 +1,106 @@ +#include +#include +#include +#include +#include + +namespace io { + class SoapyWrapper { + public: + SoapyWrapper() { + output.init(64000); + refresh(); + setDevice(devList[0]); + } + + void start() { + if (running) { + return; + } + dev = SoapySDR::Device::make(args); + _stream = dev->setupStream(SOAPY_SDR_RX, "CF32"); + dev->activateStream(_stream); + running = true; + _workerThread = std::thread(_worker, this); + } + + void stop() { + if (!running) { + return; + } + running = false; + dev->deactivateStream(_stream); + dev->closeStream(_stream); + _workerThread.join(); + SoapySDR::Device::unmake(dev); + } + + void refresh() { + if (running) { + return; + } + + devList = SoapySDR::Device::enumerate(); + txtDevList = ""; + for (int i = 0; i < devList.size(); i++) { + txtDevList += devList[i]["label"]; + txtDevList += '\0'; + } + } + + void setDevice(SoapySDR::Kwargs devArgs) { + if (running) { + return; + } + args = devArgs; + dev = SoapySDR::Device::make(devArgs); + txtSampleRateList = ""; + sampleRates = dev->listSampleRates(SOAPY_SDR_RX, 0); + for (int i = 0; i < sampleRates.size(); i++) { + txtSampleRateList += std::to_string((int)sampleRates[i]); + txtSampleRateList += '\0'; + } + } + + void setSampleRate(float sampleRate) { + if (running) { + return; + } + dev->setSampleRate(SOAPY_SDR_RX, 0, sampleRate); + } + + void setFrequency(float freq) { + dev->setFrequency(SOAPY_SDR_RX, 0, freq); + } + + bool isRunning() { + return running; + } + + SoapySDR::KwargsList devList; + std::string txtDevList; + std::vector sampleRates; + std::string txtSampleRateList; + + dsp::stream output; + + private: + static void _worker(SoapyWrapper* _this) { + dsp::complex_t* buf = new dsp::complex_t[32000]; + int flags = 0; + long long timeMs = 0; + while (_this->running) { + _this->dev->readStream(_this->_stream, (void**)&buf, 32000, flags, timeMs); + _this->output.write(buf, 32000); + } + printf("Read worker terminated\n"); + delete[] buf; + } + + SoapySDR::Kwargs args; + SoapySDR::Device* dev; + SoapySDR::Stream* _stream; + std::thread _workerThread; + bool running = false; + }; +}; \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index cb876fc8..cd113792 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,6 +7,7 @@ #include #include + #ifdef _WIN32 #include #endif diff --git a/src/main_window.cpp b/src/main_window.cpp index a7a8190c..7e7f8c6b 100644 --- a/src/main_window.cpp +++ b/src/main_window.cpp @@ -1,22 +1,20 @@ #include #include -#include -#include -#include -#include -#include +#include +#include +#include #include #include -#include -#include +#include +#include #include #include #include +#include std::thread worker; std::mutex fft_mtx; ImGui::WaterFall wtf; -hackrf_device* dev; fftwf_complex *fft_in, *fft_out; fftwf_plan p; float* tempData; @@ -25,11 +23,13 @@ int fftSize = 8192 * 8; bool dcbias = true; -cdsp::HackRFSource src; +io::SoapyWrapper soapy; + +//dsp::HackRFSource src; SignalPath sigPath; std::vector _data; std::vector fftTaps; -void fftHandler(cdsp::complex_t* samples) { +void fftHandler(dsp::complex_t* samples) { fftwf_execute(p); int half = fftSize / 2; @@ -61,29 +61,34 @@ void windowInit() { printf("Starting DSP Thread!\n"); - hackrf_init(); - hackrf_device_list_t* list = hackrf_device_list(); + // 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); + // 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); - src.init(dev, 64000); + //src.init(dev, 64000); - sigPath.init(sampleRate, 20, fftSize, &src.output, (cdsp::complex_t*)fft_in, fftHandler); + sigPath.init(sampleRate, 20, fftSize, &soapy.output, (dsp::complex_t*)fft_in, fftHandler); sigPath.start(); } -int Current = 0; +int devId = 0; +int _devId = -1; + +int srId = 0; +int _srId = -1; + bool showExample = false; int freq = 90500; @@ -102,7 +107,8 @@ void drawWindow() { if (freq != _freq) { _freq = freq; wtf.centerFrequency = freq * 1000; - hackrf_set_freq(dev, freq * 1000); + soapy.setFrequency(freq * 1000); + //hackrf_set_freq(dev, freq * 1000); } if (vfoFreq != lastVfoFreq) { @@ -115,6 +121,15 @@ void drawWindow() { sigPath.setVolume(volume); } + if (devId != _devId) { + _devId = devId; + soapy.setDevice(soapy.devList[devId]); + } + + if (srId != _srId) { + soapy.setSampleRate(soapy.sampleRates[srId]); + } + if (ImGui::BeginMenuBar()) { @@ -148,16 +163,22 @@ void drawWindow() { ImGui::BeginChild("Left Column"); if (ImGui::CollapsingHeader("Source")) { - - //ImGui::Combo("Source", &Current, "HackRF One\0RTL-SDR"); - ImGui::SliderFloat("Volume", &volume, 0.0f, 1.0f); + ImGui::PushItemWidth(ImGui::GetWindowSize().x); + ImGui::Combo("##_0_", &devId, soapy.txtDevList.c_str()); + ImGui::Combo("##_1_", &srId, soapy.txtSampleRateList.c_str()); + + ImGui::SliderFloat("##_2_", &volume, 0.0f, 1.0f, ""); if (ImGui::Button("Start") && !state) { state = true; - src.start(); + soapy.start(); } + ImGui::SameLine(); if (ImGui::Button("Stop") && state) { state = false; - src.stop(); + soapy.stop(); + } + if (ImGui::Button("Refresh")) { + soapy.refresh(); } } diff --git a/src/signal_path.cpp b/src/signal_path.cpp index 747d7b4c..aa933d4e 100644 --- a/src/signal_path.cpp +++ b/src/signal_path.cpp @@ -4,26 +4,17 @@ 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*)) { +void SignalPath::init(uint64_t sampleRate, int fftRate, int fftSize, dsp::stream* input, dsp::complex_t* fftBuffer, void fftHandler(dsp::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); @@ -31,13 +22,15 @@ void SignalPath::init(uint64_t sampleRate, int fftRate, int fftSize, cdsp::strea 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); + mainVFO.init(&split.output_b, sampleRate, 200000, 200000, 0, 32000); demod.init(mainVFO.output, 100000, 200000, 800); - amDemod.init(mainVFO.output, 800); + amDemod.init(mainVFO.output, 50); - audioResamp.init(&demod.output, 200000, 40000, 800, 20000); + audioResamp.init(&demod.output, 200000, 40000, 20000, 800); audio.init(audioResamp.output, 160); + + ns.init(mainVFO.output, 800); } void SignalPath::setVFOFrequency(long frequency) { @@ -69,16 +62,16 @@ void SignalPath::setDemodulator(int demId) { // Set input of the audio resampler if (demId == DEMOD_FM) { printf("Starting FM demodulator\n"); - mainVFO.setBandwidth(200000); + mainVFO.setOutputSampleRate(200000, 200000); audioResamp.setInput(&demod.output); - audioResamp.setInputSampleRate(200000); + audioResamp.setInputSampleRate(200000, 800); demod.start(); } else if (demId == DEMOD_AM) { printf("Starting AM demodulator\n"); - mainVFO.setBandwidth(12000); + mainVFO.setOutputSampleRate(12500, 12500); audioResamp.setInput(&amDemod.output); - audioResamp.setInputSampleRate(12000); + audioResamp.setInputSampleRate(12500, 50); amDemod.start(); } @@ -94,6 +87,7 @@ void SignalPath::start() { mainVFO.start(); demod.start(); + //ns.start(); audioResamp.start(); audio.start(); diff --git a/src/signal_path.h b/src/signal_path.h index aa79e40e..01e77f72 100644 --- a/src/signal_path.h +++ b/src/signal_path.h @@ -1,16 +1,19 @@ #pragma once -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include class SignalPath { public: SignalPath(); - void init(uint64_t sampleRate, int fftRate, int fftSize, cdsp::stream* input, cdsp::complex_t* fftBuffer, void fftHandler(cdsp::complex_t*)); + void init(uint64_t sampleRate, int fftRate, int fftSize, dsp::stream* input, dsp::complex_t* fftBuffer, void fftHandler(dsp::complex_t*)); void start(); void setSampleRate(float sampleRate); void setDCBiasCorrection(bool enabled); @@ -28,22 +31,26 @@ public: }; private: - cdsp::DCBiasRemover dcBiasRemover; - cdsp::Splitter split; + dsp::DCBiasRemover dcBiasRemover; + dsp::Splitter split; // FFT - cdsp::BlockDecimator fftBlockDec; - cdsp::HandlerSink fftHandlerSink; + dsp::BlockDecimator fftBlockDec; + dsp::HandlerSink fftHandlerSink; // VFO - VFO mainVFO; + dsp::VFO mainVFO; - cdsp::FMDemodulator demod; - cdsp::AMDemodulator amDemod; + // Demodulators + dsp::FMDemodulator demod; + dsp::AMDemodulator amDemod; - //cdsp::FloatDecimatingFIRFilter audioDecFilt; - cdsp::FractionalResampler audioResamp; - cdsp::AudioSink audio; + // Audio output + dsp::FloatResampler audioResamp; + io::AudioSink audio; + + // DEBUG + dsp::NullSink ns; float sampleRate; float fftRate; diff --git a/src/vfo.cpp b/src/vfo.cpp deleted file mode 100644 index 5611fdcd..00000000 --- a/src/vfo.cpp +++ /dev/null @@ -1,104 +0,0 @@ -#include -#include - -VFO::VFO() { - -} - -void VFO::init(cdsp::stream* input, float offset, float inputSampleRate, float bandWidth, int bufferSize) { - _input = input; - outputSampleRate = ceilf(bandWidth / OUTPUT_SR_ROUND) * OUTPUT_SR_ROUND; - _inputSampleRate = inputSampleRate; - int _gcd = std::gcd((int)inputSampleRate, (int)outputSampleRate); - _interp = outputSampleRate / _gcd; - _decim = inputSampleRate / _gcd; - _bandWidth = bandWidth; - _bufferSize = bufferSize; - lo.init(offset, inputSampleRate, bufferSize); - mixer.init(&lo.output, input, bufferSize); - interp.init(&mixer.output, _interp, bufferSize); - - BlackmanWindow(decimTaps, inputSampleRate * _interp, bandWidth / 2.0f, bandWidth / 2.0f); - - if (_interp != 1) { - printf("Interpolation needed\n"); - decFir.init(&interp.output, decimTaps, bufferSize * _interp, _decim); - } - else { - decFir.init(&mixer.output, decimTaps, bufferSize, _decim); - printf("Interpolation NOT needed: %d %d %d\n", bufferSize / _decim, _decim, _interp); - } - - output = &decFir.output; -} - - -void VFO::start() { - lo.start(); - mixer.start(); - if (_interp != 1) { - interp.start(); - } - decFir.start(); -} - -void VFO::stop() { - // TODO: Stop LO - mixer.stop(); - interp.stop(); - decFir.stop(); -} - - -void VFO::setOffset(float freq) { - lo.setFrequency(-freq); -} - -void VFO::setBandwidth(float bandWidth) { - if (bandWidth == _bandWidth) { - return; - } - outputSampleRate = ceilf(bandWidth / OUTPUT_SR_ROUND) * OUTPUT_SR_ROUND; - int _gcd = std::gcd((int)_inputSampleRate, (int)outputSampleRate); - int interpol = outputSampleRate / _gcd; - int decim = _inputSampleRate / _gcd; - _bandWidth = bandWidth; - - BlackmanWindow(decimTaps, _inputSampleRate * _interp, bandWidth / 2, bandWidth); - - decFir.stop(); - decFir.setTaps(decimTaps); - decFir.setDecimation(decim); - - if (interpol != _interp) { - interp.stop(); - if (interpol == 1) { - decFir.setBufferSize(_bufferSize); - decFir.setInput(&mixer.output); - } - else if (_interp == 1) { - decFir.setInput(&interp.output); - decFir.setBufferSize(_bufferSize * _interp); - interp.setInterpolation(interpol); - interp.start(); - } - else { - decFir.setBufferSize(_bufferSize * _interp); - interp.setInterpolation(interpol); - interp.start(); - } - } - - _interp = interpol; - _decim = decim; - - decFir.start(); -} - -void VFO::setSampleRate(int sampleRate) { - -} - -int VFO::getOutputSampleRate() { - return outputSampleRate; -} \ No newline at end of file diff --git a/src/vfo.h b/src/vfo.h deleted file mode 100644 index 9f9c9cd6..00000000 --- a/src/vfo.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once -#include -#include -#include -#include - -// Round up to next 5KHz multiple frequency -#define OUTPUT_SR_ROUND 5000.0f - -class VFO { -public: - VFO(); - void init(cdsp::stream* input, float offset, float sampleRate, float bandWidth, int bufferSize); - - void start(); - void stop(); - - void setOffset(float freq); - void setBandwidth(float bandwidth); - void setSampleRate(int sampleRate); - - int getOutputSampleRate(); - - cdsp::stream* output; - -private: - cdsp::ComplexSineSource lo; - cdsp::Multiplier mixer; - cdsp::IQInterpolator interp; - cdsp::DecimatingFIRFilter decFir; - - std::vector decimTaps; - - int _interp; - int _decim; - float _inputSampleRate; - float _outputSampleRate; - float _bandWidth; - int _bufferSize; - int outputSampleRate; - - cdsp::stream* _input; -}; \ No newline at end of file