mirror of
https://github.com/AlexandreRouma/SDRPlusPlus.git
synced 2024-12-24 01:48:28 +01:00
rewrote DSP
This commit is contained in:
parent
61ba7f1420
commit
b78c2cf415
@ -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")
|
||||
|
BIN
lib/SoapySDR.lib
Normal file
BIN
lib/SoapySDR.lib
Normal file
Binary file not shown.
@ -1,84 +0,0 @@
|
||||
#ifdef _MSC_VER
|
||||
#include <tmmintrin.h>
|
||||
#else
|
||||
#include <x86intrin.h>
|
||||
#endif
|
||||
|
||||
#include <cdsp/types.h>
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
@ -1,127 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// Code by: Stellaris
|
||||
|
||||
#include <cmath>
|
||||
#include <complex>
|
||||
#include <cassert>
|
||||
#include <cdsp/types.h>
|
||||
|
||||
#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();
|
104
src/cdsp/file.h
104
src/cdsp/file.h
@ -1,104 +0,0 @@
|
||||
#pragma once
|
||||
#include <thread>
|
||||
#include <cdsp/stream.h>
|
||||
#include <cdsp/types.h>
|
||||
#include <fstream>
|
||||
|
||||
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<float> 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<float>* 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<float>* 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<float>* _input;
|
||||
std::thread _workerThread;
|
||||
};
|
||||
};
|
@ -1,156 +0,0 @@
|
||||
#pragma once
|
||||
#include <thread>
|
||||
#include <cdsp/stream.h>
|
||||
#include <cdsp/types.h>
|
||||
|
||||
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<float> 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<float> 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<complex_t> 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;
|
||||
};
|
||||
};
|
@ -1,194 +0,0 @@
|
||||
#pragma once
|
||||
#include <thread>
|
||||
#include <cdsp/stream.h>
|
||||
#include <cdsp/types.h>
|
||||
#include <fstream>
|
||||
#include <hackrf.h>
|
||||
#include <Windows.h>
|
||||
|
||||
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<complex_t>* in, int bufferSize) : output(bufferSize * 2) {
|
||||
_input = in;
|
||||
_bufferSize = bufferSize;
|
||||
}
|
||||
|
||||
void init(stream<complex_t>* in, int bufferSize) {
|
||||
output.init(bufferSize * 2);
|
||||
_input = in;
|
||||
_bufferSize = bufferSize;
|
||||
}
|
||||
|
||||
stream<hackrf_sample_t> 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<complex_t>* _input;
|
||||
std::thread _workerThread;
|
||||
};
|
||||
|
||||
class HackRF2Complex {
|
||||
public:
|
||||
HackRF2Complex() {
|
||||
|
||||
}
|
||||
|
||||
HackRF2Complex(stream<complex_t>* out, int bufferSize) : input(bufferSize * 2) {
|
||||
_output = out;
|
||||
_bufferSize = bufferSize;
|
||||
}
|
||||
|
||||
void init(stream<complex_t>* out, int bufferSize) {
|
||||
input.init(bufferSize * 2);
|
||||
_output = out;
|
||||
_bufferSize = bufferSize;
|
||||
}
|
||||
|
||||
void start() {
|
||||
_workerThread = std::thread(_worker, this);
|
||||
}
|
||||
|
||||
stream<hackrf_sample_t> 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<complex_t>* _output;
|
||||
std::thread _workerThread;
|
||||
};
|
||||
|
||||
class HackRFSink {
|
||||
public:
|
||||
HackRFSink() {
|
||||
|
||||
}
|
||||
|
||||
HackRFSink(hackrf_device* dev, int bufferSize, stream<complex_t>* input) : gen(input, bufferSize) {
|
||||
_input = input;
|
||||
_dev = dev;
|
||||
gen.start();
|
||||
}
|
||||
|
||||
void init(hackrf_device* dev, int bufferSize, stream<complex_t>* 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<complex_t>* _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<complex_t> 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;
|
||||
};
|
||||
};
|
@ -1,70 +0,0 @@
|
||||
#pragma once
|
||||
#include <thread>
|
||||
#include <cdsp/stream.h>
|
||||
#include <cdsp/types.h>
|
||||
#include <cdsp/fast_math.h>
|
||||
|
||||
namespace cdsp {
|
||||
class Multiplier {
|
||||
public:
|
||||
Multiplier() {
|
||||
|
||||
}
|
||||
|
||||
Multiplier(stream<complex_t>* a, stream<complex_t>* b, int bufferSize) : output(bufferSize * 2) {
|
||||
_a = a;
|
||||
_b = b;
|
||||
_bufferSize = bufferSize;
|
||||
}
|
||||
|
||||
void init(stream<complex_t>* a, stream<complex_t>* b, int bufferSize) {
|
||||
output.init(bufferSize * 2);
|
||||
_a = a;
|
||||
_b = b;
|
||||
_bufferSize = bufferSize;
|
||||
}
|
||||
|
||||
void start() {
|
||||
if (running) {
|
||||
return;
|
||||
}
|
||||
running = true;
|
||||
_workerThread = std::thread(_worker, this);
|
||||
}
|
||||
|
||||
void stop() {
|
||||
if (!running) {
|
||||
return;
|
||||
}
|
||||
_a->stopReader();
|
||||
_b->stopReader();
|
||||
output.stopWriter();
|
||||
_workerThread.join();
|
||||
running = false;
|
||||
_a->clearReadStop();
|
||||
_b->clearReadStop();
|
||||
output.clearWriteStop();
|
||||
}
|
||||
|
||||
stream<complex_t> output;
|
||||
|
||||
private:
|
||||
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<complex_t>* _a;
|
||||
stream<complex_t>* _b;
|
||||
int _bufferSize;
|
||||
bool running = false;
|
||||
std::thread _workerThread;
|
||||
};
|
||||
};
|
@ -1,57 +0,0 @@
|
||||
#pragma once
|
||||
#include <thread>
|
||||
#include <cdsp/stream.h>
|
||||
#include <cdsp/types.h>
|
||||
#include <cmath>
|
||||
|
||||
namespace cdsp {
|
||||
class FMModulator {
|
||||
public:
|
||||
FMModulator() {
|
||||
|
||||
}
|
||||
|
||||
FMModulator(stream<float>* 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<float>* in, float deviation, long sampleRate, int bufferSize) {
|
||||
output.init(bufferSize * 2);
|
||||
_input = in;
|
||||
_bufferSize = bufferSize;
|
||||
_phase = 0.0f;
|
||||
_phasorSpeed = (2 * 3.1415926535) / (sampleRate / deviation);
|
||||
}
|
||||
|
||||
void start() {
|
||||
_workerThread = std::thread(_worker, this);
|
||||
}
|
||||
|
||||
stream<complex_t> 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<float>* _input;
|
||||
int _bufferSize;
|
||||
float _phase;
|
||||
float _phasorSpeed;
|
||||
std::thread _workerThread;
|
||||
};
|
||||
};
|
@ -1,317 +0,0 @@
|
||||
#pragma once
|
||||
#include <thread>
|
||||
#include <cdsp/stream.h>
|
||||
#include <cdsp/types.h>
|
||||
#include <cdsp/filter.h>
|
||||
#include <numeric>
|
||||
|
||||
|
||||
namespace cdsp {
|
||||
class Interpolator {
|
||||
public:
|
||||
Interpolator() {
|
||||
|
||||
}
|
||||
|
||||
Interpolator(stream<float>* in, float interpolation, int bufferSize) : output(bufferSize * 2) {
|
||||
_input = in;
|
||||
_interpolation = interpolation;
|
||||
_bufferSize = bufferSize;
|
||||
running = false;
|
||||
}
|
||||
|
||||
void init(stream<float>* in, float interpolation, int bufferSize) {
|
||||
output.init(bufferSize * 2);
|
||||
_input = in;
|
||||
_interpolation = interpolation;
|
||||
_bufferSize = bufferSize;
|
||||
running = false;
|
||||
}
|
||||
|
||||
void start() {
|
||||
if (running) {
|
||||
return;
|
||||
}
|
||||
running = true;
|
||||
_workerThread = std::thread(_worker, this);
|
||||
}
|
||||
|
||||
void stop() {
|
||||
if (!running) {
|
||||
return;
|
||||
}
|
||||
running = false;
|
||||
_input->stopReader();
|
||||
output.stopWriter();
|
||||
_workerThread.join();
|
||||
_input->clearReadStop();
|
||||
output.clearWriteStop();
|
||||
}
|
||||
|
||||
void setInterpolation(float interpolation) {
|
||||
if (running) {
|
||||
return;
|
||||
}
|
||||
_interpolation = interpolation;
|
||||
}
|
||||
|
||||
void setInput(stream<float>* in) {
|
||||
if (running) {
|
||||
return;
|
||||
}
|
||||
_input = in;
|
||||
}
|
||||
|
||||
stream<float> output;
|
||||
|
||||
private:
|
||||
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<float>* _input;
|
||||
int _bufferSize;
|
||||
float _interpolation;
|
||||
std::thread _workerThread;
|
||||
bool running;
|
||||
};
|
||||
|
||||
|
||||
class IQInterpolator {
|
||||
public:
|
||||
IQInterpolator() {
|
||||
|
||||
}
|
||||
|
||||
IQInterpolator(stream<complex_t>* in, float interpolation, int bufferSize) : output(bufferSize * 2) {
|
||||
_input = in;
|
||||
_interpolation = interpolation;
|
||||
_bufferSize = bufferSize;
|
||||
running = false;
|
||||
}
|
||||
|
||||
void init(stream<complex_t>* in, float interpolation, int bufferSize) {
|
||||
output.init(bufferSize * 2);
|
||||
_input = in;
|
||||
_interpolation = interpolation;
|
||||
_bufferSize = bufferSize;
|
||||
running = false;
|
||||
}
|
||||
|
||||
void start() {
|
||||
if (running) {
|
||||
return;
|
||||
}
|
||||
_workerThread = std::thread(_worker, this);
|
||||
running = true;
|
||||
}
|
||||
|
||||
void stop() {
|
||||
if (!running) {
|
||||
return;
|
||||
}
|
||||
_input->stopReader();
|
||||
output.stopWriter();
|
||||
_workerThread.join();
|
||||
_input->clearReadStop();
|
||||
output.clearWriteStop();
|
||||
running = false;
|
||||
}
|
||||
|
||||
void setInterpolation(float interpolation) {
|
||||
if (running) {
|
||||
return;
|
||||
}
|
||||
_interpolation = interpolation;
|
||||
}
|
||||
|
||||
stream<complex_t> output;
|
||||
|
||||
private:
|
||||
static void _worker(IQInterpolator* _this) {
|
||||
complex_t* inBuf = new complex_t[_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<complex_t>* _input;
|
||||
int _bufferSize;
|
||||
float _interpolation;
|
||||
std::thread _workerThread;
|
||||
bool running;
|
||||
};
|
||||
|
||||
class BlockDecimator {
|
||||
public:
|
||||
BlockDecimator() {
|
||||
|
||||
}
|
||||
|
||||
BlockDecimator(stream<complex_t>* in, int skip, int bufferSize) : output(bufferSize * 2) {
|
||||
_input = in;
|
||||
_skip = skip;
|
||||
_bufferSize = bufferSize;
|
||||
}
|
||||
|
||||
void init(stream<complex_t>* in, int skip, int bufferSize) {
|
||||
output.init(bufferSize * 2);
|
||||
_input = in;
|
||||
_skip = skip;
|
||||
_bufferSize = bufferSize;
|
||||
}
|
||||
|
||||
void start() {
|
||||
_workerThread = std::thread(_worker, this);
|
||||
}
|
||||
|
||||
stream<complex_t> 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<complex_t>* _input;
|
||||
int _bufferSize;
|
||||
int _skip;
|
||||
std::thread _workerThread;
|
||||
};
|
||||
|
||||
class FractionalResampler {
|
||||
public:
|
||||
FractionalResampler() {
|
||||
|
||||
}
|
||||
|
||||
void init(stream<float>* input, float inputSampleRate, float outputSampleRate, int bufferSize, float customCutoff = INFINITY) {
|
||||
_input = input;
|
||||
float lowestFreq = std::min<float>(inputSampleRate, outputSampleRate);
|
||||
int _gcd = std::gcd((int)inputSampleRate, (int)outputSampleRate);
|
||||
_interp = outputSampleRate / _gcd;
|
||||
_decim = inputSampleRate / _gcd;
|
||||
_inputSampleRate = inputSampleRate;
|
||||
_outputSampleRate = outputSampleRate;
|
||||
running = false;
|
||||
|
||||
interpolator.init(input, _interp, bufferSize);
|
||||
|
||||
BlackmanWindow(decimTaps, inputSampleRate * _interp, lowestFreq / 2.0f, lowestFreq / 2.0f);
|
||||
|
||||
if (_interp != 1) {
|
||||
printf("FR Interpolation needed\n");
|
||||
decimator.init(&interpolator.output, decimTaps, bufferSize * _interp, _decim);
|
||||
}
|
||||
else {
|
||||
decimator.init(input, decimTaps, bufferSize, _decim);
|
||||
printf("FR Interpolation NOT needed: %d %d %d\n", bufferSize / _decim, _decim, _interp);
|
||||
}
|
||||
|
||||
output = &decimator.output;
|
||||
}
|
||||
|
||||
void start() {
|
||||
if (_interp != 1) {
|
||||
interpolator.start();
|
||||
}
|
||||
decimator.start();
|
||||
running = true;
|
||||
}
|
||||
|
||||
void stop() {
|
||||
interpolator.stop();
|
||||
decimator.stop();
|
||||
running = false;
|
||||
}
|
||||
|
||||
void setInputSampleRate(float inputSampleRate) {
|
||||
if (running) {
|
||||
return;
|
||||
}
|
||||
float lowestFreq = std::min<float>(inputSampleRate, _outputSampleRate);
|
||||
int _gcd = std::gcd((int)inputSampleRate, (int)_outputSampleRate);
|
||||
_interp = _outputSampleRate / _gcd;
|
||||
_decim = inputSampleRate / _gcd;
|
||||
|
||||
// TODO: Add checks from VFO to remove the need to stop both
|
||||
interpolator.setInterpolation(_interp);
|
||||
decimator.setDecimation(_decim);
|
||||
if (_interp != 1) {
|
||||
decimator.setInput(&interpolator.output);
|
||||
}
|
||||
else {
|
||||
decimator.setInput(_input);
|
||||
}
|
||||
}
|
||||
|
||||
void setOutputSampleRate(float outputSampleRate) {
|
||||
if (running) {
|
||||
return;
|
||||
}
|
||||
float lowestFreq = std::min<float>(_inputSampleRate, outputSampleRate);
|
||||
int _gcd = std::gcd((int)_inputSampleRate, (int)outputSampleRate);
|
||||
_interp = outputSampleRate / _gcd;
|
||||
_decim = _inputSampleRate / _gcd;
|
||||
|
||||
// TODO: Add checks from VFO to remove the need to stop both
|
||||
interpolator.setInterpolation(_interp);
|
||||
decimator.setDecimation(_decim);
|
||||
if (_interp != 1) {
|
||||
decimator.setInput(&interpolator.output);
|
||||
}
|
||||
else {
|
||||
decimator.setInput(_input);
|
||||
}
|
||||
}
|
||||
|
||||
void setInput(stream<float>* input) {
|
||||
if (running) {
|
||||
return;
|
||||
}
|
||||
_input = input;
|
||||
if (_interp != 1) {
|
||||
interpolator.setInput(input);
|
||||
decimator.setInput(&interpolator.output);
|
||||
}
|
||||
else {
|
||||
decimator.setInput(input);
|
||||
}
|
||||
}
|
||||
|
||||
stream<float>* output;
|
||||
|
||||
private:
|
||||
Interpolator interpolator;
|
||||
FloatDecimatingFIRFilter decimator;
|
||||
std::vector<float> decimTaps;
|
||||
stream<float>* _input;
|
||||
int _interp;
|
||||
int _decim;
|
||||
int _bufferSize;
|
||||
float _inputSampleRate;
|
||||
float _outputSampleRate;
|
||||
bool running;
|
||||
};
|
||||
};
|
63
src/dsp/correction.h
Normal file
63
src/dsp/correction.h
Normal file
@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
#include <thread>
|
||||
#include <dsp/stream.h>
|
||||
#include <dsp/types.h>
|
||||
#include <vector>
|
||||
|
||||
namespace dsp {
|
||||
class DCBiasRemover {
|
||||
public:
|
||||
DCBiasRemover() {
|
||||
|
||||
}
|
||||
|
||||
DCBiasRemover(stream<complex_t>* input, int bufferSize) : output(bufferSize * 2) {
|
||||
_in = input;
|
||||
_bufferSize = bufferSize;
|
||||
bypass = false;
|
||||
}
|
||||
|
||||
void init(stream<complex_t>* input, int bufferSize) {
|
||||
output.init(bufferSize * 2);
|
||||
_in = input;
|
||||
_bufferSize = bufferSize;
|
||||
bypass = false;
|
||||
}
|
||||
|
||||
void start() {
|
||||
_workerThread = std::thread(_worker, this);
|
||||
}
|
||||
|
||||
stream<complex_t> 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<complex_t>* _in;
|
||||
int _bufferSize;
|
||||
std::thread _workerThread;
|
||||
};
|
||||
};
|
@ -1,13 +1,17 @@
|
||||
#pragma once
|
||||
#include <thread>
|
||||
#include <cdsp/stream.h>
|
||||
#include <cdsp/types.h>
|
||||
#include <dsp/stream.h>
|
||||
#include <dsp/types.h>
|
||||
|
||||
/*
|
||||
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<complex_t>* in, float deviation, long sampleRate, int bufferSize) : output(bufferSize * 2) {
|
||||
FMDemodulator(stream<complex_t>* 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<complex_t>* in, float deviation, long sampleRate, int bufferSize) {
|
||||
output.init(bufferSize * 2);
|
||||
void init(stream<complex_t>* 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<float> 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<complex_t>* _input;
|
||||
bool running;
|
||||
int _bufferSize;
|
||||
int _blockSize;
|
||||
float _phase;
|
||||
float _phasorSpeed;
|
||||
std::thread _workerThread;
|
||||
@ -107,17 +119,17 @@ namespace cdsp {
|
||||
|
||||
}
|
||||
|
||||
AMDemodulator(stream<complex_t>* in, int bufferSize) : output(bufferSize * 2) {
|
||||
AMDemodulator(stream<complex_t>* in, int blockSize) : output(blockSize * 2) {
|
||||
running = false;
|
||||
_input = in;
|
||||
_bufferSize = bufferSize;
|
||||
_blockSize = blockSize;
|
||||
}
|
||||
|
||||
void init(stream<complex_t>* in, int bufferSize) {
|
||||
output.init(bufferSize * 2);
|
||||
void init(stream<complex_t>* 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<float> 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<complex_t>* _input;
|
||||
bool running;
|
||||
int _bufferSize;
|
||||
int _blockSize;
|
||||
std::thread _workerThread;
|
||||
};
|
||||
};
|
@ -1,41 +1,40 @@
|
||||
#pragma once
|
||||
#include <thread>
|
||||
#include <cdsp/stream.h>
|
||||
#include <cdsp/types.h>
|
||||
#include <dsp/stream.h>
|
||||
#include <dsp/types.h>
|
||||
#include <vector>
|
||||
#include <dsp/math.h>
|
||||
|
||||
#define GET_FROM_RIGHT_BUF(buffer, delayLine, delayLineSz, n) (((n) < 0) ? delayLine[(delayLineSz) + (n)] : buffer[(n)])
|
||||
#define M_PI 3.1415926535f
|
||||
|
||||
|
||||
inline void BlackmanWindow(std::vector<float>& taps, float sampleRate, float cutoff, float transWidth) {
|
||||
taps.clear();
|
||||
float fc = cutoff / sampleRate;
|
||||
int _M = 4.0f / (transWidth / sampleRate);
|
||||
if (_M % 2 == 0) { _M++; }
|
||||
float M = _M;
|
||||
float sum = 0.0f;
|
||||
for (int i = 0; i < _M; i++) {
|
||||
float val = (sin(2.0f * M_PI * fc * ((float)i - (M / 2))) / ((float)i - (M / 2))) * (0.42f - (0.5f * cos(2.0f * M_PI / M)) + (0.8f * cos(4.0f * M_PI / M)));
|
||||
taps.push_back(val);
|
||||
sum += val;
|
||||
namespace dsp {
|
||||
inline void BlackmanWindow(std::vector<float>& taps, float sampleRate, float cutoff, float transWidth) {
|
||||
taps.clear();
|
||||
float fc = cutoff / sampleRate;
|
||||
int _M = 4.0f / (transWidth / sampleRate);
|
||||
if (_M % 2 == 0) { _M++; }
|
||||
float M = _M;
|
||||
float sum = 0.0f;
|
||||
for (int i = 0; i < _M; i++) {
|
||||
float val = (sin(2.0f * M_PI * fc * ((float)i - (M / 2))) / ((float)i - (M / 2))) * (0.42f - (0.5f * cos(2.0f * M_PI / M)) + (0.8f * cos(4.0f * M_PI / M)));
|
||||
taps.push_back(val);
|
||||
sum += val;
|
||||
}
|
||||
for (int i = 0; i < M; i++) {
|
||||
taps[i] /= sum;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < M; i++) {
|
||||
taps[i] /= sum;
|
||||
}
|
||||
}
|
||||
|
||||
namespace cdsp {
|
||||
class DecimatingFIRFilter {
|
||||
public:
|
||||
DecimatingFIRFilter() {
|
||||
|
||||
}
|
||||
|
||||
DecimatingFIRFilter(stream<complex_t>* input, std::vector<float> taps, int bufferSize, float decim) : output(bufferSize * 2) {
|
||||
output.init(bufferSize * 2);
|
||||
DecimatingFIRFilter(stream<complex_t>* input, std::vector<float> 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<complex_t>* input, std::vector<float>& taps, int bufferSize, float decim) {
|
||||
output.init(bufferSize * 2);
|
||||
void init(stream<complex_t>* input, std::vector<float>& 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<complex_t> 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<complex_t>* _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<float>* input, std::vector<float> taps, int bufferSize, float decim) : output(bufferSize * 2) {
|
||||
output.init(bufferSize * 2);
|
||||
FloatDecimatingFIRFilter(stream<float>* input, std::vector<float> 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<float>* input, std::vector<float>& taps, int bufferSize, float decim) {
|
||||
output.init(bufferSize * 2);
|
||||
void init(stream<float>* input, std::vector<float>& 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<float> 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<float>* _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<complex_t>* input, int bufferSize) : output(bufferSize * 2) {
|
||||
_in = input;
|
||||
_bufferSize = bufferSize;
|
||||
bypass = false;
|
||||
}
|
||||
|
||||
void init(stream<complex_t>* input, int bufferSize) {
|
||||
output.init(bufferSize * 2);
|
||||
_in = input;
|
||||
_bufferSize = bufferSize;
|
||||
bypass = false;
|
||||
}
|
||||
|
||||
void start() {
|
||||
_workerThread = std::thread(_worker, this);
|
||||
}
|
||||
|
||||
stream<complex_t> 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<complex_t>* _in;
|
||||
int _bufferSize;
|
||||
std::thread _workerThread;
|
||||
};
|
||||
|
||||
class HandlerSink {
|
||||
public:
|
||||
HandlerSink() {
|
||||
|
||||
}
|
||||
|
||||
HandlerSink(stream<complex_t>* input, complex_t* buffer, int bufferSize, void handler(complex_t*)) {
|
||||
_in = input;
|
||||
_bufferSize = bufferSize;
|
||||
_buffer = buffer;
|
||||
_handler = handler;
|
||||
}
|
||||
|
||||
void init(stream<complex_t>* 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<complex_t>* _in;
|
||||
int _bufferSize;
|
||||
complex_t* _buffer;
|
||||
std::thread _workerThread;
|
||||
void (*_handler)(complex_t*);
|
||||
};
|
||||
|
||||
class Splitter {
|
||||
public:
|
||||
Splitter() {
|
||||
|
||||
}
|
||||
|
||||
Splitter(stream<complex_t>* input, int bufferSize) {
|
||||
_in = input;
|
||||
_bufferSize = bufferSize;
|
||||
output_a.init(bufferSize);
|
||||
output_b.init(bufferSize);
|
||||
}
|
||||
|
||||
void init(stream<complex_t>* input, int bufferSize) {
|
||||
_in = input;
|
||||
_bufferSize = bufferSize;
|
||||
output_a.init(bufferSize);
|
||||
output_b.init(bufferSize);
|
||||
}
|
||||
|
||||
void start() {
|
||||
_workerThread = std::thread(_worker, this);
|
||||
}
|
||||
|
||||
stream<complex_t> output_a;
|
||||
stream<complex_t> output_b;
|
||||
|
||||
private:
|
||||
static void _worker(Splitter* _this) {
|
||||
complex_t* buf = new complex_t[_this->_bufferSize];
|
||||
while (true) {
|
||||
_this->_in->read(buf, _this->_bufferSize);
|
||||
_this->output_a.write(buf, _this->_bufferSize);
|
||||
_this->output_b.write(buf, _this->_bufferSize);
|
||||
}
|
||||
}
|
||||
|
||||
stream<complex_t>* _in;
|
||||
int _bufferSize;
|
||||
std::thread _workerThread;
|
||||
};
|
||||
};
|
85
src/dsp/math.h
Normal file
85
src/dsp/math.h
Normal file
@ -0,0 +1,85 @@
|
||||
#pragma once
|
||||
#include <thread>
|
||||
#include <dsp/stream.h>
|
||||
#include <dsp/types.h>
|
||||
#include <volk.h>
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.1415926535f
|
||||
#endif
|
||||
|
||||
namespace dsp {
|
||||
class Multiplier {
|
||||
public:
|
||||
Multiplier() {
|
||||
|
||||
}
|
||||
|
||||
Multiplier(stream<complex_t>* a, stream<complex_t>* b, int blockSize) : output(blockSize * 2) {
|
||||
_a = a;
|
||||
_b = b;
|
||||
_blockSize = blockSize;
|
||||
}
|
||||
|
||||
void init(stream<complex_t>* a, stream<complex_t>* 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<complex_t> 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<complex_t>* _a;
|
||||
stream<complex_t>* _b;
|
||||
int _blockSize;
|
||||
bool running = false;
|
||||
std::thread _workerThread;
|
||||
};
|
||||
};
|
425
src/dsp/resampling.h
Normal file
425
src/dsp/resampling.h
Normal file
@ -0,0 +1,425 @@
|
||||
#pragma once
|
||||
#include <thread>
|
||||
#include <dsp/filter.h>
|
||||
#include <dsp/stream.h>
|
||||
#include <dsp/types.h>
|
||||
#include <numeric>
|
||||
|
||||
|
||||
namespace dsp {
|
||||
template <class T>
|
||||
class Interpolator {
|
||||
public:
|
||||
Interpolator() {
|
||||
|
||||
}
|
||||
|
||||
Interpolator(stream<T>* in, float interpolation, int blockSize) : output(blockSize * interpolation * 2) {
|
||||
_input = in;
|
||||
_interpolation = interpolation;
|
||||
_blockSize = blockSize;
|
||||
}
|
||||
|
||||
void init(stream<T>* 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<T>* input) {
|
||||
if (running) {
|
||||
return;
|
||||
}
|
||||
_input = input;
|
||||
}
|
||||
|
||||
stream<T> output;
|
||||
|
||||
private:
|
||||
static void _worker(Interpolator<T>* _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<T>* _input;
|
||||
int _blockSize;
|
||||
float _interpolation;
|
||||
std::thread _workerThread;
|
||||
bool running = false;
|
||||
};
|
||||
|
||||
class BlockDecimator {
|
||||
public:
|
||||
BlockDecimator() {
|
||||
|
||||
}
|
||||
|
||||
BlockDecimator(stream<complex_t>* in, int skip, int blockSize) : output(blockSize * 2) {
|
||||
_input = in;
|
||||
_skip = skip;
|
||||
_blockSize = blockSize;
|
||||
}
|
||||
|
||||
void init(stream<complex_t>* 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<complex_t> 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<complex_t>* _input;
|
||||
int _blockSize;
|
||||
int _skip;
|
||||
std::thread _workerThread;
|
||||
bool running = false;
|
||||
};
|
||||
|
||||
class Resampler {
|
||||
public:
|
||||
Resampler() {
|
||||
|
||||
}
|
||||
|
||||
void init(stream<complex_t>* 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<complex_t>* input) {
|
||||
if (running) {
|
||||
return;
|
||||
}
|
||||
_input = input;
|
||||
interp.setInput(_input);
|
||||
if (_interp == 1) {
|
||||
decim.setInput(_input);
|
||||
}
|
||||
}
|
||||
|
||||
stream<complex_t>* output;
|
||||
|
||||
private:
|
||||
Interpolator<complex_t> interp;
|
||||
DecimatingFIRFilter decim;
|
||||
stream<complex_t>* _input;
|
||||
|
||||
std::vector<float> _taps;
|
||||
int _interp;
|
||||
int _decim;
|
||||
float _outputSampleRate;
|
||||
float _inputSampleRate;
|
||||
float _blockSize;
|
||||
bool running = false;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class FloatResampler {
|
||||
public:
|
||||
FloatResampler() {
|
||||
|
||||
}
|
||||
|
||||
void init(stream<float>* 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<float>* input) {
|
||||
if (running) {
|
||||
return;
|
||||
}
|
||||
_input = input;
|
||||
interp.setInput(_input);
|
||||
if (_interp == 1) {
|
||||
decim.setInput(_input);
|
||||
}
|
||||
}
|
||||
|
||||
stream<float>* output;
|
||||
|
||||
private:
|
||||
Interpolator<float> interp;
|
||||
FloatDecimatingFIRFilter decim;
|
||||
stream<float>* _input;
|
||||
|
||||
std::vector<float> _taps;
|
||||
int _interp;
|
||||
int _decim;
|
||||
float _outputSampleRate;
|
||||
float _inputSampleRate;
|
||||
float _blockSize;
|
||||
bool running = false;
|
||||
};
|
||||
|
||||
|
||||
};
|
49
src/dsp/routing.h
Normal file
49
src/dsp/routing.h
Normal file
@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
#include <thread>
|
||||
#include <dsp/stream.h>
|
||||
#include <dsp/types.h>
|
||||
#include <vector>
|
||||
|
||||
namespace dsp {
|
||||
class Splitter {
|
||||
public:
|
||||
Splitter() {
|
||||
|
||||
}
|
||||
|
||||
Splitter(stream<complex_t>* input, int bufferSize) {
|
||||
_in = input;
|
||||
_bufferSize = bufferSize;
|
||||
output_a.init(bufferSize);
|
||||
output_b.init(bufferSize);
|
||||
}
|
||||
|
||||
void init(stream<complex_t>* input, int bufferSize) {
|
||||
_in = input;
|
||||
_bufferSize = bufferSize;
|
||||
output_a.init(bufferSize);
|
||||
output_b.init(bufferSize);
|
||||
}
|
||||
|
||||
void start() {
|
||||
_workerThread = std::thread(_worker, this);
|
||||
}
|
||||
|
||||
stream<complex_t> output_a;
|
||||
stream<complex_t> output_b;
|
||||
|
||||
private:
|
||||
static void _worker(Splitter* _this) {
|
||||
complex_t* buf = new complex_t[_this->_bufferSize];
|
||||
while (true) {
|
||||
_this->_in->read(buf, _this->_bufferSize);
|
||||
_this->output_a.write(buf, _this->_bufferSize);
|
||||
_this->output_b.write(buf, _this->_bufferSize);
|
||||
}
|
||||
}
|
||||
|
||||
stream<complex_t>* _in;
|
||||
int _bufferSize;
|
||||
std::thread _workerThread;
|
||||
};
|
||||
};
|
118
src/dsp/sink.h
Normal file
118
src/dsp/sink.h
Normal file
@ -0,0 +1,118 @@
|
||||
#pragma once
|
||||
#include <thread>
|
||||
#include <dsp/stream.h>
|
||||
#include <dsp/types.h>
|
||||
#include <vector>
|
||||
|
||||
namespace dsp {
|
||||
class HandlerSink {
|
||||
public:
|
||||
HandlerSink() {
|
||||
|
||||
}
|
||||
|
||||
HandlerSink(stream<complex_t>* input, complex_t* buffer, int bufferSize, void handler(complex_t*)) {
|
||||
_in = input;
|
||||
_bufferSize = bufferSize;
|
||||
_buffer = buffer;
|
||||
_handler = handler;
|
||||
}
|
||||
|
||||
void init(stream<complex_t>* 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<complex_t>* _in;
|
||||
int _bufferSize;
|
||||
complex_t* _buffer;
|
||||
std::thread _workerThread;
|
||||
void (*_handler)(complex_t*);
|
||||
};
|
||||
|
||||
class NullSink {
|
||||
public:
|
||||
NullSink() {
|
||||
|
||||
}
|
||||
|
||||
NullSink(stream<complex_t>* input, int bufferSize) {
|
||||
_in = input;
|
||||
_bufferSize = bufferSize;
|
||||
}
|
||||
|
||||
void init(stream<complex_t>* 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<complex_t>* _in;
|
||||
int _bufferSize;
|
||||
std::thread _workerThread;
|
||||
};
|
||||
|
||||
class FloatNullSink {
|
||||
public:
|
||||
FloatNullSink() {
|
||||
|
||||
}
|
||||
|
||||
FloatNullSink(stream<float>* input, int bufferSize) {
|
||||
_in = input;
|
||||
_bufferSize = bufferSize;
|
||||
}
|
||||
|
||||
void init(stream<float>* 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<float>* _in;
|
||||
int _bufferSize;
|
||||
std::thread _workerThread;
|
||||
};
|
||||
};
|
82
src/dsp/source.h
Normal file
82
src/dsp/source.h
Normal file
@ -0,0 +1,82 @@
|
||||
#pragma once
|
||||
#include <thread>
|
||||
#include <dsp/stream.h>
|
||||
#include <dsp/types.h>
|
||||
#include <volk.h>
|
||||
|
||||
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<complex_t> 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;
|
||||
};
|
||||
};
|
@ -3,7 +3,9 @@
|
||||
#include <algorithm>
|
||||
#include <math.h>
|
||||
|
||||
namespace cdsp {
|
||||
#define STREAM_BUF_SZ 1000000
|
||||
|
||||
namespace dsp {
|
||||
template <class T>
|
||||
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<float>(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;
|
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
namespace cdsp {
|
||||
namespace dsp {
|
||||
struct complex_t {
|
||||
float q;
|
||||
float i;
|
158
src/dsp/vfo.h
Normal file
158
src/dsp/vfo.h
Normal file
@ -0,0 +1,158 @@
|
||||
#pragma once
|
||||
#include <dsp/source.h>
|
||||
#include <dsp/math.h>
|
||||
#include <dsp/resampling.h>
|
||||
#include <dsp/filter.h>
|
||||
|
||||
namespace dsp {
|
||||
class VFO {
|
||||
public:
|
||||
VFO() {
|
||||
|
||||
}
|
||||
|
||||
void init(stream<complex_t>* 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<complex_t>* output;
|
||||
|
||||
private:
|
||||
SineSource lo;
|
||||
Multiplier mixer;
|
||||
Interpolator<complex_t> interp;
|
||||
DecimatingFIRFilter decim;
|
||||
stream<complex_t>* _input;
|
||||
|
||||
std::vector<float> _taps;
|
||||
int _interp;
|
||||
int _decim;
|
||||
float _outputSampleRate;
|
||||
float _inputSampleRate;
|
||||
float _bandWidth;
|
||||
float _blockSize;
|
||||
};
|
||||
};
|
@ -1,18 +1,18 @@
|
||||
#pragma once
|
||||
#include <thread>
|
||||
#include <cdsp/stream.h>
|
||||
#include <cdsp/types.h>
|
||||
#include <dsp/stream.h>
|
||||
#include <dsp/types.h>
|
||||
#include <fstream>
|
||||
#include <portaudio.h>
|
||||
|
||||
namespace cdsp {
|
||||
namespace io {
|
||||
class AudioSink {
|
||||
public:
|
||||
AudioSink() {
|
||||
|
||||
}
|
||||
|
||||
AudioSink(stream<float>* in, int bufferSize) {
|
||||
AudioSink(dsp::stream<float>* in, int bufferSize) {
|
||||
_bufferSize = bufferSize;
|
||||
_input = in;
|
||||
buffer = new float[_bufferSize * 2];
|
||||
@ -20,7 +20,7 @@ namespace cdsp {
|
||||
Pa_Initialize();
|
||||
}
|
||||
|
||||
void init(stream<float>* in, int bufferSize) {
|
||||
void init(dsp::stream<float>* in, int bufferSize) {
|
||||
_bufferSize = bufferSize;
|
||||
_input = in;
|
||||
buffer = new float[_bufferSize * 2];
|
||||
@ -67,7 +67,7 @@ namespace cdsp {
|
||||
}
|
||||
|
||||
int _bufferSize;
|
||||
stream<float>* _input;
|
||||
dsp::stream<float>* _input;
|
||||
float* buffer;
|
||||
float _volume;
|
||||
PaStream *stream;
|
106
src/io/soapy.h
Normal file
106
src/io/soapy.h
Normal file
@ -0,0 +1,106 @@
|
||||
#include <string>
|
||||
#include <SoapySDR/Device.hpp>
|
||||
#include <SoapySDR/Modules.hpp>
|
||||
#include <dsp/stream.h>
|
||||
#include <dsp/types.h>
|
||||
|
||||
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<double> sampleRates;
|
||||
std::string txtSampleRateList;
|
||||
|
||||
dsp::stream<dsp::complex_t> 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;
|
||||
};
|
||||
};
|
@ -7,6 +7,7 @@
|
||||
#include <main_window.h>
|
||||
#include <styles.h>
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
@ -1,22 +1,20 @@
|
||||
#include <main_window.h>
|
||||
#include <imgui_plot.h>
|
||||
#include <hackrf.h>
|
||||
#include <cdsp/hackrf.h>
|
||||
#include <cdsp/resampling.h>
|
||||
#include <cdsp/demodulation.h>
|
||||
#include <cdsp/filter.h>
|
||||
#include <dsp/resampling.h>
|
||||
#include <dsp/demodulator.h>
|
||||
#include <dsp/filter.h>
|
||||
#include <thread>
|
||||
#include <complex>
|
||||
#include <cdsp/generator.h>
|
||||
#include <cdsp/math.h>
|
||||
#include <dsp/source.h>
|
||||
#include <dsp/math.h>
|
||||
#include <waterfall.h>
|
||||
#include <fftw3.h>
|
||||
#include <signal_path.h>
|
||||
#include <io/soapy.h>
|
||||
|
||||
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<float> _data;
|
||||
std::vector<float> 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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,26 +4,17 @@ SignalPath::SignalPath() {
|
||||
|
||||
}
|
||||
|
||||
std::vector<float> iftaps;
|
||||
std::vector<float> ataps;
|
||||
|
||||
void SignalPath::init(uint64_t sampleRate, int fftRate, int fftSize, cdsp::stream<cdsp::complex_t>* input, cdsp::complex_t* fftBuffer, void fftHandler(cdsp::complex_t*)) {
|
||||
void SignalPath::init(uint64_t sampleRate, int fftRate, int fftSize, dsp::stream<dsp::complex_t>* 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();
|
||||
|
@ -1,16 +1,19 @@
|
||||
#pragma once
|
||||
#include <cdsp/filter.h>
|
||||
#include <cdsp/resampling.h>
|
||||
#include <cdsp/generator.h>
|
||||
#include <cdsp/math.h>
|
||||
#include <cdsp/demodulation.h>
|
||||
#include <cdsp/audio.h>
|
||||
#include <vfo.h>
|
||||
#include <dsp/filter.h>
|
||||
#include <dsp/resampling.h>
|
||||
#include <dsp/source.h>
|
||||
#include <dsp/math.h>
|
||||
#include <dsp/demodulator.h>
|
||||
#include <dsp/routing.h>
|
||||
#include <dsp/sink.h>
|
||||
#include <dsp/correction.h>
|
||||
#include <dsp/vfo.h>
|
||||
#include <io/audio.h>
|
||||
|
||||
class SignalPath {
|
||||
public:
|
||||
SignalPath();
|
||||
void init(uint64_t sampleRate, int fftRate, int fftSize, cdsp::stream<cdsp::complex_t>* input, cdsp::complex_t* fftBuffer, void fftHandler(cdsp::complex_t*));
|
||||
void init(uint64_t sampleRate, int fftRate, int fftSize, dsp::stream<dsp::complex_t>* 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;
|
||||
|
104
src/vfo.cpp
104
src/vfo.cpp
@ -1,104 +0,0 @@
|
||||
#include <vfo.h>
|
||||
#include <numeric>
|
||||
|
||||
VFO::VFO() {
|
||||
|
||||
}
|
||||
|
||||
void VFO::init(cdsp::stream<cdsp::complex_t>* 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;
|
||||
}
|
43
src/vfo.h
43
src/vfo.h
@ -1,43 +0,0 @@
|
||||
#pragma once
|
||||
#include <cdsp/math.h>
|
||||
#include <cdsp/generator.h>
|
||||
#include <cdsp/resampling.h>
|
||||
#include <cdsp/filter.h>
|
||||
|
||||
// Round up to next 5KHz multiple frequency
|
||||
#define OUTPUT_SR_ROUND 5000.0f
|
||||
|
||||
class VFO {
|
||||
public:
|
||||
VFO();
|
||||
void init(cdsp::stream<cdsp::complex_t>* 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<cdsp::complex_t>* output;
|
||||
|
||||
private:
|
||||
cdsp::ComplexSineSource lo;
|
||||
cdsp::Multiplier mixer;
|
||||
cdsp::IQInterpolator interp;
|
||||
cdsp::DecimatingFIRFilter decFir;
|
||||
|
||||
std::vector<float> decimTaps;
|
||||
|
||||
int _interp;
|
||||
int _decim;
|
||||
float _inputSampleRate;
|
||||
float _outputSampleRate;
|
||||
float _bandWidth;
|
||||
int _bufferSize;
|
||||
int outputSampleRate;
|
||||
|
||||
cdsp::stream<cdsp::complex_t>* _input;
|
||||
};
|
Loading…
Reference in New Issue
Block a user