mirror of
https://github.com/AlexandreRouma/SDRPlusPlus.git
synced 2026-04-19 14:52:43 +00:00
initial commit
This commit is contained in:
47
src/cdsp/demodulation.h
Normal file
47
src/cdsp/demodulation.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
#include <thread>
|
||||
#include <cdsp/stream.h>
|
||||
#include <cdsp/types.h>
|
||||
|
||||
|
||||
namespace cdsp {
|
||||
class FMDemodulator {
|
||||
public:
|
||||
FMDemodulator(stream<complex_t>* in, float deviation, long sampleRate, int bufferSize) : output(bufferSize * 2) {
|
||||
_input = in;
|
||||
_bufferSize = bufferSize;
|
||||
_phase = 0.0f;
|
||||
_phasorSpeed = (2 * 3.1415926535) / (sampleRate / deviation);
|
||||
}
|
||||
|
||||
void start() {
|
||||
_workerThread = std::thread(_worker, this);
|
||||
}
|
||||
|
||||
stream<float> output;
|
||||
|
||||
private:
|
||||
static void _worker(FMDemodulator* _this) {
|
||||
complex_t* inBuf = new complex_t[_this->_bufferSize];
|
||||
float* outBuf = new float[_this->_bufferSize];
|
||||
float diff = 0;
|
||||
float currentPhase = 0;
|
||||
while (true) {
|
||||
_this->_input->read(inBuf, _this->_bufferSize);
|
||||
for (int i = 0; i < _this->_bufferSize; i++) {
|
||||
currentPhase = atan2f(inBuf[i].i, inBuf[i].q);
|
||||
diff = currentPhase - _this->_phase;
|
||||
outBuf[i] = diff / _this->_phasorSpeed;
|
||||
_this->_phase = currentPhase;
|
||||
}
|
||||
_this->output.write(outBuf, _this->_bufferSize);
|
||||
}
|
||||
}
|
||||
|
||||
stream<complex_t>* _input;
|
||||
int _bufferSize;
|
||||
float _phase;
|
||||
float _phasorSpeed;
|
||||
std::thread _workerThread;
|
||||
};
|
||||
};
|
||||
127
src/cdsp/fft_math.h
Normal file
127
src/cdsp/fft_math.h
Normal file
@@ -0,0 +1,127 @@
|
||||
#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();
|
||||
84
src/cdsp/file.h
Normal file
84
src/cdsp/file.h
Normal file
@@ -0,0 +1,84 @@
|
||||
#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(std::string path, int bufferSize) : output(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(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;
|
||||
};
|
||||
};
|
||||
105
src/cdsp/filter.h
Normal file
105
src/cdsp/filter.h
Normal file
@@ -0,0 +1,105 @@
|
||||
#pragma once
|
||||
#include <thread>
|
||||
#include <cdsp/stream.h>
|
||||
#include <cdsp/types.h>
|
||||
#include <vector>
|
||||
|
||||
namespace cdsp {
|
||||
class FIRFilter {
|
||||
public:
|
||||
FIRFilter(stream<complex_t>* input, std::vector<float> taps, int bufferSize) : output(bufferSize * 2) {
|
||||
_in = input;
|
||||
_bufferSize = bufferSize;
|
||||
_tapCount = taps.size();
|
||||
delayBuf = new complex_t[_tapCount];
|
||||
_taps = taps;
|
||||
}
|
||||
|
||||
void start() {
|
||||
_workerThread = std::thread(_worker, this);
|
||||
}
|
||||
|
||||
stream<complex_t> output;
|
||||
|
||||
private:
|
||||
static void _worker(FIRFilter* _this) {
|
||||
complex_t* inBuf = new complex_t[_this->_bufferSize];
|
||||
complex_t* outBuf = new complex_t[_this->_bufferSize];
|
||||
float tap = 0.0f;
|
||||
while (true) {
|
||||
_this->_in->read(inBuf, _this->_bufferSize);
|
||||
for (int i = _this->_tapCount; i < _this->_bufferSize - _this->_tapCount; i++) {
|
||||
outBuf[i].i = 0.0f;
|
||||
outBuf[i].q = 0.0f;
|
||||
}
|
||||
for (int t = 0; t < _this->_tapCount; t++) {
|
||||
tap = _this->_taps[t];
|
||||
if (tap == 0.0f) {
|
||||
continue;
|
||||
}
|
||||
for (int i = 0; i < t; i++) {
|
||||
outBuf[i].i += tap * _this->delayBuf[_this->_tapCount - t - 1].i;
|
||||
outBuf[i].q += tap * _this->delayBuf[_this->_tapCount - t - 1].q;
|
||||
}
|
||||
for (int i = t; i < _this->_bufferSize; i++) {
|
||||
outBuf[i].i += tap * inBuf[i - t].i;
|
||||
outBuf[i].q += tap * inBuf[i - t].q;
|
||||
}
|
||||
}
|
||||
// for (int i = _this->_tapCount; i < _this->_bufferSize - _this->_tapCount; i++) {
|
||||
// outBuf[i].i /= (float)_this->_tapCount;
|
||||
// outBuf[i].q /= (float)_this->_tapCount;
|
||||
// }
|
||||
memcpy(_this->delayBuf, &inBuf[_this->_bufferSize - _this->_tapCount], _this->_tapCount * sizeof(complex_t));
|
||||
_this->output.write(outBuf, _this->_bufferSize);
|
||||
}
|
||||
}
|
||||
|
||||
stream<complex_t>* _in;
|
||||
complex_t* delayBuf;
|
||||
int _bufferSize;
|
||||
int _tapCount = 0;
|
||||
std::vector<float> _taps;
|
||||
std::thread _workerThread;
|
||||
};
|
||||
|
||||
|
||||
class DCBiasRemover {
|
||||
public:
|
||||
DCBiasRemover(stream<complex_t>* input, int bufferSize) : output(bufferSize * 2) {
|
||||
_in = input;
|
||||
_bufferSize = bufferSize;
|
||||
}
|
||||
|
||||
void start() {
|
||||
_workerThread = std::thread(_worker, this);
|
||||
}
|
||||
|
||||
stream<complex_t> output;
|
||||
|
||||
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);
|
||||
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;
|
||||
};
|
||||
};
|
||||
100
src/cdsp/generator.h
Normal file
100
src/cdsp/generator.h
Normal file
@@ -0,0 +1,100 @@
|
||||
#pragma once
|
||||
#include <thread>
|
||||
#include <cdsp/stream.h>
|
||||
#include <cdsp/types.h>
|
||||
|
||||
namespace cdsp {
|
||||
class SineSource {
|
||||
public:
|
||||
SineSource(float frequency, long sampleRate, int bufferSize) : output(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(float frequency, long sampleRate, int bufferSize) : output(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(float frequency, long sampleRate, int bufferSize) : output(bufferSize * 2) {
|
||||
_bufferSize = bufferSize;
|
||||
_phasorSpeed = (2 * 3.1415926535) / (sampleRate / frequency);
|
||||
_phase = 0;
|
||||
}
|
||||
|
||||
void start() {
|
||||
_workerThread = std::thread(_worker, this);
|
||||
}
|
||||
|
||||
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);
|
||||
_this->output.write(outBuf, _this->_bufferSize);
|
||||
}
|
||||
}
|
||||
|
||||
int _bufferSize;
|
||||
float _phasorSpeed;
|
||||
float _phase;
|
||||
std::thread _workerThread;
|
||||
};
|
||||
};
|
||||
152
src/cdsp/hackrf.h
Normal file
152
src/cdsp/hackrf.h
Normal file
@@ -0,0 +1,152 @@
|
||||
#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(stream<complex_t>* in, int bufferSize) : output(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(stream<complex_t>* out, int bufferSize) : input(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(hackrf_device* dev, int bufferSize, stream<complex_t>* input) : gen(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(hackrf_device* dev, int bufferSize) : output(bufferSize * 2), gen(&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;
|
||||
};
|
||||
};
|
||||
42
src/cdsp/math.h
Normal file
42
src/cdsp/math.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
#include <thread>
|
||||
#include <cdsp/stream.h>
|
||||
#include <cdsp/types.h>
|
||||
|
||||
namespace cdsp {
|
||||
class Multiplier {
|
||||
public:
|
||||
Multiplier(stream<complex_t>* a, stream<complex_t>* b, int bufferSize) : output(bufferSize * 2) {
|
||||
_a = a;
|
||||
_b = b;
|
||||
_bufferSize = bufferSize;
|
||||
}
|
||||
|
||||
void start() {
|
||||
_workerThread = std::thread(_worker, this);
|
||||
}
|
||||
|
||||
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) {
|
||||
_this->_a->read(aBuf, _this->_bufferSize);
|
||||
_this->_b->read(bBuf, _this->_bufferSize);
|
||||
for (int i = 0; i < _this->_bufferSize; i++) {
|
||||
outBuf[i].i = (aBuf[i].q * bBuf[i].i) + (bBuf[i].q * aBuf[i].i); // BC + AD
|
||||
outBuf[i].q = (aBuf[i].q * bBuf[i].q) - (aBuf[i].i * bBuf[i].i);
|
||||
}
|
||||
_this->output.write(outBuf, _this->_bufferSize);
|
||||
}
|
||||
}
|
||||
|
||||
stream<complex_t>* _a;
|
||||
stream<complex_t>* _b;
|
||||
int _bufferSize;
|
||||
std::thread _workerThread;
|
||||
};
|
||||
};
|
||||
45
src/cdsp/modulation.h
Normal file
45
src/cdsp/modulation.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
#include <thread>
|
||||
#include <cdsp/stream.h>
|
||||
#include <cdsp/types.h>
|
||||
#include <cmath>
|
||||
|
||||
namespace cdsp {
|
||||
class FMModulator {
|
||||
public:
|
||||
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 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;
|
||||
};
|
||||
};
|
||||
102
src/cdsp/resampling.h
Normal file
102
src/cdsp/resampling.h
Normal file
@@ -0,0 +1,102 @@
|
||||
#pragma once
|
||||
#include <thread>
|
||||
#include <cdsp/stream.h>
|
||||
#include <cdsp/types.h>
|
||||
|
||||
namespace cdsp {
|
||||
class Interpolator {
|
||||
public:
|
||||
Interpolator(stream<float>* in, float interpolation, int bufferSize) : output(bufferSize * 2) {
|
||||
_input = in;
|
||||
_interpolation = interpolation;
|
||||
_bufferSize = bufferSize;
|
||||
}
|
||||
|
||||
void start() {
|
||||
_workerThread = std::thread(_worker, this);
|
||||
}
|
||||
|
||||
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) {
|
||||
_this->_input->read(inBuf, (int)((float)_this->_bufferSize / _this->_interpolation));
|
||||
for (int i = 0; i < _this->_bufferSize; i++) {
|
||||
outBuf[i] = inBuf[(int)((float)i / _this->_interpolation)];
|
||||
}
|
||||
_this->output.write(outBuf, _this->_bufferSize);
|
||||
}
|
||||
}
|
||||
|
||||
stream<float>* _input;
|
||||
int _bufferSize;
|
||||
float _interpolation;
|
||||
std::thread _workerThread;
|
||||
};
|
||||
|
||||
|
||||
class IQInterpolator {
|
||||
public:
|
||||
IQInterpolator(stream<complex_t>* in, float interpolation, int bufferSize) : output(bufferSize * 2) {
|
||||
_input = in;
|
||||
_interpolation = interpolation;
|
||||
_bufferSize = bufferSize;
|
||||
}
|
||||
|
||||
void start() {
|
||||
_workerThread = std::thread(_worker, this);
|
||||
}
|
||||
|
||||
stream<complex_t> output;
|
||||
|
||||
private:
|
||||
static void _worker(IQInterpolator* _this) {
|
||||
complex_t* inBuf = new complex_t[(int)((float)_this->_bufferSize / _this->_interpolation)];
|
||||
complex_t* outBuf = new complex_t[_this->_bufferSize];
|
||||
while (true) {
|
||||
_this->_input->read(inBuf, (int)((float)_this->_bufferSize / _this->_interpolation));
|
||||
for (int i = 0; i < _this->_bufferSize; i++) {
|
||||
outBuf[i] = inBuf[(int)((float)i / _this->_interpolation)];
|
||||
}
|
||||
_this->output.write(outBuf, _this->_bufferSize);
|
||||
}
|
||||
}
|
||||
|
||||
stream<complex_t>* _input;
|
||||
int _bufferSize;
|
||||
float _interpolation;
|
||||
std::thread _workerThread;
|
||||
};
|
||||
|
||||
class BlockDecimator {
|
||||
public:
|
||||
BlockDecimator(stream<complex_t>* in, int skip, int bufferSize) : output(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;
|
||||
};
|
||||
};
|
||||
144
src/cdsp/stream.h
Normal file
144
src/cdsp/stream.h
Normal file
@@ -0,0 +1,144 @@
|
||||
#pragma once
|
||||
#include <condition_variable>
|
||||
#include <algorithm>
|
||||
#include <math.h>
|
||||
|
||||
namespace cdsp {
|
||||
template <class T>
|
||||
class stream {
|
||||
public:
|
||||
stream(int size) {
|
||||
_buffer = new T[size];
|
||||
this->size = size;
|
||||
writec = 0;
|
||||
readc = size - 1;
|
||||
//printf("Stream init\n");
|
||||
}
|
||||
|
||||
void read(T* data, int len) {
|
||||
int dataRead = 0;
|
||||
while (dataRead < len) {
|
||||
int canRead = waitUntilReadable();
|
||||
int toRead = std::min(canRead, len - dataRead);
|
||||
|
||||
int len1 = (toRead >= (size - readc) ? (size - readc) : (toRead));
|
||||
memcpy(&data[dataRead], &_buffer[readc], len1 * sizeof(T));
|
||||
if (len1 < toRead) {
|
||||
memcpy(&data[dataRead + len1], _buffer, (toRead - len1) * sizeof(T));
|
||||
}
|
||||
|
||||
dataRead += toRead;
|
||||
readc_mtx.lock();
|
||||
readc = (readc + toRead) % size;
|
||||
readc_mtx.unlock();
|
||||
canWriteVar.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
void readAndSkip(T* data, int len, int skip) {
|
||||
int dataRead = 0;
|
||||
while (dataRead < len) {
|
||||
int canRead = waitUntilReadable();
|
||||
int toRead = std::min(canRead, len - dataRead);
|
||||
|
||||
int len1 = (toRead >= (size - readc) ? (size - readc) : (toRead));
|
||||
memcpy(&data[dataRead], &_buffer[readc], len1 * sizeof(T));
|
||||
if (len1 < toRead) {
|
||||
memcpy(&data[dataRead + len1], _buffer, (toRead - len1) * sizeof(T));
|
||||
}
|
||||
|
||||
dataRead += toRead;
|
||||
readc_mtx.lock();
|
||||
readc = (readc + toRead) % size;
|
||||
readc_mtx.unlock();
|
||||
canWriteVar.notify_one();
|
||||
}
|
||||
|
||||
// Skip
|
||||
|
||||
dataRead = 0;
|
||||
while (dataRead < skip) {
|
||||
int canRead = waitUntilReadable();
|
||||
int toRead = std::min(canRead, skip - dataRead);
|
||||
|
||||
dataRead += toRead;
|
||||
readc_mtx.lock();
|
||||
readc = (readc + toRead) % size;
|
||||
readc_mtx.unlock();
|
||||
canWriteVar.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
int waitUntilReadable() {
|
||||
int canRead = readable();
|
||||
if (canRead > 0) {
|
||||
return canRead;
|
||||
}
|
||||
std::unique_lock<std::mutex> lck(writec_mtx);
|
||||
canReadVar.wait(lck, [=](){ return (this->readable(false) > 0); });
|
||||
return this->readable(false);
|
||||
}
|
||||
|
||||
int readable(bool lock = true) {
|
||||
if (lock) { writec_mtx.lock(); }
|
||||
int _wc = writec;
|
||||
if (lock) { writec_mtx.unlock(); }
|
||||
int readable = (_wc - readc) % this->size;
|
||||
if (_wc < readc) {
|
||||
readable = (this->size + readable);
|
||||
}
|
||||
return readable - 1;
|
||||
}
|
||||
|
||||
void write(T* data, int len) {
|
||||
int dataWrite = 0;
|
||||
while (dataWrite < len) {
|
||||
int canWrite = waitUntilWriteable();
|
||||
int toWrite = std::min(canWrite, len - dataWrite);
|
||||
|
||||
int len1 = (toWrite >= (size - writec) ? (size - writec) : (toWrite));
|
||||
memcpy(&_buffer[writec], &data[dataWrite], len1 * sizeof(T));
|
||||
if (len1 < toWrite) {
|
||||
memcpy(_buffer, &data[dataWrite + len1], (toWrite - len1) * sizeof(T));
|
||||
}
|
||||
|
||||
dataWrite += toWrite;
|
||||
writec_mtx.lock();
|
||||
writec = (writec + toWrite) % size;
|
||||
writec_mtx.unlock();
|
||||
canReadVar.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
int waitUntilWriteable() {
|
||||
int canWrite = writeable();
|
||||
if (canWrite > 0) {
|
||||
return canWrite;
|
||||
}
|
||||
std::unique_lock<std::mutex> lck(readc_mtx);
|
||||
canWriteVar.wait(lck, [=](){ return (this->writeable(false) > 0); });
|
||||
return this->writeable(false);
|
||||
}
|
||||
|
||||
int writeable(bool lock = true) {
|
||||
if (lock) { readc_mtx.lock(); }
|
||||
int _rc = readc;
|
||||
if (lock) { readc_mtx.unlock(); }
|
||||
int writeable = (_rc - writec) % this->size;
|
||||
if (_rc < writec) {
|
||||
writeable = (this->size + writeable);
|
||||
}
|
||||
return writeable - 1;
|
||||
}
|
||||
|
||||
private:
|
||||
T* _buffer;
|
||||
int size;
|
||||
int readc;
|
||||
int writec;
|
||||
std::mutex readc_mtx;
|
||||
std::mutex writec_mtx;
|
||||
std::condition_variable canReadVar;
|
||||
std::condition_variable canWriteVar;
|
||||
};
|
||||
};
|
||||
8
src/cdsp/types.h
Normal file
8
src/cdsp/types.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
namespace cdsp {
|
||||
struct complex_t {
|
||||
float q;
|
||||
float i;
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user